vmspawn: add vmspawn-bind-volume glue
This is vmspawn's per-backend code for the StorageProvider integration.
Other backends (future systemd-nspawn, future service-manager
BindVolume=) consume the same shared parser and Acquire helper but
each provides its own attach/detach glue; this is vmspawn's.
- disk_type_from_bind_volume_config() turns the opaque BindVolume
'config' field (e.g. "scsi-cd") into a DiskType. Empty defaults to
virtio-blk to match the --bind-volume CLI grammar.
- vmspawn_bind_volume_acquire() takes a parsed BindVolume, calls
storage_acquire_volume() for the fd, and builds a DriveInfo ready
for vmspawn_qmp_setup_drives() (boot) or vmspawn_qmp_add_block_device()
(hotplug). Rejects directory-typed volumes (vmspawn block devices
need a regular file or a host block device).
- vmspawn_bind_volume_attach_fd() is the runtime path: takes a fd
that was already pushed across by an AddStorage caller plus the
name+config it specified, builds the DriveInfo with
QMP_DRIVE_REMOVABLE set and a varlink link, and dispatches to
vmspawn_qmp_add_block_device(). Reply is delivered asynchronously
by the existing on_add_device_add_complete() callback.
- vmspawn_bind_volume_prepare_boot() is a thin loop the boot-time
path uses to populate DriveInfos.
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
This commit is contained in:
@@ -6,6 +6,7 @@ endif
|
||||
|
||||
vmspawn_sources = files(
|
||||
'vmspawn.c',
|
||||
'vmspawn-bind-volume.c',
|
||||
'vmspawn-qemu-config.c',
|
||||
'vmspawn-qmp.c',
|
||||
'vmspawn-varlink.c',
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "sd-varlink.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "log.h"
|
||||
#include "runtime-scope.h"
|
||||
#include "stat-util.h"
|
||||
#include "storage-util.h"
|
||||
#include "string-util.h"
|
||||
#include "vmspawn-bind-volume.h"
|
||||
#include "vmspawn-qmp.h"
|
||||
|
||||
DiskType disk_type_from_bind_volume_config(const char *config) {
|
||||
if (isempty(config))
|
||||
return DISK_TYPE_VIRTIO_BLK;
|
||||
return disk_type_from_string(config);
|
||||
}
|
||||
|
||||
int vmspawn_bind_volume_acquire(
|
||||
RuntimeScope scope,
|
||||
const BindVolume *v,
|
||||
bool removable,
|
||||
sd_varlink *link,
|
||||
DriveInfo **ret,
|
||||
char **reterr_error_id) {
|
||||
|
||||
_cleanup_(storage_acquire_reply_done) StorageAcquireReply reply = STORAGE_ACQUIRE_REPLY_INIT;
|
||||
_cleanup_(drive_info_unrefp) DriveInfo *d = NULL;
|
||||
_cleanup_free_ char *err = NULL;
|
||||
int r;
|
||||
|
||||
assert(v);
|
||||
assert(ret);
|
||||
|
||||
DiskType dt = disk_type_from_bind_volume_config(v->config);
|
||||
if (dt < 0) {
|
||||
r = dt;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
r = storage_acquire_volume(scope, v, /* allow_interactive_auth= */ false, &err, &reply);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
if (reply.type == VOLUME_DIR) {
|
||||
r = log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||
"Directory volumes are not supported for vmspawn block devices.");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (fstat(reply.fd, &st) < 0) {
|
||||
r = -errno;
|
||||
goto fail;
|
||||
}
|
||||
r = stat_verify_regular_or_block(&st);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
d = drive_info_new();
|
||||
if (!d) {
|
||||
r = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
d->id = strjoin(v->provider, ":", v->volume);
|
||||
d->disk_driver = strdup(ASSERT_PTR(qemu_device_driver_to_string(dt)));
|
||||
d->format = strdup("raw");
|
||||
d->path = strdup(v->volume);
|
||||
if (!d->id || !d->disk_driver || !d->format || !d->path) {
|
||||
r = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
d->disk_type = dt;
|
||||
d->fd = TAKE_FD(reply.fd);
|
||||
|
||||
if (reply.type == VOLUME_BLK || S_ISBLK(st.st_mode))
|
||||
d->flags |= QMP_DRIVE_BLOCK_DEVICE;
|
||||
if (reply.read_only > 0 || dt == DISK_TYPE_VIRTIO_SCSI_CDROM)
|
||||
d->flags |= QMP_DRIVE_READ_ONLY;
|
||||
if (removable)
|
||||
d->flags |= QMP_DRIVE_REMOVABLE;
|
||||
d->link = sd_varlink_ref(link);
|
||||
|
||||
*ret = TAKE_PTR(d);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if (reterr_error_id)
|
||||
*reterr_error_id = TAKE_PTR(err);
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Takes ownership of fd unconditionally — it is closed on every error path too. */
|
||||
int vmspawn_bind_volume_attach_fd(
|
||||
VmspawnQmpBridge *bridge,
|
||||
sd_varlink *link,
|
||||
int fd,
|
||||
const char *name,
|
||||
const char *config) {
|
||||
|
||||
_cleanup_close_ int owned_fd = fd;
|
||||
int r;
|
||||
|
||||
assert(bridge);
|
||||
assert(link);
|
||||
assert(fd >= 0);
|
||||
assert(name);
|
||||
|
||||
DiskType dt = disk_type_from_bind_volume_config(config);
|
||||
if (dt < 0)
|
||||
return dt;
|
||||
|
||||
struct stat st;
|
||||
if (fstat(owned_fd, &st) < 0)
|
||||
return -errno;
|
||||
r = stat_verify_regular_or_block(&st);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
_cleanup_(drive_info_unrefp) DriveInfo *d = drive_info_new();
|
||||
if (!d)
|
||||
return -ENOMEM;
|
||||
|
||||
d->id = strdup(name);
|
||||
d->disk_driver = strdup(ASSERT_PTR(qemu_device_driver_to_string(dt)));
|
||||
d->format = strdup("raw");
|
||||
d->path = strdup(name);
|
||||
if (!d->id || !d->disk_driver || !d->format || !d->path)
|
||||
return -ENOMEM;
|
||||
|
||||
int oflags = fcntl(owned_fd, F_GETFL);
|
||||
if (oflags < 0)
|
||||
return -errno;
|
||||
|
||||
d->disk_type = dt;
|
||||
d->fd = TAKE_FD(owned_fd);
|
||||
if (S_ISBLK(st.st_mode))
|
||||
d->flags |= QMP_DRIVE_BLOCK_DEVICE;
|
||||
if (dt == DISK_TYPE_VIRTIO_SCSI_CDROM || (oflags & O_ACCMODE_STRICT) == O_RDONLY)
|
||||
d->flags |= QMP_DRIVE_READ_ONLY;
|
||||
d->flags |= QMP_DRIVE_REMOVABLE;
|
||||
d->link = sd_varlink_ref(link);
|
||||
|
||||
return vmspawn_qmp_add_block_device(bridge, TAKE_PTR(d));
|
||||
}
|
||||
|
||||
void bind_volumes_done(BindVolumes *bv) {
|
||||
assert(bv);
|
||||
FOREACH_ARRAY(v, bv->items, bv->n_items)
|
||||
bind_volume_free(*v);
|
||||
bv->items = mfree(bv->items);
|
||||
bv->n_items = 0;
|
||||
}
|
||||
|
||||
int vmspawn_bind_volume_prepare_boot(
|
||||
RuntimeScope scope,
|
||||
const BindVolumes *bv,
|
||||
DriveInfos *drives) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(bv);
|
||||
assert(drives);
|
||||
|
||||
if (bv->n_items == 0)
|
||||
return 0;
|
||||
|
||||
if (!GREEDY_REALLOC(drives->drives, drives->n_drives + bv->n_items))
|
||||
return log_oom();
|
||||
|
||||
FOREACH_ARRAY(it, bv->items, bv->n_items) {
|
||||
BindVolume *v = *it;
|
||||
_cleanup_(drive_info_unrefp) DriveInfo *d = NULL;
|
||||
_cleanup_free_ char *error_id = NULL;
|
||||
|
||||
r = vmspawn_bind_volume_acquire(
|
||||
scope, v,
|
||||
/* removable= */ false,
|
||||
/* link= */ NULL,
|
||||
&d, &error_id);
|
||||
if (r < 0) {
|
||||
if (error_id)
|
||||
return log_error_errno(r,
|
||||
"Failed to acquire storage volume '%s:%s' (%s): %m",
|
||||
v->provider, v->volume, error_id);
|
||||
return log_error_errno(r,
|
||||
"Failed to acquire storage volume '%s:%s': %m",
|
||||
v->provider, v->volume);
|
||||
}
|
||||
|
||||
drives->drives[drives->n_drives++] = TAKE_PTR(d);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "machine-util.h"
|
||||
#include "shared-forward.h"
|
||||
#include "vmspawn-qmp.h"
|
||||
|
||||
/* Empty/NULL defaults to virtio-blk; otherwise delegates to disk_type_from_string(). */
|
||||
DiskType disk_type_from_bind_volume_config(const char *config);
|
||||
|
||||
/* Acquires the volume and builds a DriveInfo with id="<provider>:<volume>" (the
|
||||
* bridge-visible name; QMP-side names are still allocated by add_block_device). */
|
||||
int vmspawn_bind_volume_acquire(
|
||||
RuntimeScope scope,
|
||||
const BindVolume *v,
|
||||
bool removable,
|
||||
sd_varlink *link,
|
||||
DriveInfo **ret,
|
||||
char **reterr_error_id);
|
||||
|
||||
typedef struct BindVolumes {
|
||||
BindVolume **items;
|
||||
size_t n_items;
|
||||
} BindVolumes;
|
||||
|
||||
void bind_volumes_done(BindVolumes *bv);
|
||||
|
||||
int vmspawn_bind_volume_prepare_boot(
|
||||
RuntimeScope scope,
|
||||
const BindVolumes *bv,
|
||||
DriveInfos *drives);
|
||||
|
||||
/* Takes ownership of fd unconditionally. */
|
||||
int vmspawn_bind_volume_attach_fd(
|
||||
VmspawnQmpBridge *bridge,
|
||||
sd_varlink *link,
|
||||
int fd,
|
||||
const char *name,
|
||||
const char *config);
|
||||
Reference in New Issue
Block a user