Skip to content

Commit c2c90da

Browse files
committed
config(macOS): add support for creating ASIF drives
1 parent 5017346 commit c2c90da

4 files changed

Lines changed: 41 additions & 1 deletion

File tree

Configuration/UTMAppleConfigurationDrive.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ struct UTMAppleConfigurationDrive: UTMConfigurationDrive {
2828
var isNvme: Bool
2929
var imageURL: URL?
3030
var imageName: String?
31+
var isASIF: Bool = false // not saved
3132

3233
private(set) var id = UUID().uuidString
3334

@@ -121,6 +122,7 @@ struct UTMAppleConfigurationDrive: UTMConfigurationDrive {
121122
isNvme.hash(into: &hasher)
122123
isExternal.hash(into: &hasher)
123124
id.hash(into: &hasher)
125+
isASIF.hash(into: &hasher)
124126
}
125127

126128
func clone() -> UTMAppleConfigurationDrive {

Configuration/UTMConfigurationDrive.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,15 @@ extension UTMConfigurationDrive {
7676
throw UTMConfigurationError.driveAlreadyExists(newURL)
7777
}
7878
if isRawImage {
79+
#if os(macOS)
80+
if let appleDrive = self as? UTMAppleConfigurationDrive, appleDrive.isASIF {
81+
try await createAsifImage(at: newURL, size: sizeMib)
82+
} else {
83+
try await createRawImage(at: newURL, size: sizeMib)
84+
}
85+
#else
7986
try await createRawImage(at: newURL, size: sizeMib)
87+
#endif
8088
} else {
8189
try await createQcow2Image(at: newURL, size: sizeMib)
8290
}
@@ -113,6 +121,16 @@ extension UTMConfigurationDrive {
113121
#endif
114122
}
115123

124+
private func createAsifImage(at newURL: URL, size sizeMib: Int) async throws {
125+
let numBlocks = sizeMib * Int(bytesInMib) / 512
126+
guard let asif = UTMASIFImage.sharedInstance() else {
127+
throw UTMConfigurationError.cannotCreateDiskImage
128+
}
129+
try await Task.detached {
130+
try asif.createBlank(with: newURL, numBlocks: numBlocks)
131+
}.value
132+
}
133+
116134
#if os(macOS)
117135
private func convertQcow2Image(at sourceURL: URL, to destFolderURL: URL) async throws -> URL {
118136
let destQcow2 = UTMData.newImage(from: sourceURL,

Platform/Shared/VMWizardState.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,9 @@ struct AlertMessage: Identifiable {
358358
if #available(macOS 14, *), useNvmeAsDiskInterface {
359359
newDisk.isNvme = true
360360
}
361+
if #available(macOS 26, *), UTMASIFImage.sharedInstance() != nil {
362+
newDisk.isASIF = true
363+
}
361364
config.drives.append(newDisk)
362365
}
363366
if #available(macOS 12, *), let sharingDirectoryURL = sharingDirectoryURL {

Platform/macOS/VMConfigAppleDriveCreateView.swift

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,17 @@ struct VMConfigAppleDriveCreateView: View {
2323
@Binding var config: UTMAppleConfigurationDrive
2424
@State private var isGiB: Bool = true
2525

26+
private var isASIFSupported: Bool {
27+
if #available(macOS 26, *) {
28+
return UTMASIFImage.sharedInstance() != nil
29+
} else {
30+
return false
31+
}
32+
}
33+
2634
var body: some View {
2735
Form {
28-
VStack {
36+
VStack(alignment: .leading) {
2937
Toggle(isOn: $config.isExternal.animation(), label: {
3038
Text("Removable")
3139
}).help("If checked, the drive image will be stored with the VM.")
@@ -37,13 +45,22 @@ struct VMConfigAppleDriveCreateView: View {
3745
} else {
3846
config.sizeMib = 10240
3947
config.isReadOnly = false
48+
config.isASIF = isASIFSupported
4049
}
4150
}
4251
if #available(macOS 14, *), !config.isExternal {
4352
Toggle(isOn: $config.isNvme.animation(), label: {
4453
Text("Use NVMe Interface")
4554
}).help("If checked, use NVMe instead of virtio as the disk interface, available on macOS 14+ for Linux guests only. This interface is slower but less likely to encounter filesystem errors.")
4655
}
56+
if isASIFSupported {
57+
Toggle(isOn: $config.isASIF) {
58+
Text("Use Apple Sparse Image Format")
59+
}.help("ASIF is more efficient and performant but is not compatible with older versions of macOS hosts.")
60+
.onAppear {
61+
config.isASIF = isASIFSupported
62+
}
63+
}
4764
if !config.isExternal {
4865
SizeTextField($config.sizeMib)
4966
}

0 commit comments

Comments
 (0)