From: Christian Brauner Date: Fri, 1 May 2026 11:35:44 +0000 (+0200) Subject: vmspawn: add --bind-volume= command line option X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=413fd62dd79c1983d4c2ec93570fb85f8167d242;p=thirdparty%2Fsystemd.git vmspawn: add --bind-volume= command line option systemd-vmspawn --bind-volume=PROVIDER:VOLUME[:CONFIG][:K=V,...] For each --bind-volume passed at startup, vmspawn calls Acquire() on the named StorageProvider and attaches the resulting fd to the VM as an additional drive. The drive is identified by the user-visible name ':' on the bridge — that is also the handle used later when machinectl unbind-volume detaches drives at runtime (though boot-time drives like these are NOT removable; that is the StorageImmutable behaviour added earlier). The colon grammar is parsed by the shared bind_volume_parse() helper. The 3rd 'config' field selects the guest device type from the disk_type_table[] vocabulary (virtio-blk, virtio-scsi, nvme, scsi-cd); empty defaults to virtio-blk per the TASK grammar. Wiring lives next to the existing --extra-drive setup: parse_argv() appends a parsed BindVolume to arg_bind_volumes, and prepare_device_info() hands the array to vmspawn_bind_volume_prepare_boot() which Acquires each volume and pushes a DriveInfo onto the existing drives array. PCIe port assignment (assign_pcie_ports()) and the QMP setup loop pick them up automatically. Signed-off-by: Christian Brauner (Amutable) --- diff --git a/shell-completion/bash/systemd-vmspawn b/shell-completion/bash/systemd-vmspawn index efa0dae58de..62fa5ab5206 100644 --- a/shell-completion/bash/systemd-vmspawn +++ b/shell-completion/bash/systemd-vmspawn @@ -38,7 +38,7 @@ _systemd_vmspawn() { [BIND]='--bind --bind-ro' [SSH_KEY]='--ssh-key' [CONSOLE]='--console' - [ARG]='--cpus --ram --vsock-cid -M --machine --uuid --private-users --background --set-credential --load-credential --forward-journal-max-use --forward-journal-keep-free --forward-journal-max-file-size --forward-journal-max-files' + [ARG]='--cpus --ram --vsock-cid -M --machine --uuid --private-users --background --set-credential --load-credential --forward-journal-max-use --forward-journal-keep-free --forward-journal-max-file-size --forward-journal-max-files --bind-volume' [IMAGE_FORMAT]='--image-format' [IMAGE_DISK_TYPE]='--image-disk-type' ) diff --git a/src/vmspawn/vmspawn.c b/src/vmspawn/vmspawn.c index 81c035c250d..ee8ae518f03 100644 --- a/src/vmspawn/vmspawn.c +++ b/src/vmspawn/vmspawn.c @@ -88,6 +88,7 @@ #include "user-record.h" #include "user-util.h" #include "utf8.h" +#include "vmspawn-bind-volume.h" #include "vmspawn-mount.h" #include "vmspawn-qemu-config.h" #include "vmspawn-qmp.h" @@ -163,6 +164,7 @@ static bool arg_keep_unit = false; static sd_id128_t arg_uuid = {}; static char **arg_kernel_cmdline_extra = NULL; static ExtraDriveContext arg_extra_drives = {}; +static BindVolumes arg_bind_volumes = {}; static char *arg_background = NULL; static bool arg_pass_ssh_key = true; static char *arg_ssh_key_type = NULL; @@ -200,6 +202,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_runtime_mounts, runtime_mount_context_done); STATIC_DESTRUCTOR_REGISTER(arg_forward_journal, freep); STATIC_DESTRUCTOR_REGISTER(arg_kernel_cmdline_extra, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_extra_drives, extra_drive_context_done); +STATIC_DESTRUCTOR_REGISTER(arg_bind_volumes, bind_volumes_done); STATIC_DESTRUCTOR_REGISTER(arg_background, freep); STATIC_DESTRUCTOR_REGISTER(arg_ssh_key_type, freep); STATIC_DESTRUCTOR_REGISTER(arg_smbios11, strv_freep); @@ -766,6 +769,30 @@ static int parse_argv(int argc, char *argv[]) { break; } + OPTION_LONG("bind-volume", "PROVIDER:VOLUME[:CONFIG][:KEY=VALUE,...]", + "Acquire a storage volume from a StorageProvider and attach it to the VM"): { + _cleanup_(bind_volume_freep) BindVolume *bv = NULL; + + r = bind_volume_parse(opts.arg, &bv); + if (r < 0) + return log_error_errno(r, "Failed to parse --bind-volume= argument '%s': %m", opts.arg); + + if (disk_type_from_bind_volume_config(bv->config) < 0) { + _cleanup_free_ char *valid = NULL; + for (DiskType t = 0; t < _DISK_TYPE_MAX; t++) + if (!strextend_with_separator(&valid, ", ", disk_type_to_string(t))) + return log_oom(); + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Unknown device type '%s' for --bind-volume=. Valid values: %s.", + bv->config, valid); + } + + if (!GREEDY_REALLOC(arg_bind_volumes.items, arg_bind_volumes.n_items + 1)) + return log_oom(); + arg_bind_volumes.items[arg_bind_volumes.n_items++] = TAKE_PTR(bv); + break; + } + OPTION_LONG("bind-user", "NAME", "Bind user from host to virtual machine"): if (!valid_user_group_name(opts.arg, /* flags= */ 0)) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid user name to bind: %s", opts.arg); @@ -2475,7 +2502,7 @@ static int prepare_device_info(const char *runtime_dir, MachineConfig *c) { /* Build drive info for QMP-based setup. vmspawn opens all image files and * passes fds to QEMU via add-fd — QEMU never needs filesystem access. */ - drives->drives = new0(DriveInfo*, 1 + arg_extra_drives.n_drives); + drives->drives = new0(DriveInfo*, 1 + arg_extra_drives.n_drives + arg_bind_volumes.n_items); if (!drives->drives) return log_oom(); @@ -2487,6 +2514,10 @@ static int prepare_device_info(const char *runtime_dir, MachineConfig *c) { if (r < 0) return r; + r = vmspawn_bind_volume_prepare_boot(arg_runtime_scope, &arg_bind_volumes, drives); + if (r < 0) + return r; + return assign_pcie_ports(c); }