]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
shared: extract disk-spec parsing into machine-util
authorChristian Brauner <brauner@kernel.org>
Mon, 20 Apr 2026 07:28:54 +0000 (09:28 +0200)
committerChristian Brauner <brauner@kernel.org>
Fri, 24 Apr 2026 12:39:24 +0000 (14:39 +0200)
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>
src/shared/machine-util.c [new file with mode: 0644]
src/shared/machine-util.h [new file with mode: 0644]
src/shared/meson.build
src/vmspawn/vmspawn-settings.c
src/vmspawn/vmspawn-settings.h
src/vmspawn/vmspawn.c

diff --git a/src/shared/machine-util.c b/src/shared/machine-util.c
new file mode 100644 (file)
index 0000000..fa5e46a
--- /dev/null
@@ -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;
+}
diff --git a/src/shared/machine-util.h b/src/shared/machine-util.h
new file mode 100644 (file)
index 0000000..3937ce1
--- /dev/null
@@ -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);
index 741dbb60a451a086b570eed67e74c314558d168d..56b823c50cd4f166c542ef7cfd18b6f47d65335c 100644 (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',
index 9382172e2ca805cdea39da4cd06b1e228bc12d43..502189e7bea630b6e4a78470c2d7ff8451a7f0ae 100644 (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);
 
index f02b499201ed87f5bf02d928d048154cffda6227..596a66cecddb8a8a055a1e9c49fa3326be96ad33 100644 (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);
index 01325bf9eb1e74033fe4b874fa66cdfa9fc244ed..1b696f70144803aca0f0810a82ddfc8ccd58f905 100644 (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;