Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tart run: do not require root to mount a block device #798

Merged
merged 1 commit into from
Apr 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 19 additions & 9 deletions Sources/tart/Commands/Run.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Dispatch
import SwiftUI
import Virtualization
import Sentry
import System

var vm: VM?

Expand Down Expand Up @@ -68,8 +69,9 @@ struct Run: AsyncParsableCommand {
Learn how to create a disk image using Disk Utility here:
https://support.apple.com/en-gb/guide/disk-utility/dskutl11888/mac

To work with block devices 'tart' binary must be executed as root which affects locating Tart VMs.
To workaround this issue pass TART_HOME explicitly:
To work with block devices, the easiest way is to modify their permissions (e.g. by using "sudo chown $USER /dev/diskX") or to run the Tart binary as root, which affects locating Tart VMs.

To work around this pass TART_HOME explicitly:

sudo TART_HOME="$HOME/.tart" tart run sonoma --disk=/dev/disk0
""", valueName: "path[:ro]"))
Expand Down Expand Up @@ -451,18 +453,26 @@ struct Run: AsyncParsableCommand {

// check if `diskPath` is a block device or a directory
if pathHasMode(diskPath, mode: S_IFBLK) || pathHasMode(diskPath, mode: S_IFDIR) {
print("Using block device\n")
guard #available(macOS 14, *) else {
throw UnsupportedOSError("attaching block devices", "are")
}
let fileHandle = FileHandle(forUpdatingAtPath: diskPath)
guard fileHandle != nil else {
if ProcessInfo.processInfo.userName != "root" {
throw RuntimeError.VMConfigurationError("need to run as root to work with block devices")

let fd = open(diskPath, diskReadOnly ? O_RDONLY : O_RDWR)
if fd == -1 {
let details = Errno(rawValue: CInt(errno))

switch details.rawValue {
case EBUSY:
throw RuntimeError.FailedToOpenBlockDevice(diskURL.url.path, "already in use, try umounting it via \"diskutil unmountDisk\" (when the whole disk) or \"diskutil umount\" (when mounting a single partition)")
case EACCES:
throw RuntimeError.FailedToOpenBlockDevice(diskURL.url.path, "permission denied, consider changing the disk's owner using \"sudo chown $USER \(diskURL.url.path)\" or run Tart as a superuser (see --disk help for more details on how to do that correctly)")
default:
throw RuntimeError.FailedToOpenBlockDevice(diskURL.url.path, "\(details)")
}
throw RuntimeError.VMConfigurationError("block device \(diskURL.url.path) seems to be already in use, unmount it first via 'diskutil unmount'")
}
let attachment = try VZDiskBlockDeviceStorageDeviceAttachment(fileHandle: fileHandle!, readOnly: diskReadOnly, synchronizationMode: .full)

let attachment = try VZDiskBlockDeviceStorageDeviceAttachment(fileHandle: FileHandle(fileDescriptor: fd, closeOnDealloc: true),
readOnly: diskReadOnly, synchronizationMode: .full)
result.append(VZVirtioBlockDeviceConfiguration(attachment: attachment))
} else {
// Error out if the disk is locked by the host (e.g. it was mounted in Finder),
Expand Down
3 changes: 3 additions & 0 deletions Sources/tart/VMStorageHelper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ enum RuntimeError : Error {
case VMAlreadyRunning(_ message: String)
case NoIPAddressFound(_ message: String)
case DiskAlreadyInUse(_ message: String)
case FailedToOpenBlockDevice(_ path: String, _ explanation: String)
case InvalidDiskSize(_ message: String)
case FailedToUpdateAccessDate(_ message: String)
case PIDLockFailed(_ message: String)
Expand Down Expand Up @@ -93,6 +94,8 @@ extension RuntimeError : CustomStringConvertible {
return message
case .DiskAlreadyInUse(let message):
return message
case .FailedToOpenBlockDevice(let path, let explanation):
return "failed to open block device \(path): \(explanation)"
case .InvalidDiskSize(let message):
return message
case .FailedToUpdateAccessDate(let message):
Expand Down
Loading