break
if (
- state.config.output_format in (OutputFormat.cpio, OutputFormat.uki) and
+ state.config.output_format in (OutputFormat.cpio, OutputFormat.uki, OutputFormat.directory) and
state.config.bootable == ConfigFeature.auto
):
return
last = presets[-1]
- if args.verb == Verb.qemu and last.output_format in (
- OutputFormat.directory,
- OutputFormat.tar,
- ):
- die(f"{last.output_format} images cannot be booted in qemu.")
-
if args.verb in (Verb.shell, Verb.boot):
opname = "acquire shell in" if args.verb == Verb.shell else "boot"
if last.output_format in (OutputFormat.tar, OutputFormat.cpio):
for config in presets:
try_import(f"mkosi.distributions.{config.distribution}")
- invoked_as_root = os.getuid() == 0
name = InvokingUser.name()
# Get the user UID/GID either on the host or in the user namespace running the build
# right after (and we're in a mount namespace so the /usr mount disappears when we exit)
with mount_usr(last.tools_tree, umount=False), mount_passwd(name, uid, gid, umount=False):
- # After mounting the last tools tree, if we're not going to execute systemd-nspawn, we don't need to
- # be (fake) root anymore, so switch user to the invoking user. If we're going to invoke qemu and
- # mkosi was executed as root, we also don't drop privileges as depending on the environment and
- # options passed, running qemu might need root privileges as well.
- if not args.verb.needs_root() and (args.verb != Verb.qemu or not invoked_as_root):
+ # After mounting the last tools tree, if we're not going to execute systemd-nspawn or qemu, we don't need to
+ # be (fake) root anymore, so switch user to the invoking user.
+ if not args.verb.needs_root() and args.verb != Verb.qemu:
os.setresgid(gid, gid, gid)
os.setresuid(uid, uid, uid)
run_shell(args, last)
if args.verb == Verb.qemu:
- run_qemu(args, last)
+ run_qemu(args, last, uid, gid)
if args.verb == Verb.ssh:
run_ssh(args, last)
import uuid
from collections.abc import Iterator
from pathlib import Path
+from typing import Optional
from mkosi.architecture import Architecture
from mkosi.config import (
from mkosi.run import MkosiAsyncioThread, run, spawn
from mkosi.tree import copy_tree, rmtree
from mkosi.types import PathString
-from mkosi.util import (
- InvokingUser,
- format_bytes,
- qemu_check_kvm_support,
- qemu_check_vsock_support,
-)
+from mkosi.util import format_bytes, qemu_check_kvm_support, qemu_check_vsock_support
def machine_cid(config: MkosiConfig) -> int:
@contextlib.contextmanager
-def start_virtiofsd(directory: Path) -> Iterator[Path]:
- uid, gid = InvokingUser.uid_gid()
-
+def start_virtiofsd(directory: Path, uid: Optional[int] = None, gid: Optional[int] = None) -> Iterator[Path]:
with tempfile.TemporaryDirectory() as state:
# Make sure virtiofsd is allowed to create its socket in this temporary directory.
- os.chown(state, uid, gid)
+ os.chown(state, uid if uid is not None else os.getuid(), gid if gid is not None else os.getgid())
# Make sure we can use the socket name as a unique identifier for the fs as well but make sure it's not too
# long as virtiofs tag names are limited to 36 bytes.
else:
die("virtiofsd must be installed to use RuntimeMounts= with mkosi qemu")
- # virtiofsd has to run unprivileged to use the --uid-map and --gid-map options, so we always run it as the user
- # running mkosi.
- proc = spawn([
+ cmdline: list[PathString] = [
virtiofsd,
"--socket-path", sock,
"--shared-dir", directory,
"--xattr",
"--posix-acl",
- # Map the user running mkosi to root in the virtual machine for the virtiofs instance to make sure all
- # files created by root in the VM are owned by the user running mkosi on the host.
- "--uid-map", f":0:{uid}:1:",
- "--gid-map", f":0:{gid}:1:",
- ], user=uid, group=gid)
+ ]
+
+ # Map the given user/group to root in the virtual machine for the virtiofs instance to make sure all files
+ # created by root in the VM are owned by the user running mkosi on the host.
+ if uid is not None:
+ cmdline += ["--uid-map", f":0:{uid}:1:"]
+ if gid is not None:
+ cmdline += ["--gid-map", f":0:{gid}:1:"]
+
+ # virtiofsd has to run unprivileged to use the --uid-map and --gid-map options, so run it as the given
+ # user/group if those are provided.
+ proc = spawn(cmdline, user=uid, group=gid)
try:
yield sock
rmtree(tmp)
-def run_qemu(args: MkosiArgs, config: MkosiConfig) -> None:
- if config.output_format not in (OutputFormat.disk, OutputFormat.cpio, OutputFormat.uki):
+def run_qemu(args: MkosiArgs, config: MkosiConfig, uid: int, gid: int) -> None:
+ if config.output_format not in (OutputFormat.disk, OutputFormat.cpio, OutputFormat.uki, OutputFormat.directory):
die(f"{config.output_format} images cannot be booted in qemu")
if (
accel = "kvm"
if config.qemu_firmware == QemuFirmware.auto:
- if config.output_format == OutputFormat.cpio or config.architecture.to_efi() is None:
+ if config.output_format in (OutputFormat.cpio, OutputFormat.directory) or config.architecture.to_efi() is None:
firmware = QemuFirmware.linux
else:
firmware = QemuFirmware.uefi
with contextlib.ExitStack() as stack:
for src, target in config.runtime_trees:
- sock = stack.enter_context(start_virtiofsd(src))
+ sock = stack.enter_context(start_virtiofsd(src, uid, gid))
cmdline += [
"-chardev", f"socket,id={sock.name},path={sock}",
"-device", f"vhost-user-fs-pci,queue-size=1024,chardev={sock.name},tag={sock.name}",
"--offline=yes",
fname])
- if firmware == QemuFirmware.linux or config.output_format in (OutputFormat.cpio, OutputFormat.uki):
+ if firmware == QemuFirmware.linux or config.output_format in (OutputFormat.cpio, OutputFormat.uki, OutputFormat.directory):
if config.output_format == OutputFormat.uki:
kernel = fname if firmware == QemuFirmware.uefi else config.output_dir / config.output_split_kernel
elif config.qemu_kernel:
root = finalize_root(find_partitions(fname))
if not root:
die("Cannot perform a direct kernel boot without a root or usr partition")
+ elif config.output_format == OutputFormat.directory:
+ # This virtiofsd has to run as root so that it can write files owned by any uid:gid created by the VM.
+ sock = stack.enter_context(start_virtiofsd(fname))
+ cmdline += [
+ "-chardev", f"socket,id={sock.name},path={sock}",
+ "-device", f"vhost-user-fs-pci,queue-size=1024,chardev={sock.name},tag=/dev/root",
+ ]
+ root = "root=/dev/root rootfstype=virtiofs rw"
else:
root = ""