From 62b6acf57e2cb9eae72cfbdaef87b4fa11cfee1e Mon Sep 17 00:00:00 2001 From: Eugene Cheah Date: Fri, 18 Mar 2022 12:36:18 +0800 Subject: [PATCH 01/22] Added .runAsSnapshot to UTMAppleConfiguration.swift --- Configuration/UTMAppleConfiguration.swift | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Configuration/UTMAppleConfiguration.swift b/Configuration/UTMAppleConfiguration.swift index f43cd724d..87a84712d 100644 --- a/Configuration/UTMAppleConfiguration.swift +++ b/Configuration/UTMAppleConfiguration.swift @@ -864,6 +864,7 @@ struct DiskImage: Codable, Hashable, Identifiable { var sizeMib: Int var isReadOnly: Bool var isExternal: Bool + var runAsSnapshot: Bool var imageURL: URL? private var uuid = UUID() // for identifiable @@ -871,6 +872,7 @@ struct DiskImage: Codable, Hashable, Identifiable { case sizeMib case isReadOnly case isExternal + case runAsSnapshot case imagePath case imageBookmark } @@ -891,12 +893,14 @@ struct DiskImage: Codable, Hashable, Identifiable { sizeMib = newSize isReadOnly = false isExternal = false + runAsSnapshot = false } - init(importImage url: URL, isReadOnly: Bool = false, isExternal: Bool = false) { + init(importImage url: URL, isReadOnly: Bool = false, isExternal: Bool = false, runAsSnapshot: Bool = false) { self.imageURL = url self.isReadOnly = isReadOnly self.isExternal = isExternal + self.runAsSnapshot = runAsSnapshot if let attributes = try? url.resourceValues(forKeys: [.fileSizeKey]), let fileSize = attributes.fileSize { sizeMib = fileSize / bytesInMib } else { @@ -912,6 +916,7 @@ struct DiskImage: Codable, Hashable, Identifiable { sizeMib = try container.decode(Int.self, forKey: .sizeMib) isReadOnly = try container.decode(Bool.self, forKey: .isReadOnly) isExternal = try container.decode(Bool.self, forKey: .isExternal) + runAsSnapshot = try container.decode(Bool.self, forKey: .runAsSnapshot) if !isExternal, let imagePath = try container.decodeIfPresent(String.self, forKey: .imagePath) { imageURL = dataURL.appendingPathComponent(imagePath) } else if let bookmark = try container.decodeIfPresent(Data.self, forKey: .imageBookmark) { @@ -925,6 +930,7 @@ struct DiskImage: Codable, Hashable, Identifiable { try container.encode(sizeMib, forKey: .sizeMib) try container.encode(isReadOnly, forKey: .isReadOnly) try container.encode(isExternal, forKey: .isExternal) + try container.encode(runAsSnapshot, forKey: .runAsSnapshot) if !isExternal { try container.encodeIfPresent(imageURL?.lastPathComponent, forKey: .imagePath) } else { @@ -942,6 +948,7 @@ struct DiskImage: Codable, Hashable, Identifiable { } func vzDiskImage() throws -> VZDiskImageStorageDeviceAttachment? { + // @TODO : Support runAsSnapshot logic if let imageURL = imageURL { return try VZDiskImageStorageDeviceAttachment(url: imageURL, readOnly: isReadOnly) } else { From 96df2a61c202fa615383c5bb209f2ef1210da1a2 Mon Sep 17 00:00:00 2001 From: Eugene Cheah Date: Sat, 19 Mar 2022 11:12:37 +0800 Subject: [PATCH 02/22] Adding runAsSnapshot toggle on the UI --- Platform/macOS/VMConfigAppleDriveDetailsView.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Platform/macOS/VMConfigAppleDriveDetailsView.swift b/Platform/macOS/VMConfigAppleDriveDetailsView.swift index f4e1e2159..5ebab5410 100644 --- a/Platform/macOS/VMConfigAppleDriveDetailsView.swift +++ b/Platform/macOS/VMConfigAppleDriveDetailsView.swift @@ -25,6 +25,7 @@ struct VMConfigAppleDriveDetailsView: View { TextField("Name", text: .constant(diskImage.imageURL?.lastPathComponent ?? NSLocalizedString("(New Drive)", comment: "VMConfigAppleDriveDetailsView"))) .disabled(true) Toggle("Read Only?", isOn: $diskImage.isReadOnly) + Toggle("Run using a snapshot? (similar to qemu --snapshot)", isOn: $diskImage.runAsSnapshot) Button(action: onDelete) { Label("Delete Drive", systemImage: "externaldrive.badge.minus") .foregroundColor(.red) From 7e5783bb8a34b58b44a225d74fe6cfc6db6b4e38 Mon Sep 17 00:00:00 2001 From: Eugene Cheah Date: Sat, 19 Mar 2022 12:50:19 +0800 Subject: [PATCH 03/22] Experimental "runAsSnapshot" for apple VM --- Configuration/UTMAppleConfiguration.swift | 31 ++++++++++++++++++++++- Managers/UTMAppleVirtualMachine.swift | 2 ++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/Configuration/UTMAppleConfiguration.swift b/Configuration/UTMAppleConfiguration.swift index 87a84712d..05831ea3c 100644 --- a/Configuration/UTMAppleConfiguration.swift +++ b/Configuration/UTMAppleConfiguration.swift @@ -616,6 +616,14 @@ final class UTMAppleConfiguration: UTMConfigurable, Codable, ObservableObject { } return urls } + + func resetDriveSnapShot() throws { + for i in diskImages.indices { + if( diskImages[i].runAsSnapshot ) { + diskImages[i].resetDriveSnapShot() + } + } + } } struct Bootloader: Codable { @@ -947,8 +955,29 @@ struct DiskImage: Codable, Hashable, Identifiable { } } + func snapshotURL() { + let snapshotURL = imageURL + snapshotURL.appendingPathComponent(".snapshot") + return snapshotURL + } + + func deleteDriveSnapShot() throws { + FileManager.default.removeItem( self.snapshotURL() ) + } + + func resetDriveSnapShot() throws { + let snapshotURL = try self.snapshotURL(); + FileManager.default.removeItem( snapshotURL ) + try FileManager.default.copyItem( imageURL, snapshotURL ) + } + func vzDiskImage() throws -> VZDiskImageStorageDeviceAttachment? { - // @TODO : Support runAsSnapshot logic + if runAsSnapshot, let snapshotURL = try self.snapshotURL() { + return try VZDiskImageStorageDeviceAttachment(url: snapshotURL, readOnly: isReadOnly) + } else { + return nil + } + if let imageURL = imageURL { return try VZDiskImageStorageDeviceAttachment(url: imageURL, readOnly: isReadOnly) } else { diff --git a/Managers/UTMAppleVirtualMachine.swift b/Managers/UTMAppleVirtualMachine.swift index e7ec08f32..951a6687d 100644 --- a/Managers/UTMAppleVirtualMachine.swift +++ b/Managers/UTMAppleVirtualMachine.swift @@ -323,6 +323,8 @@ import Virtualization } fsConfig.share = self?.makeDirectoryShare(from: newShares) } + + try appleConfig.resetDriveSnapShot() } apple = VZVirtualMachine(configuration: appleConfig.apple, queue: vmQueue) apple.delegate = self From fe07b9ecfc459658ba6b20a5ea408500e9c078e2 Mon Sep 17 00:00:00 2001 From: Eugene Cheah Date: Sat, 19 Mar 2022 13:04:00 +0800 Subject: [PATCH 04/22] Fixing beginers swift errors --- Configuration/UTMAppleConfiguration.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Configuration/UTMAppleConfiguration.swift b/Configuration/UTMAppleConfiguration.swift index 05831ea3c..63451f1bd 100644 --- a/Configuration/UTMAppleConfiguration.swift +++ b/Configuration/UTMAppleConfiguration.swift @@ -620,7 +620,7 @@ final class UTMAppleConfiguration: UTMConfigurable, Codable, ObservableObject { func resetDriveSnapShot() throws { for i in diskImages.indices { if( diskImages[i].runAsSnapshot ) { - diskImages[i].resetDriveSnapShot() + try diskImages[i].resetDriveSnapShot() } } } @@ -955,9 +955,9 @@ struct DiskImage: Codable, Hashable, Identifiable { } } - func snapshotURL() { + func snapshotURL() throws -> URL { let snapshotURL = imageURL - snapshotURL.appendingPathComponent(".snapshot") + snapshotURL = try snapshotURL.appendingPathComponent(".snapshot") return snapshotURL } From f2b20ef18d88c2dde78c610570851cce065d3530 Mon Sep 17 00:00:00 2001 From: Eugene Cheah Date: Sat, 19 Mar 2022 13:09:27 +0800 Subject: [PATCH 05/22] Fixing of compilation errors (swift noob here) --- Configuration/UTMAppleConfiguration.swift | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/Configuration/UTMAppleConfiguration.swift b/Configuration/UTMAppleConfiguration.swift index 63451f1bd..17f78f358 100644 --- a/Configuration/UTMAppleConfiguration.swift +++ b/Configuration/UTMAppleConfiguration.swift @@ -617,6 +617,15 @@ final class UTMAppleConfiguration: UTMConfigurable, Codable, ObservableObject { return urls } + /// Remove the snapshot URL image, this can be done as part of VM cleanup + func cleanupDriveSnapShot() { + for i in diskImages.indices { + diskImages[i].cleanupDriveSnapShot() + } + } + + /// Perform a snapshot clone of the current image URL to the snapshot URL + /// this is required for the snapshotURL image to "work" func resetDriveSnapShot() throws { for i in diskImages.indices { if( diskImages[i].runAsSnapshot ) { @@ -955,22 +964,30 @@ struct DiskImage: Codable, Hashable, Identifiable { } } + /// Returns the snapshot equivalent URL for the current image + /// Does not actually prepare the snapshot (this is done via resetDriveSnapShot) func snapshotURL() throws -> URL { let snapshotURL = imageURL snapshotURL = try snapshotURL.appendingPathComponent(".snapshot") return snapshotURL } - func deleteDriveSnapShot() throws { + /// Remove the snapshot URL image, this can be done as part of VM cleanup + func cleanupDriveSnapShot() { + /// this is intentionally allowed to fail, as the file may not exists FileManager.default.removeItem( self.snapshotURL() ) } + /// Perform a snapshot clone of the current image URL to the snapshot URL + /// this is required for the snapshotURL image to "work" func resetDriveSnapShot() throws { let snapshotURL = try self.snapshotURL(); FileManager.default.removeItem( snapshotURL ) try FileManager.default.copyItem( imageURL, snapshotURL ) } + /// Return the VZDiskImageStorageDeviceAttachment using the snapshotURL if runAsSnapshot is enabled + /// else returns using the imageURL if its configured. func vzDiskImage() throws -> VZDiskImageStorageDeviceAttachment? { if runAsSnapshot, let snapshotURL = try self.snapshotURL() { return try VZDiskImageStorageDeviceAttachment(url: snapshotURL, readOnly: isReadOnly) From ff2357ba086b231ff1e360caeddafc2ffd446cde Mon Sep 17 00:00:00 2001 From: Eugene Cheah Date: Sat, 19 Mar 2022 13:36:26 +0800 Subject: [PATCH 06/22] more swift compilation fixes --- Configuration/UTMAppleConfiguration.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Configuration/UTMAppleConfiguration.swift b/Configuration/UTMAppleConfiguration.swift index 17f78f358..ef26a513f 100644 --- a/Configuration/UTMAppleConfiguration.swift +++ b/Configuration/UTMAppleConfiguration.swift @@ -966,16 +966,16 @@ struct DiskImage: Codable, Hashable, Identifiable { /// Returns the snapshot equivalent URL for the current image /// Does not actually prepare the snapshot (this is done via resetDriveSnapShot) - func snapshotURL() throws -> URL { - let snapshotURL = imageURL - snapshotURL = try snapshotURL.appendingPathComponent(".snapshot") + func snapshotURL() throws -> URL? { + var snapshotURL = imageURL + snapshotURL = try snapshotURL?.appendingPathComponent(".snapshot") return snapshotURL } /// Remove the snapshot URL image, this can be done as part of VM cleanup func cleanupDriveSnapShot() { /// this is intentionally allowed to fail, as the file may not exists - FileManager.default.removeItem( self.snapshotURL() ) + FileManager.default.removeItem( at: self.snapshotURL() ) } /// Perform a snapshot clone of the current image URL to the snapshot URL @@ -983,7 +983,7 @@ struct DiskImage: Codable, Hashable, Identifiable { func resetDriveSnapShot() throws { let snapshotURL = try self.snapshotURL(); FileManager.default.removeItem( snapshotURL ) - try FileManager.default.copyItem( imageURL, snapshotURL ) + try FileManager.default.copyItem( atPath: imageURL, toPath: snapshotURL ) } /// Return the VZDiskImageStorageDeviceAttachment using the snapshotURL if runAsSnapshot is enabled From cc8d6e3f8d18847ef71cc511c283cc9d079639e8 Mon Sep 17 00:00:00 2001 From: Eugene Cheah Date: Sat, 19 Mar 2022 22:05:06 +0800 Subject: [PATCH 07/22] more swift syntax fixes --- Configuration/UTMAppleConfiguration.swift | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/Configuration/UTMAppleConfiguration.swift b/Configuration/UTMAppleConfiguration.swift index ef26a513f..c9fac660f 100644 --- a/Configuration/UTMAppleConfiguration.swift +++ b/Configuration/UTMAppleConfiguration.swift @@ -968,22 +968,28 @@ struct DiskImage: Codable, Hashable, Identifiable { /// Does not actually prepare the snapshot (this is done via resetDriveSnapShot) func snapshotURL() throws -> URL? { var snapshotURL = imageURL - snapshotURL = try snapshotURL?.appendingPathComponent(".snapshot") + snapshotURL = snapshotURL?.appendingPathComponent(".snapshot") return snapshotURL } /// Remove the snapshot URL image, this can be done as part of VM cleanup func cleanupDriveSnapShot() { - /// this is intentionally allowed to fail, as the file may not exists - FileManager.default.removeItem( at: self.snapshotURL() ) + if let snapshotURL = try snapshotURL() { + // this is intentionally allowed to return false, as the file may not exists + FileManager.default.removeItem( at: snapshotURL ) + } } /// Perform a snapshot clone of the current image URL to the snapshot URL /// this is required for the snapshotURL image to "work" func resetDriveSnapShot() throws { - let snapshotURL = try self.snapshotURL(); - FileManager.default.removeItem( snapshotURL ) - try FileManager.default.copyItem( atPath: imageURL, toPath: snapshotURL ) + if let snapshotURL = try self.snapshotURL() { + // this is intentionally allowed to return false, as the file may not exists + FileManager.default.removeItem( at: snapshotURL ) + + // lets actually perform the path copy + try FileManager.default.copyItem( atPath: imageURL, toPath: snapshotURL ) + } } /// Return the VZDiskImageStorageDeviceAttachment using the snapshotURL if runAsSnapshot is enabled From 5e489862d07a7e3d30617dd6463364ecda9e1f0f Mon Sep 17 00:00:00 2001 From: Eugene Cheah Date: Sat, 19 Mar 2022 22:15:30 +0800 Subject: [PATCH 08/22] more swift tweaks --- Configuration/UTMAppleConfiguration.swift | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/Configuration/UTMAppleConfiguration.swift b/Configuration/UTMAppleConfiguration.swift index c9fac660f..39b1442f1 100644 --- a/Configuration/UTMAppleConfiguration.swift +++ b/Configuration/UTMAppleConfiguration.swift @@ -974,28 +974,31 @@ struct DiskImage: Codable, Hashable, Identifiable { /// Remove the snapshot URL image, this can be done as part of VM cleanup func cleanupDriveSnapShot() { - if let snapshotURL = try snapshotURL() { + if let snapshotURL = snapshotURL() { // this is intentionally allowed to return false, as the file may not exists - FileManager.default.removeItem( at: snapshotURL ) + if try? FileManager.default.removeItem( at: snapshotURL ) { + // does nothing + } } } /// Perform a snapshot clone of the current image URL to the snapshot URL /// this is required for the snapshotURL image to "work" func resetDriveSnapShot() throws { - if let snapshotURL = try self.snapshotURL() { - // this is intentionally allowed to return false, as the file may not exists - FileManager.default.removeItem( at: snapshotURL ) + // Perform any needed cleanup first + cleanupDriveSnapShot() + // and make a copy of the provided imageURL + if let snapshotURL = snapshotURL() { // lets actually perform the path copy try FileManager.default.copyItem( atPath: imageURL, toPath: snapshotURL ) - } + } } /// Return the VZDiskImageStorageDeviceAttachment using the snapshotURL if runAsSnapshot is enabled /// else returns using the imageURL if its configured. func vzDiskImage() throws -> VZDiskImageStorageDeviceAttachment? { - if runAsSnapshot, let snapshotURL = try self.snapshotURL() { + if runAsSnapshot, let snapshotURL = snapshotURL() { return try VZDiskImageStorageDeviceAttachment(url: snapshotURL, readOnly: isReadOnly) } else { return nil From 5cfdf7d4cd594b139c81a7c843221914f100c044 Mon Sep 17 00:00:00 2001 From: Eugene Cheah Date: Sat, 19 Mar 2022 22:37:35 +0800 Subject: [PATCH 09/22] still trying =| --- Configuration/UTMAppleConfiguration.swift | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Configuration/UTMAppleConfiguration.swift b/Configuration/UTMAppleConfiguration.swift index 39b1442f1..17dcb9f2e 100644 --- a/Configuration/UTMAppleConfiguration.swift +++ b/Configuration/UTMAppleConfiguration.swift @@ -976,9 +976,7 @@ struct DiskImage: Codable, Hashable, Identifiable { func cleanupDriveSnapShot() { if let snapshotURL = snapshotURL() { // this is intentionally allowed to return false, as the file may not exists - if try? FileManager.default.removeItem( at: snapshotURL ) { - // does nothing - } + try FileManager.default.removeItem( at: snapshotURL ) } } @@ -989,7 +987,7 @@ struct DiskImage: Codable, Hashable, Identifiable { cleanupDriveSnapShot() // and make a copy of the provided imageURL - if let snapshotURL = snapshotURL() { + if try let snapshotURL = snapshotURL() { // lets actually perform the path copy try FileManager.default.copyItem( atPath: imageURL, toPath: snapshotURL ) } @@ -998,7 +996,7 @@ struct DiskImage: Codable, Hashable, Identifiable { /// Return the VZDiskImageStorageDeviceAttachment using the snapshotURL if runAsSnapshot is enabled /// else returns using the imageURL if its configured. func vzDiskImage() throws -> VZDiskImageStorageDeviceAttachment? { - if runAsSnapshot, let snapshotURL = snapshotURL() { + if runAsSnapshot, try let snapshotURL = snapshotURL() { return try VZDiskImageStorageDeviceAttachment(url: snapshotURL, readOnly: isReadOnly) } else { return nil From e31e008489c58eab20209cbdbf261015f2d18341 Mon Sep 17 00:00:00 2001 From: Eugene Cheah Date: Sat, 19 Mar 2022 22:42:51 +0800 Subject: [PATCH 10/22] fixing the let try (i should install the xcode) --- Configuration/UTMAppleConfiguration.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Configuration/UTMAppleConfiguration.swift b/Configuration/UTMAppleConfiguration.swift index 17dcb9f2e..a2f58b13d 100644 --- a/Configuration/UTMAppleConfiguration.swift +++ b/Configuration/UTMAppleConfiguration.swift @@ -974,7 +974,7 @@ struct DiskImage: Codable, Hashable, Identifiable { /// Remove the snapshot URL image, this can be done as part of VM cleanup func cleanupDriveSnapShot() { - if let snapshotURL = snapshotURL() { + if let snapshotURL = try snapshotURL() { // this is intentionally allowed to return false, as the file may not exists try FileManager.default.removeItem( at: snapshotURL ) } @@ -987,7 +987,7 @@ struct DiskImage: Codable, Hashable, Identifiable { cleanupDriveSnapShot() // and make a copy of the provided imageURL - if try let snapshotURL = snapshotURL() { + if let snapshotURL = try snapshotURL() { // lets actually perform the path copy try FileManager.default.copyItem( atPath: imageURL, toPath: snapshotURL ) } @@ -996,7 +996,7 @@ struct DiskImage: Codable, Hashable, Identifiable { /// Return the VZDiskImageStorageDeviceAttachment using the snapshotURL if runAsSnapshot is enabled /// else returns using the imageURL if its configured. func vzDiskImage() throws -> VZDiskImageStorageDeviceAttachment? { - if runAsSnapshot, try let snapshotURL = snapshotURL() { + if runAsSnapshot, let snapshotURL = try snapshotURL() { return try VZDiskImageStorageDeviceAttachment(url: snapshotURL, readOnly: isReadOnly) } else { return nil From d4b9380fa1cb76c9a7c72cb5537c86e4980d6d42 Mon Sep 17 00:00:00 2001 From: Eugene Cheah Date: Sun, 20 Mar 2022 00:30:04 +0800 Subject: [PATCH 11/22] more attempts at fixing the apple config --- Configuration/UTMAppleConfiguration.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Configuration/UTMAppleConfiguration.swift b/Configuration/UTMAppleConfiguration.swift index a2f58b13d..834b10062 100644 --- a/Configuration/UTMAppleConfiguration.swift +++ b/Configuration/UTMAppleConfiguration.swift @@ -618,7 +618,7 @@ final class UTMAppleConfiguration: UTMConfigurable, Codable, ObservableObject { } /// Remove the snapshot URL image, this can be done as part of VM cleanup - func cleanupDriveSnapShot() { + func cleanupDriveSnapShot() throws { for i in diskImages.indices { diskImages[i].cleanupDriveSnapShot() } @@ -973,7 +973,7 @@ struct DiskImage: Codable, Hashable, Identifiable { } /// Remove the snapshot URL image, this can be done as part of VM cleanup - func cleanupDriveSnapShot() { + func cleanupDriveSnapShot() throws { if let snapshotURL = try snapshotURL() { // this is intentionally allowed to return false, as the file may not exists try FileManager.default.removeItem( at: snapshotURL ) @@ -989,7 +989,7 @@ struct DiskImage: Codable, Hashable, Identifiable { // and make a copy of the provided imageURL if let snapshotURL = try snapshotURL() { // lets actually perform the path copy - try FileManager.default.copyItem( atPath: imageURL, toPath: snapshotURL ) + try FileManager.default.copyItem( at: imageURL, to: snapshotURL ) } } From d7ce877d3080f5001ff9a67ba6b3f1add0876627 Mon Sep 17 00:00:00 2001 From: Eugene Cheah Date: Sun, 20 Mar 2022 00:38:26 +0800 Subject: [PATCH 12/22] Still figuring out swift --- Configuration/UTMAppleConfiguration.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Configuration/UTMAppleConfiguration.swift b/Configuration/UTMAppleConfiguration.swift index 834b10062..8917d03e7 100644 --- a/Configuration/UTMAppleConfiguration.swift +++ b/Configuration/UTMAppleConfiguration.swift @@ -620,7 +620,7 @@ final class UTMAppleConfiguration: UTMConfigurable, Codable, ObservableObject { /// Remove the snapshot URL image, this can be done as part of VM cleanup func cleanupDriveSnapShot() throws { for i in diskImages.indices { - diskImages[i].cleanupDriveSnapShot() + try diskImages[i].cleanupDriveSnapShot() } } @@ -984,10 +984,10 @@ struct DiskImage: Codable, Hashable, Identifiable { /// this is required for the snapshotURL image to "work" func resetDriveSnapShot() throws { // Perform any needed cleanup first - cleanupDriveSnapShot() + try cleanupDriveSnapShot() // and make a copy of the provided imageURL - if let snapshotURL = try snapshotURL() { + if let snapshotURL = try snapshotURL(), let imageURL = imageURL { // lets actually perform the path copy try FileManager.default.copyItem( at: imageURL, to: snapshotURL ) } From 9935034fca77ca83450a5d6aa871f89479270faa Mon Sep 17 00:00:00 2001 From: Eugene Cheah Date: Sun, 20 Mar 2022 00:55:15 +0800 Subject: [PATCH 13/22] Added comments for copyItem --- Configuration/UTMAppleConfiguration.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Configuration/UTMAppleConfiguration.swift b/Configuration/UTMAppleConfiguration.swift index 8917d03e7..215955378 100644 --- a/Configuration/UTMAppleConfiguration.swift +++ b/Configuration/UTMAppleConfiguration.swift @@ -988,7 +988,8 @@ struct DiskImage: Codable, Hashable, Identifiable { // and make a copy of the provided imageURL if let snapshotURL = try snapshotURL(), let imageURL = imageURL { - // lets actually perform the path copy + // lets setup the snapshot file + // (currently i have no idea if this optimizes when under APFS) try FileManager.default.copyItem( at: imageURL, to: snapshotURL ) } } From eb2023230b9698e63519e782a959fd725b4cf00e Mon Sep 17 00:00:00 2001 From: Eugene Cheah Date: Thu, 7 Apr 2022 16:58:46 +0800 Subject: [PATCH 14/22] Made suggested change, for triggering cleanupDriveSnapShot when VM stops --- Managers/UTMAppleVirtualMachine.swift | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Managers/UTMAppleVirtualMachine.swift b/Managers/UTMAppleVirtualMachine.swift index 951a6687d..ef3fecf2f 100644 --- a/Managers/UTMAppleVirtualMachine.swift +++ b/Managers/UTMAppleVirtualMachine.swift @@ -190,6 +190,12 @@ import Virtualization } } } + + + // This perform any cleanup for the "--snapshot" feature, + // if it was initialized previously + try appleConfig.cleanupDriveSnapShot() + } override func vmStop(force: Bool) async throws { @@ -323,9 +329,11 @@ import Virtualization } fsConfig.share = self?.makeDirectoryShare(from: newShares) } - - try appleConfig.resetDriveSnapShot() } + + // This perform any reset's needed for the "--snapshot" feature (if its in use) + try appleConfig.resetDriveSnapShot() + apple = VZVirtualMachine(configuration: appleConfig.apple, queue: vmQueue) apple.delegate = self } From 4b1507b66ce2727ad6aee4c694bbcbebd28f8813 Mon Sep 17 00:00:00 2001 From: Eugene Cheah Date: Thu, 7 Apr 2022 17:00:17 +0800 Subject: [PATCH 15/22] renamed resetDriveSnapshot with setupDriveSnapshot --- Configuration/UTMAppleConfiguration.swift | 8 ++++---- Managers/UTMAppleVirtualMachine.swift | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Configuration/UTMAppleConfiguration.swift b/Configuration/UTMAppleConfiguration.swift index 215955378..6603e64a2 100644 --- a/Configuration/UTMAppleConfiguration.swift +++ b/Configuration/UTMAppleConfiguration.swift @@ -626,10 +626,10 @@ final class UTMAppleConfiguration: UTMConfigurable, Codable, ObservableObject { /// Perform a snapshot clone of the current image URL to the snapshot URL /// this is required for the snapshotURL image to "work" - func resetDriveSnapShot() throws { + func setupDriveSnapShot() throws { for i in diskImages.indices { if( diskImages[i].runAsSnapshot ) { - try diskImages[i].resetDriveSnapShot() + try diskImages[i].setupDriveSnapShot() } } } @@ -965,7 +965,7 @@ struct DiskImage: Codable, Hashable, Identifiable { } /// Returns the snapshot equivalent URL for the current image - /// Does not actually prepare the snapshot (this is done via resetDriveSnapShot) + /// Does not actually prepare the snapshot (this is done via setupDriveSnapShot) func snapshotURL() throws -> URL? { var snapshotURL = imageURL snapshotURL = snapshotURL?.appendingPathComponent(".snapshot") @@ -982,7 +982,7 @@ struct DiskImage: Codable, Hashable, Identifiable { /// Perform a snapshot clone of the current image URL to the snapshot URL /// this is required for the snapshotURL image to "work" - func resetDriveSnapShot() throws { + func setupDriveSnapShot() throws { // Perform any needed cleanup first try cleanupDriveSnapShot() diff --git a/Managers/UTMAppleVirtualMachine.swift b/Managers/UTMAppleVirtualMachine.swift index ef3fecf2f..ebd2b24b6 100644 --- a/Managers/UTMAppleVirtualMachine.swift +++ b/Managers/UTMAppleVirtualMachine.swift @@ -332,7 +332,7 @@ import Virtualization } // This perform any reset's needed for the "--snapshot" feature (if its in use) - try appleConfig.resetDriveSnapShot() + try appleConfig.setupDriveSnapShot() apple = VZVirtualMachine(configuration: appleConfig.apple, queue: vmQueue) apple.delegate = self From ff191aa8b14ab964402509cdb820840497d92393 Mon Sep 17 00:00:00 2001 From: Eugene Cheah Date: Thu, 7 Apr 2022 17:01:27 +0800 Subject: [PATCH 16/22] Standardise SnapShot to Snapshot --- Configuration/UTMAppleConfiguration.swift | 16 ++++++++-------- Managers/UTMAppleVirtualMachine.swift | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Configuration/UTMAppleConfiguration.swift b/Configuration/UTMAppleConfiguration.swift index 6603e64a2..aeb7ebae5 100644 --- a/Configuration/UTMAppleConfiguration.swift +++ b/Configuration/UTMAppleConfiguration.swift @@ -618,18 +618,18 @@ final class UTMAppleConfiguration: UTMConfigurable, Codable, ObservableObject { } /// Remove the snapshot URL image, this can be done as part of VM cleanup - func cleanupDriveSnapShot() throws { + func cleanupDriveSnapshot() throws { for i in diskImages.indices { - try diskImages[i].cleanupDriveSnapShot() + try diskImages[i].cleanupDriveSnapshot() } } /// Perform a snapshot clone of the current image URL to the snapshot URL /// this is required for the snapshotURL image to "work" - func setupDriveSnapShot() throws { + func setupDriveSnapshot() throws { for i in diskImages.indices { if( diskImages[i].runAsSnapshot ) { - try diskImages[i].setupDriveSnapShot() + try diskImages[i].setupDriveSnapshot() } } } @@ -965,7 +965,7 @@ struct DiskImage: Codable, Hashable, Identifiable { } /// Returns the snapshot equivalent URL for the current image - /// Does not actually prepare the snapshot (this is done via setupDriveSnapShot) + /// Does not actually prepare the snapshot (this is done via setupDriveSnapshot) func snapshotURL() throws -> URL? { var snapshotURL = imageURL snapshotURL = snapshotURL?.appendingPathComponent(".snapshot") @@ -973,7 +973,7 @@ struct DiskImage: Codable, Hashable, Identifiable { } /// Remove the snapshot URL image, this can be done as part of VM cleanup - func cleanupDriveSnapShot() throws { + func cleanupDriveSnapshot() throws { if let snapshotURL = try snapshotURL() { // this is intentionally allowed to return false, as the file may not exists try FileManager.default.removeItem( at: snapshotURL ) @@ -982,9 +982,9 @@ struct DiskImage: Codable, Hashable, Identifiable { /// Perform a snapshot clone of the current image URL to the snapshot URL /// this is required for the snapshotURL image to "work" - func setupDriveSnapShot() throws { + func setupDriveSnapshot() throws { // Perform any needed cleanup first - try cleanupDriveSnapShot() + try cleanupDriveSnapshot() // and make a copy of the provided imageURL if let snapshotURL = try snapshotURL(), let imageURL = imageURL { diff --git a/Managers/UTMAppleVirtualMachine.swift b/Managers/UTMAppleVirtualMachine.swift index ebd2b24b6..f35dd741e 100644 --- a/Managers/UTMAppleVirtualMachine.swift +++ b/Managers/UTMAppleVirtualMachine.swift @@ -194,7 +194,7 @@ import Virtualization // This perform any cleanup for the "--snapshot" feature, // if it was initialized previously - try appleConfig.cleanupDriveSnapShot() + try appleConfig.cleanupDriveSnapshot() } @@ -332,7 +332,7 @@ import Virtualization } // This perform any reset's needed for the "--snapshot" feature (if its in use) - try appleConfig.setupDriveSnapShot() + try appleConfig.setupDriveSnapshot() apple = VZVirtualMachine(configuration: appleConfig.apple, queue: vmQueue) apple.delegate = self From 4a18aaa3d6ef511c53e60129bcd7f1d5dde0f129 Mon Sep 17 00:00:00 2001 From: Eugene Cheah Date: Thu, 7 Apr 2022 17:04:10 +0800 Subject: [PATCH 17/22] Apply suggested swift code formatting --- Configuration/UTMAppleConfiguration.swift | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Configuration/UTMAppleConfiguration.swift b/Configuration/UTMAppleConfiguration.swift index aeb7ebae5..6c60b6175 100644 --- a/Configuration/UTMAppleConfiguration.swift +++ b/Configuration/UTMAppleConfiguration.swift @@ -628,7 +628,7 @@ final class UTMAppleConfiguration: UTMConfigurable, Codable, ObservableObject { /// this is required for the snapshotURL image to "work" func setupDriveSnapshot() throws { for i in diskImages.indices { - if( diskImages[i].runAsSnapshot ) { + if diskImages[i].runAsSnapshot { try diskImages[i].setupDriveSnapshot() } } @@ -967,9 +967,7 @@ struct DiskImage: Codable, Hashable, Identifiable { /// Returns the snapshot equivalent URL for the current image /// Does not actually prepare the snapshot (this is done via setupDriveSnapshot) func snapshotURL() throws -> URL? { - var snapshotURL = imageURL - snapshotURL = snapshotURL?.appendingPathComponent(".snapshot") - return snapshotURL + return imageURL?.appendingPathComponent(".snapshot") } /// Remove the snapshot URL image, this can be done as part of VM cleanup From 6cc2c6eaa8165693bc23c84b98b9068c2e49cda9 Mon Sep 17 00:00:00 2001 From: Eugene Cheah Date: Thu, 7 Apr 2022 17:06:22 +0800 Subject: [PATCH 18/22] Additional swift formatting change --- Configuration/UTMAppleConfiguration.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Configuration/UTMAppleConfiguration.swift b/Configuration/UTMAppleConfiguration.swift index 6c60b6175..4a9b1cc11 100644 --- a/Configuration/UTMAppleConfiguration.swift +++ b/Configuration/UTMAppleConfiguration.swift @@ -973,8 +973,8 @@ struct DiskImage: Codable, Hashable, Identifiable { /// Remove the snapshot URL image, this can be done as part of VM cleanup func cleanupDriveSnapshot() throws { if let snapshotURL = try snapshotURL() { - // this is intentionally allowed to return false, as the file may not exists - try FileManager.default.removeItem( at: snapshotURL ) + // The file may not exists, if so nothing happens + try FileManager.default.removeItem(at: snapshotURL) } } @@ -988,7 +988,7 @@ struct DiskImage: Codable, Hashable, Identifiable { if let snapshotURL = try snapshotURL(), let imageURL = imageURL { // lets setup the snapshot file // (currently i have no idea if this optimizes when under APFS) - try FileManager.default.copyItem( at: imageURL, to: snapshotURL ) + try FileManager.default.copyItem(at: imageURL, to: snapshotURL) } } From f7f2f70ed46b18a62d56772ff4690f07db003d6b Mon Sep 17 00:00:00 2001 From: Eugene Cheah Date: Thu, 7 Apr 2022 17:09:32 +0800 Subject: [PATCH 19/22] commenting clarification for copyItem --- Configuration/UTMAppleConfiguration.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Configuration/UTMAppleConfiguration.swift b/Configuration/UTMAppleConfiguration.swift index 4a9b1cc11..d86b1d15a 100644 --- a/Configuration/UTMAppleConfiguration.swift +++ b/Configuration/UTMAppleConfiguration.swift @@ -987,7 +987,7 @@ struct DiskImage: Codable, Hashable, Identifiable { // and make a copy of the provided imageURL if let snapshotURL = try snapshotURL(), let imageURL = imageURL { // lets setup the snapshot file - // (currently i have no idea if this optimizes when under APFS) + // AFAICT this does a shallow copy on APFS drives try FileManager.default.copyItem(at: imageURL, to: snapshotURL) } } From 5e651600b0e2544ae54de2bd178eb89ca45e2826 Mon Sep 17 00:00:00 2001 From: Eugene Cheah Date: Thu, 7 Apr 2022 17:26:55 +0800 Subject: [PATCH 20/22] Adding system wide "isRunAsSnapshot" --- Configuration/UTMAppleConfiguration.swift | 5 +++++ Platform/macOS/VMConfigAppleSystemView.swift | 3 +++ 2 files changed, 8 insertions(+) diff --git a/Configuration/UTMAppleConfiguration.swift b/Configuration/UTMAppleConfiguration.swift index d86b1d15a..8ae213751 100644 --- a/Configuration/UTMAppleConfiguration.swift +++ b/Configuration/UTMAppleConfiguration.swift @@ -275,6 +275,7 @@ final class UTMAppleConfiguration: UTMConfigurable, Codable, ObservableObject { @Published var isSerialEnabled: Bool @Published var isConsoleDisplay: Bool + @Published var isRunAsSnapshot: Bool @available(macOS 12, *) var isKeyboardEnabled: Bool { @@ -337,6 +338,7 @@ final class UTMAppleConfiguration: UTMConfigurable, Codable, ObservableObject { case isConsoleDisplay case isKeyboardEnabled case isPointingEnabled + case isRunAsSnapshot } init() { @@ -357,6 +359,7 @@ final class UTMAppleConfiguration: UTMConfigurable, Codable, ObservableObject { isAppleVirtualization = true isSerialEnabled = false isConsoleDisplay = false + isRunAsSnapshot = false memorySize = 4 * 1024 * 1024 * 1024 cpuCount = 4 } @@ -398,6 +401,7 @@ final class UTMAppleConfiguration: UTMConfigurable, Codable, ObservableObject { isEntropyEnabled = try values.decode(Bool.self, forKey: .isEntropyEnabled) isSerialEnabled = try values.decode(Bool.self, forKey: .isSerialEnabled) isConsoleDisplay = try values.decode(Bool.self, forKey: .isConsoleDisplay) + isRunAsSnapshot = try values.decode(Bool.self, forKey: .isRunAsSnapshot) name = try values.decode(String.self, forKey: .name) architecture = try values.decode(String.self, forKey: .architecture) icon = try values.decodeIfPresent(String.self, forKey: .icon) @@ -435,6 +439,7 @@ final class UTMAppleConfiguration: UTMConfigurable, Codable, ObservableObject { try container.encode(isEntropyEnabled, forKey: .isEntropyEnabled) try container.encode(isSerialEnabled, forKey: .isSerialEnabled) try container.encode(isConsoleDisplay, forKey: .isConsoleDisplay) + try container.encode(isRunAsSnapshot, forKey: .isRunAsSnapshot) try container.encode(name, forKey: .name) try container.encode(architecture, forKey: .architecture) try container.encodeIfPresent(icon, forKey: .icon) diff --git a/Platform/macOS/VMConfigAppleSystemView.swift b/Platform/macOS/VMConfigAppleSystemView.swift index 5cd3f6f0c..b03ed5394 100644 --- a/Platform/macOS/VMConfigAppleSystemView.swift +++ b/Platform/macOS/VMConfigAppleSystemView.swift @@ -75,6 +75,9 @@ struct VMConfigAppleSystemView: View { Toggle("Enable Keyboard", isOn: $config.isKeyboardEnabled) Toggle("Enable Pointer", isOn: $config.isPointingEnabled) } + + // System wide --runAsSnapshot toggle, if enabled this would be set as "true" on all drives + Toggle("Enable 'Run using a snapshot' on all drives", isOn: $config.runAsSnapshot) } } } From 24813ebceddd9fb6c4fc2544952be6858131c0c1 Mon Sep 17 00:00:00 2001 From: Eugene Cheah Date: Thu, 7 Apr 2022 17:30:02 +0800 Subject: [PATCH 21/22] Systemwide --snapshot toggle --- Configuration/UTMAppleConfiguration.swift | 6 ++++++ Platform/macOS/VMConfigAppleSystemView.swift | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Configuration/UTMAppleConfiguration.swift b/Configuration/UTMAppleConfiguration.swift index 8ae213751..08afd4b6e 100644 --- a/Configuration/UTMAppleConfiguration.swift +++ b/Configuration/UTMAppleConfiguration.swift @@ -633,6 +633,12 @@ final class UTMAppleConfiguration: UTMConfigurable, Codable, ObservableObject { /// this is required for the snapshotURL image to "work" func setupDriveSnapshot() throws { for i in diskImages.indices { + // Apply --snapshot on all volumes if its configured systemwide + if self.isRunAsSnapshot { + diskImages[i].runAsSnapshot = true + } + + // Setup the --snapshot on a per drive level if diskImages[i].runAsSnapshot { try diskImages[i].setupDriveSnapshot() } diff --git a/Platform/macOS/VMConfigAppleSystemView.swift b/Platform/macOS/VMConfigAppleSystemView.swift index b03ed5394..08e83f4e4 100644 --- a/Platform/macOS/VMConfigAppleSystemView.swift +++ b/Platform/macOS/VMConfigAppleSystemView.swift @@ -76,7 +76,7 @@ struct VMConfigAppleSystemView: View { Toggle("Enable Pointer", isOn: $config.isPointingEnabled) } - // System wide --runAsSnapshot toggle, if enabled this would be set as "true" on all drives + // System wide --snapshot toggle, if enabled this would be set as "true" on all drives Toggle("Enable 'Run using a snapshot' on all drives", isOn: $config.runAsSnapshot) } } From 7112e2679876d18cdf18c6175f877e9fa43935f9 Mon Sep 17 00:00:00 2001 From: Eugene Cheah Date: Thu, 7 Apr 2022 20:51:47 +0800 Subject: [PATCH 22/22] Fixed wrong var ref --- Platform/macOS/VMConfigAppleSystemView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Platform/macOS/VMConfigAppleSystemView.swift b/Platform/macOS/VMConfigAppleSystemView.swift index 08e83f4e4..62f4e6ae1 100644 --- a/Platform/macOS/VMConfigAppleSystemView.swift +++ b/Platform/macOS/VMConfigAppleSystemView.swift @@ -77,7 +77,7 @@ struct VMConfigAppleSystemView: View { } // System wide --snapshot toggle, if enabled this would be set as "true" on all drives - Toggle("Enable 'Run using a snapshot' on all drives", isOn: $config.runAsSnapshot) + Toggle("Enable 'Run using a snapshot' on all drives", isOn: $config.isRunAsSnapshot) } } }