Skip to content

Commit

Permalink
Merge pull request #5919 from gnattu/main
Browse files Browse the repository at this point in the history
fix: Add workarounds to prevent filesystem corruption on Apple Virtualization VMs.
  • Loading branch information
osy authored May 24, 2024
2 parents 9dc2e94 + e63b53a commit 8e84a1e
Show file tree
Hide file tree
Showing 9 changed files with 58 additions and 5 deletions.
4 changes: 3 additions & 1 deletion Configuration/UTMAppleConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
19 changes: 16 additions & 3 deletions Configuration/UTMAppleConfigurationDrive.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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?

Expand All @@ -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"
Expand All @@ -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 {
Expand All @@ -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)
}

Expand All @@ -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
}
Expand All @@ -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)
}
Expand All @@ -127,6 +139,7 @@ extension UTMAppleConfigurationDrive {
sizeMib = oldDrive.sizeMib
isReadOnly = oldDrive.isReadOnly
isExternal = oldDrive.isExternal
isNvme = false
imageURL = oldDrive.imageURL
}
}
7 changes: 6 additions & 1 deletion Platform/Shared/VMWizardState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)]
Expand Down
6 changes: 6 additions & 0 deletions Platform/ja.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -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 ゲストのみ利用可能です。このインターフェースは遅いですが、ファイルシステムエラーが発生する可能性が低いです。";

6 changes: 6 additions & 0 deletions Platform/macOS/VMConfigAppleDriveCreateView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
6 changes: 6 additions & 0 deletions Platform/macOS/VMConfigAppleDriveDetailsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions Platform/zh-HK.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -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 客戶機器。這個介面速度較慢,但較不容易遇到檔案系統錯誤。";
5 changes: 5 additions & 0 deletions Platform/zh-Hans.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -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 客户机。此接口速度较慢,但不太容易遇到文件系统错误。";
5 changes: 5 additions & 0 deletions Platform/zh-Hant.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -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 客戶機器。這個介面速度較慢,但較不容易遇到檔案系統錯誤。";

0 comments on commit 8e84a1e

Please sign in to comment.