Merge pull request #5919 from gnattu/main

fix: Add workarounds to prevent filesystem corruption on Apple Virtualization VMs.
This commit is contained in:
osy
2024-05-23 20:57:38 -07:00
committed by GitHub
9 changed files with 58 additions and 5 deletions
+3 -1
View File
@@ -265,11 +265,13 @@ extension UTMAppleConfiguration {
}
if !ignoringDrives {
vzconfig.storageDevices = try drives.compactMap { drive in
guard let attachment = try drive.vzDiskImage() else {
guard let attachment = try drive.vzDiskImage(useFsWorkAround: system.boot.operatingSystem == .linux) else {
return nil
}
if #available(macOS 13, *), drive.isExternal {
return VZUSBMassStorageDeviceConfiguration(attachment: attachment)
} else if #available(macOS 14, *), drive.isNvme, system.boot.operatingSystem == .linux {
return VZNVMExpressControllerDeviceConfiguration(attachment: attachment)
} else {
return VZVirtioBlockDeviceConfiguration(attachment: attachment)
}
+16 -3
View File
@@ -25,6 +25,7 @@ struct UTMAppleConfigurationDrive: UTMConfigurationDrive {
var sizeMib: Int = 0
var isReadOnly: Bool
var isExternal: Bool
var isNvme: Bool
var imageURL: URL?
var imageName: String?
@@ -36,6 +37,7 @@ struct UTMAppleConfigurationDrive: UTMConfigurationDrive {
private enum CodingKeys: String, CodingKey {
case isReadOnly = "ReadOnly"
case isNvme = "Nvme"
case imageName = "ImageName"
case bookmark = "Bookmark" // legacy only
case identifier = "Identifier"
@@ -55,12 +57,14 @@ struct UTMAppleConfigurationDrive: UTMConfigurationDrive {
sizeMib = newSize
isReadOnly = false
isExternal = false
isNvme = false
}
init(existingURL url: URL?, isExternal: Bool = false) {
init(existingURL url: URL?, isExternal: Bool = false, isNvme: Bool = false) {
self.imageURL = url
self.isReadOnly = isExternal
self.isExternal = isExternal
self.isNvme = isNvme
}
init(from decoder: Decoder) throws {
@@ -83,6 +87,7 @@ struct UTMAppleConfigurationDrive: UTMConfigurationDrive {
isExternal = true
}
isReadOnly = try container.decodeIfPresent(Bool.self, forKey: .isReadOnly) ?? isExternal
isNvme = try container.decodeIfPresent(Bool.self, forKey: .isNvme) ?? false
id = try container.decode(String.self, forKey: .identifier)
}
@@ -92,12 +97,18 @@ struct UTMAppleConfigurationDrive: UTMConfigurationDrive {
try container.encodeIfPresent(imageName, forKey: .imageName)
}
try container.encode(isReadOnly, forKey: .isReadOnly)
try container.encode(isNvme, forKey: .isNvme)
try container.encode(id, forKey: .identifier)
}
func vzDiskImage() throws -> VZDiskImageStorageDeviceAttachment? {
func vzDiskImage(useFsWorkAround: Bool = false) throws -> VZDiskImageStorageDeviceAttachment? {
if let imageURL = imageURL {
return try VZDiskImageStorageDeviceAttachment(url: imageURL, readOnly: isReadOnly)
// Use cached caching mode for virtio drive to prevent fs corruption on linux when possible
if #available(macOS 12.0, *), !isNvme, useFsWorkAround {
return try VZDiskImageStorageDeviceAttachment(url: imageURL, readOnly: isReadOnly, cachingMode: .cached, synchronizationMode: .full)
} else {
return try VZDiskImageStorageDeviceAttachment(url: imageURL, readOnly: isReadOnly)
}
} else {
return nil
}
@@ -107,6 +118,7 @@ struct UTMAppleConfigurationDrive: UTMConfigurationDrive {
imageName?.hash(into: &hasher)
sizeMib.hash(into: &hasher)
isReadOnly.hash(into: &hasher)
isNvme.hash(into: &hasher)
isExternal.hash(into: &hasher)
id.hash(into: &hasher)
}
@@ -127,6 +139,7 @@ extension UTMAppleConfigurationDrive {
sizeMib = oldDrive.sizeMib
isReadOnly = oldDrive.isReadOnly
isExternal = oldDrive.isExternal
isNvme = false
imageURL = oldDrive.imageURL
}
}
+6 -1
View File
@@ -135,6 +135,7 @@ enum VMBootDevice: Int, Identifiable {
@Published var sharingReadOnly: Bool = false
@Published var name: String?
@Published var isOpenSettingsAfterCreation: Bool = false
@Published var useNvmeAsDiskInterface = false
/// SwiftUI BUG: on macOS 12, when VoiceOver is enabled and isBusy changes the disable state of a button being clicked,
var isNeverDisabledWorkaround: Bool {
@@ -342,7 +343,11 @@ enum VMBootDevice: Int, Identifiable {
}
}
if !isSkipDiskCreate {
config.drives.append(UTMAppleConfigurationDrive(newSize: storageSizeGib * bytesInGib / bytesInMib))
var newDisk = UTMAppleConfigurationDrive(newSize: storageSizeGib * bytesInGib / bytesInMib)
if #available(macOS 14, *), useNvmeAsDiskInterface {
newDisk.isNvme = true
}
config.drives.append(newDisk)
}
if #available(macOS 12, *), let sharingDirectoryURL = sharingDirectoryURL {
config.sharedDirectories = [UTMAppleConfigurationSharedDirectory(directoryURL: sharingDirectoryURL, isReadOnly: sharingReadOnly)]
+6
View File
@@ -1141,3 +1141,9 @@
// UTMQemuMonitor.m
"Guest panic" = "ゲストがパニック状態に陥りました";
/* VMConfigAppleDriveDetailsView
VMConfigAppleDriveCreateView*/
"Use NVMe Interface" = "NVMe を使用します";
"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." = "チェックされている場合、ディスクインターフェースとして virtio の代わりに NVMe を使用します。macOS 14+ では Linux ゲストのみ利用可能です。このインターフェースは遅いですが、ファイルシステムエラーが発生する可能性が低いです。";
@@ -33,11 +33,17 @@ struct VMConfigAppleDriveCreateView: View {
if newValue {
config.sizeMib = 0
config.isReadOnly = true
config.isNvme = false
} else {
config.sizeMib = 10240
config.isReadOnly = false
}
}
if #available(macOS 14, *), !config.isExternal {
Toggle(isOn: $config.isNvme.animation(), label: {
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 !config.isExternal {
SizeTextField($config.sizeMib)
}
@@ -28,6 +28,12 @@ struct VMConfigAppleDriveDetailsView: View {
TextField("Name", text: .constant(config.imageURL?.lastPathComponent ?? NSLocalizedString("(New Drive)", comment: "VMConfigAppleDriveDetailsView")))
.disabled(true)
Toggle("Read Only?", isOn: $config.isReadOnly)
if #available(macOS 14, *), !config.isExternal {
Toggle(isOn: $config.isNvme,
label: {
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 #unavailable(macOS 12) {
Button {
requestDriveDelete = config
+5
View File
@@ -2297,3 +2297,8 @@
/* No comment provided by engineer. */
"Zoom" = "縮放";
/* VMConfigAppleDriveDetailsView
VMConfigAppleDriveCreateView*/
"Use NVMe Interface" = "使用 NVMe 磁碟介面";
"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." = "如果勾選,將使用 NVMe 而非 virtio 作為磁碟介面,僅在 macOS 14+ 中適用於 Linux 客戶機器。這個介面速度較慢,但較不容易遇到檔案系統錯誤。";
@@ -2297,3 +2297,8 @@
/* No comment provided by engineer. */
"Zoom" = "缩放";
/* VMConfigAppleDriveDetailsView
VMConfigAppleDriveCreateView*/
"Use NVMe Interface" = "使用 NVMe 磁盘接口";
"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." = "如果选中,使用 NVMe 而不是 virtio 作为磁盘接口,仅适用于 macOS 14+ 上的 Linux 客户机。此接口速度较慢,但不太容易遇到文件系统错误。";
@@ -2002,3 +2002,8 @@
/* No comment provided by engineer. */
"Zoom" = "縮放";
/* VMConfigAppleDriveDetailsView
VMConfigAppleDriveCreateView*/
"Use NVMe Interface" = "使用 NVMe 磁碟介面";
"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." = "如果選取,將使用 NVMe 而非 virtio 作為磁碟介面,僅在 macOS 14+ 中適用於 Linux 客戶機器。這個介面速度較慢,但較不容易遇到檔案系統錯誤。";