소스 검색

config(macOS): add support for creating ASIF drives

osy 1 개월 전
부모
커밋
c2c90da484

+ 2 - 0
Configuration/UTMAppleConfigurationDrive.swift

@@ -28,6 +28,7 @@ struct UTMAppleConfigurationDrive: UTMConfigurationDrive {
     var isNvme: Bool
     var imageURL: URL?
     var imageName: String?
+    var isASIF: Bool = false // not saved
     
     private(set) var id = UUID().uuidString
     
@@ -121,6 +122,7 @@ struct UTMAppleConfigurationDrive: UTMConfigurationDrive {
         isNvme.hash(into: &hasher)
         isExternal.hash(into: &hasher)
         id.hash(into: &hasher)
+        isASIF.hash(into: &hasher)
     }
     
     func clone() -> UTMAppleConfigurationDrive {

+ 18 - 0
Configuration/UTMConfigurationDrive.swift

@@ -76,7 +76,15 @@ extension UTMConfigurationDrive {
                 throw UTMConfigurationError.driveAlreadyExists(newURL)
             }
             if isRawImage {
+                #if os(macOS)
+                if let appleDrive = self as? UTMAppleConfigurationDrive, appleDrive.isASIF {
+                    try await createAsifImage(at: newURL, size: sizeMib)
+                } else {
+                    try await createRawImage(at: newURL, size: sizeMib)
+                }
+                #else
                 try await createRawImage(at: newURL, size: sizeMib)
+                #endif
             } else {
                 try await createQcow2Image(at: newURL, size: sizeMib)
             }
@@ -113,6 +121,16 @@ extension UTMConfigurationDrive {
         #endif
     }
     
+    private func createAsifImage(at newURL: URL, size sizeMib: Int) async throws {
+        let numBlocks = sizeMib * Int(bytesInMib) / 512
+        guard let asif = UTMASIFImage.sharedInstance() else {
+            throw UTMConfigurationError.cannotCreateDiskImage
+        }
+        try await Task.detached {
+            try asif.createBlank(with: newURL, numBlocks: numBlocks)
+        }.value
+    }
+    
     #if os(macOS)
     private func convertQcow2Image(at sourceURL: URL, to destFolderURL: URL) async throws -> URL {
         let destQcow2 = UTMData.newImage(from: sourceURL,

+ 3 - 0
Platform/Shared/VMWizardState.swift

@@ -358,6 +358,9 @@ struct AlertMessage: Identifiable {
             if #available(macOS 14, *), useNvmeAsDiskInterface {
                 newDisk.isNvme = true
             }
+            if #available(macOS 26, *), UTMASIFImage.sharedInstance() != nil {
+                newDisk.isASIF = true
+            }
             config.drives.append(newDisk)
         }
         if #available(macOS 12, *), let sharingDirectoryURL = sharingDirectoryURL {

+ 18 - 1
Platform/macOS/VMConfigAppleDriveCreateView.swift

@@ -23,9 +23,17 @@ struct VMConfigAppleDriveCreateView: View {
     @Binding var config: UTMAppleConfigurationDrive
     @State private var isGiB: Bool = true
     
+    private var isASIFSupported: Bool {
+        if #available(macOS 26, *) {
+            return UTMASIFImage.sharedInstance() != nil
+        } else {
+            return false
+        }
+    }
+    
     var body: some View {
         Form {
-            VStack {
+            VStack(alignment: .leading) {
                 Toggle(isOn: $config.isExternal.animation(), label: {
                     Text("Removable")
                 }).help("If checked, the drive image will be stored with the VM.")
@@ -37,6 +45,7 @@ struct VMConfigAppleDriveCreateView: View {
                     } else {
                         config.sizeMib = 10240
                         config.isReadOnly = false
+                        config.isASIF = isASIFSupported
                     }
                 }
                 if #available(macOS 14, *), !config.isExternal {
@@ -44,6 +53,14 @@ struct VMConfigAppleDriveCreateView: View {
                         Text("Use NVMe Interface")
                     }).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.")
                 }
+                if isASIFSupported {
+                    Toggle(isOn: $config.isASIF) {
+                        Text("Use Apple Sparse Image Format")
+                    }.help("ASIF is more efficient and performant but is not compatible with older versions of macOS hosts.")
+                    .onAppear {
+                        config.isASIF = isASIFSupported
+                    }
+                }
                 if !config.isExternal {
                     SizeTextField($config.sizeMib)
                 }