Repository: utmapp/UTM
File: Configuration/UTMAppleConfigurationDrive.swift (Swift)

Code before:
            isExternal = true
        } else {
            imageURL = nil
            imageName = nil
            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)
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        if !isExternal {
            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(useFsWorkAround: Bool = false) throws -> VZDiskImageStorageDeviceAttachment? {
        if let imageURL = imageURL {
            // 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: .fsync)
            } else {
                return try VZDiskImageStorageDeviceAttachment(url: imageURL, readOnly: isReadOnly)
            }
        } else {
            return nil
        }
    }
    
    func hash(into hasher: inout Hasher) {
        imageName?.hash(into: &hasher)
        sizeMib.hash(into: &hasher)
        isReadOnly.hash(into: &hasher)
        isNvme.hash(into: &hasher)
        isExternal.hash(into: &hasher)
        id.hash(into: &hasher)
    }
    
    func clone() -> UTMAppleConfigurationDrive {
        var cloned = self
        cloned.id = UUID().uuidString
        return cloned
    }
}

// MARK: - Conversion of old config format

Review comment (suggestion):
In my case, `synchronizationMode` defaults to `.full` when `cachingMode` and `synchronizationMode` aren't supplied.
```
print(mainDiskAttachment.synchronizationMode.rawValue) // -> 1 == .full
// https://developer.apple.com/documentation/virtualization/vzdiskimagesynchronizationmode/full
```
Does it default to `.fsync` for you? I didn't notice any differences in terms of corruption for any synchronization modes, so I'd say we should stick to framework default.

Code after:
            isExternal = true
        } else {
            imageURL = nil
            imageName = nil
            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)
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        if !isExternal {
            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(useFsWorkAround: Bool = false) throws -> VZDiskImageStorageDeviceAttachment? {
        if let imageURL = imageURL {
            // 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
        }
    }
    
    func hash(into hasher: inout Hasher) {
        imageName?.hash(into: &hasher)
        sizeMib.hash(into: &hasher)
        isReadOnly.hash(into: &hasher)
        isNvme.hash(into: &hasher)
        isExternal.hash(into: &hasher)
        id.hash(into: &hasher)
    }
    
    func clone() -> UTMAppleConfigurationDrive {
        var cloned = self
        cloned.id = UUID().uuidString
        return cloned
    }
}

// MARK: - Conversion of old config format