shared: extract disk-spec parsing into machine-util

Move the ImageFormat / DiskType enums and their string tables out of
vmspawn's private settings header into a new src/shared/machine-util,
and add parse_disk_spec() — the colon-prefix loop that turns
"[FORMAT:][DISKTYPE:]PATH" into the two enums plus a normalized path.

No behavior change for vmspawn. A follow-up machinectl attach-disk
change accepts the same syntax and consumes the shared helper.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
This commit is contained in:
Christian Brauner
2026-04-24 14:39:24 +02:00
parent c697634ae2
commit 258c667845
6 changed files with 138 additions and 66 deletions
+102
View File
@@ -0,0 +1,102 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "alloc-util.h"
#include "extract-word.h"
#include "machine-util.h"
#include "parse-argument.h"
#include "string-table.h"
static const char *const image_format_table[_IMAGE_FORMAT_MAX] = {
[IMAGE_FORMAT_RAW] = "raw",
[IMAGE_FORMAT_QCOW2] = "qcow2",
};
DEFINE_STRING_TABLE_LOOKUP(image_format, ImageFormat);
static const char *const disk_type_table[_DISK_TYPE_MAX] = {
[DISK_TYPE_VIRTIO_BLK] = "virtio-blk",
[DISK_TYPE_VIRTIO_SCSI] = "virtio-scsi",
[DISK_TYPE_NVME] = "nvme",
[DISK_TYPE_VIRTIO_SCSI_CDROM] = "scsi-cd",
};
DEFINE_STRING_TABLE_LOOKUP(disk_type, DiskType);
/* Wire value for the io.systemd.VirtualMachineInstance.BlockDriver IDL enum. */
static const char *const block_driver_table[_DISK_TYPE_MAX] = {
[DISK_TYPE_VIRTIO_BLK] = "virtio_blk",
[DISK_TYPE_VIRTIO_SCSI] = "scsi_hd",
[DISK_TYPE_NVME] = "nvme",
[DISK_TYPE_VIRTIO_SCSI_CDROM] = "scsi_cd",
};
DEFINE_STRING_TABLE_LOOKUP(block_driver, DiskType);
/* QEMU -device driver name (e.g. "virtio-blk-pci"). */
static const char *const qemu_device_driver_table[_DISK_TYPE_MAX] = {
[DISK_TYPE_VIRTIO_BLK] = "virtio-blk-pci",
[DISK_TYPE_VIRTIO_SCSI] = "scsi-hd",
[DISK_TYPE_NVME] = "nvme",
[DISK_TYPE_VIRTIO_SCSI_CDROM] = "scsi-cd",
};
DEFINE_STRING_TABLE_LOOKUP(qemu_device_driver, DiskType);
int parse_disk_spec(
const char *arg,
ImageFormat *format,
DiskType *disk_type,
char **ret_path) {
int r;
assert(arg);
assert(format);
assert(disk_type);
assert(ret_path);
ImageFormat parsed_format = *format;
DiskType parsed_disk_type = *disk_type;
const char *dp = arg;
/* Format and disk-type vocabularies don't overlap, so prefixes may appear in any order. */
for (;;) {
_cleanup_free_ char *word = NULL;
const char *save = dp;
r = extract_first_word(&dp, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
if (r < 0)
return r;
if (r == 0 || !dp) {
/* No ':' remained after this word — rest is the path. */
dp = save;
break;
}
ImageFormat f = image_format_from_string(word);
if (f >= 0) {
parsed_format = f;
continue;
}
DiskType dt = disk_type_from_string(word);
if (dt >= 0) {
parsed_disk_type = dt;
continue;
}
/* Unknown prefix — rewind, remainder is the path. */
dp = save;
break;
}
_cleanup_free_ char *path = NULL;
r = parse_path_argument(dp, /* suppress_root= */ false, &path);
if (r < 0)
return r;
*format = parsed_format;
*disk_type = parsed_disk_type;
*ret_path = TAKE_PTR(path);
return 0;
}
+32
View File
@@ -0,0 +1,32 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "shared-forward.h"
typedef enum ImageFormat {
IMAGE_FORMAT_RAW,
IMAGE_FORMAT_QCOW2,
_IMAGE_FORMAT_MAX,
_IMAGE_FORMAT_INVALID = -EINVAL,
} ImageFormat;
typedef enum DiskType {
DISK_TYPE_VIRTIO_BLK,
DISK_TYPE_VIRTIO_SCSI,
DISK_TYPE_NVME,
DISK_TYPE_VIRTIO_SCSI_CDROM,
_DISK_TYPE_MAX,
_DISK_TYPE_INVALID = -EINVAL,
} DiskType;
DECLARE_STRING_TABLE_LOOKUP(image_format, ImageFormat);
DECLARE_STRING_TABLE_LOOKUP(disk_type, DiskType);
DECLARE_STRING_TABLE_LOOKUP(block_driver, DiskType);
DECLARE_STRING_TABLE_LOOKUP(qemu_device_driver, DiskType);
/* Parse "[FORMAT:][DISKTYPE:]PATH"; *format and *disk_type are in-out. */
int parse_disk_spec(
const char *arg,
ImageFormat *format,
DiskType *disk_type,
char **ret_path);
+1
View File
@@ -133,6 +133,7 @@ shared_sources = files(
'machine-credential.c',
'machine-id-setup.c',
'machine-register.c',
'machine-util.c',
'macvlan-util.c',
'main-func.c',
'metrics.c',
-16
View File
@@ -3,22 +3,6 @@
#include "string-table.h"
#include "vmspawn-settings.h"
static const char *const image_format_table[_IMAGE_FORMAT_MAX] = {
[IMAGE_FORMAT_RAW] = "raw",
[IMAGE_FORMAT_QCOW2] = "qcow2",
};
DEFINE_STRING_TABLE_LOOKUP(image_format, ImageFormat);
static const char *const disk_type_table[_DISK_TYPE_MAX] = {
[DISK_TYPE_VIRTIO_BLK] = "virtio-blk",
[DISK_TYPE_VIRTIO_SCSI] = "virtio-scsi",
[DISK_TYPE_NVME] = "nvme",
[DISK_TYPE_VIRTIO_SCSI_CDROM] = "scsi-cd",
};
DEFINE_STRING_TABLE_LOOKUP(disk_type, DiskType);
void extra_drive_context_done(ExtraDriveContext *ctx) {
assert(ctx);
+1 -18
View File
@@ -1,24 +1,9 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "machine-util.h"
#include "shared-forward.h"
typedef enum ImageFormat {
IMAGE_FORMAT_RAW,
IMAGE_FORMAT_QCOW2,
_IMAGE_FORMAT_MAX,
_IMAGE_FORMAT_INVALID = -EINVAL,
} ImageFormat;
typedef enum DiskType {
DISK_TYPE_VIRTIO_BLK,
DISK_TYPE_VIRTIO_SCSI,
DISK_TYPE_NVME,
DISK_TYPE_VIRTIO_SCSI_CDROM,
_DISK_TYPE_MAX,
_DISK_TYPE_INVALID = -EINVAL,
} DiskType;
typedef struct ExtraDrive {
char *path;
ImageFormat format;
@@ -69,6 +54,4 @@ typedef enum SettingsMask {
DECLARE_STRING_TABLE_LOOKUP(console_mode, ConsoleMode);
DECLARE_STRING_TABLE_LOOKUP(console_transport, ConsoleTransport);
DECLARE_STRING_TABLE_LOOKUP(disk_type, DiskType);
DECLARE_STRING_TABLE_LOOKUP(firmware, Firmware);
DECLARE_STRING_TABLE_LOOKUP(image_format, ImageFormat);
+2 -32
View File
@@ -752,39 +752,9 @@ static int parse_argv(int argc, char *argv[]) {
OPTION_LONG("extra-drive", "[FORMAT:][DISKTYPE:]PATH", "Adds an additional disk to the VM"): {
ImageFormat format = IMAGE_FORMAT_RAW;
DiskType extra_disk_type = _DISK_TYPE_INVALID;
const char *dp = arg;
/* Parse optional colon-separated prefixes. The format and disk type
* value sets don't overlap, so they can appear in any order. */
for (;;) {
const char *colon = strchr(dp, ':');
if (!colon)
break;
_cleanup_free_ char *prefix = strndup(dp, colon - dp);
if (!prefix)
return log_oom();
ImageFormat f = image_format_from_string(prefix);
if (f >= 0) {
format = f;
dp = colon + 1;
continue;
}
DiskType dt = disk_type_from_string(prefix);
if (dt >= 0) {
extra_disk_type = dt;
dp = colon + 1;
continue;
}
/* Not a recognized prefix, treat the rest as the path */
break;
}
_cleanup_free_ char *drive_path = NULL;
r = parse_path_argument(dp, /* suppress_root= */ false, &drive_path);
r = parse_disk_spec(arg, &format, &extra_disk_type, &drive_path);
if (r < 0)
return r;