]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
udev: add dissect_image builtin
authorLennart Poettering <lennart@poettering.net>
Wed, 5 Mar 2025 16:42:33 +0000 (17:42 +0100)
committerLennart Poettering <lennart@poettering.net>
Thu, 3 Apr 2025 09:08:57 +0000 (11:08 +0200)
Fixes: #33453 #27897 #18035
rules.d/90-image-dissect.rules
src/shared/dissect-image.c
src/shared/dissect-image.h
src/udev/meson.build
src/udev/udev-builtin-dissect_image.c [new file with mode: 0644]
src/udev/udev-builtin.c
src/udev/udev-builtin.h
src/udev/udev-def.h

index e606d266b48a8d595ecab59df536fead023b0b10..9bb097a7fe3cb1eb5316c046cd05ce0913d8a107 100644 (file)
@@ -30,4 +30,27 @@ LABEL="gpt_auto_root_end"
 ENV{DM_UUID}=="CRYPT-*", ENV{DM_NAME}=="root", IMPORT{builtin}="factory_reset status", SYMLINK+="gpt-auto-root"
 ENV{DM_UUID}=="CRYPT-*", ENV{DM_NAME}=="root", ENV{ID_FACTORY_RESET}=="on|complete", SYMLINK+="gpt-auto-root-ignore-factory-reset"
 
+# If this is the whole disk that we booted from, then dissect it
+ENV{DEVTYPE}=="disk", ENV{ID_PART_GPT_AUTO_ROOT_DISK}=="1", IMPORT{builtin}="dissect_image probe"
+ENV{DEVTYPE}=="disk", ENV{ID_PART_GPT_AUTO_ROOT_DISK}=="1", ENV{ID_FACTORY_RESET}=="", IMPORT{builtin}="factory_reset status"
+
+# If this is a partition, and we found something on the parent, then copy the
+# right properties from the parent, and rename them
+ENV{DEVTYPE}=="partition", ENV{ID_DISSECT_IMAGE}!="", IMPORT{builtin}="dissect_image copy"
+
+# Create symlinks based on the designator for the partitions themselves. If we detect LUKS or Verity, suffix them with "-luks" or "-vdata"
+ENV{DEVTYPE}!="partition", GOTO="dissect_partition_symlinks_end"
+    ENV{ID_DISSECT_PART_DESIGNATOR}=="", GOTO="dissect_partition_symlinks_end"
+        ENV{ID_FS_TYPE}!="crypto_LUKS", ENV{ID_DISSECT_PART_HAS_VERITY}!="1", ENV{ID_FACTORY_RESET}!="on", SYMLINK+="disk/by-designator/$env{ID_DISSECT_PART_DESIGNATOR}"
+        ENV{ID_FS_TYPE}!="crypto_LUKS", ENV{ID_DISSECT_PART_HAS_VERITY}!="1", ENV{ID_FACTORY_RESET}=="on|complete", SYMLINK+="disk/by-designator/$env{ID_DISSECT_PART_DESIGNATOR}-ignore-factory-reset"
+        ENV{ID_FS_TYPE}=="crypto_LUKS", ENV{ID_FACTORY_RESET}!="on", SYMLINK+="disk/by-designator/$env{ID_DISSECT_PART_DESIGNATOR}-luks"
+        ENV{ID_FS_TYPE}=="crypto_LUKS", ENV{ID_FACTORY_RESET}=="on|complete", SYMLINK+="disk/by-designator/$env{ID_DISSECT_PART_DESIGNATOR}-luks-ignore-factory-reset"
+        ENV{ID_FS_TYPE}!="crypto_LUKS", ENV{ID_DISSECT_PART_HAS_VERITY}=="1", ENV{ID_FACTORY_RESET}!="on", SYMLINK+="disk/by-designator/$env{ID_DISSECT_PART_DESIGNATOR}-verity-data"
+        ENV{ID_FS_TYPE}!="crypto_LUKS", ENV{ID_DISSECT_PART_HAS_VERITY}=="1", ENV{ID_FACTORY_RESET}=="on|complete", SYMLINK+="disk/by-designator/$env{ID_DISSECT_PART_DESIGNATOR}-verity-data-ignore-factory-reset"
+LABEL="dissect_partition_symlinks_end"
+
+# For LUKS or Verity partitions we rely on the selected volume name
+ENV{DM_UUID}=="CRYPT-*", ENV{DM_NAME}=="root|usr|home|srv|swap|tmp|var", IMPORT{builtin}="factory_reset status", SYMLINK+="disk/by-designator/$env{DM_NAME}"
+ENV{DM_UUID}=="CRYPT-*", ENV{DM_NAME}=="root|usr|home|srv|swap|tmp|var", ENV{ID_FACTORY_RESET}=="on|complete", SYMLINK+="disk/by-designator/$env{DM_NAME}-ignore-factory-reset"
+
 LABEL="image_dissect_end"
index 8c8592cc77f56c26f65a0bfe8923f8fdc57f5fcc..76db2834db3e115322fcf5b03b07e24086d75ddb 100644 (file)
@@ -3384,6 +3384,47 @@ int verity_settings_load(
         return 1;
 }
 
+int verity_settings_copy(VeritySettings *dest, const VeritySettings *source) {
+        assert(dest);
+
+        if (!source) {
+                *dest = VERITY_SETTINGS_DEFAULT;
+                return 0;
+        }
+
+        _cleanup_free_ void *rh = NULL;
+        if (source->root_hash_size > 0) {
+                rh = memdup(source->root_hash, source->root_hash_size);
+                if (!rh)
+                        return log_oom_debug();
+        }
+
+        _cleanup_free_ void *sig = NULL;
+        if (source->root_hash_sig_size > 0) {
+                sig = memdup(source->root_hash_sig, source->root_hash_sig_size);
+                if (!sig)
+                        return log_oom_debug();
+        }
+
+        _cleanup_free_ char *p = NULL;
+        if (source->data_path) {
+                p = strdup(source->data_path);
+                if (!p)
+                        return log_oom_debug();
+        }
+
+        *dest = (VeritySettings) {
+                .root_hash = TAKE_PTR(rh),
+                .root_hash_size = source->root_hash_size,
+                .root_hash_sig = TAKE_PTR(sig),
+                .root_hash_sig_size = source->root_hash_sig_size,
+                .data_path = TAKE_PTR(p),
+                .designator = source->designator,
+        };
+
+        return 1;
+}
+
 int dissected_image_load_verity_sig_partition(
                 DissectedImage *m,
                 int fd,
index 9de93039da6b3bd61b40c11115d1941fbf23ff54..dbdd13b5aee257d0fc66f51af4d91e611077f23e 100644 (file)
@@ -144,7 +144,7 @@ struct VeritySettings {
         PartitionDesignator designator;
 };
 
-#define VERITY_SETTINGS_DEFAULT {                               \
+#define VERITY_SETTINGS_DEFAULT (VeritySettings) {              \
                 .designator = _PARTITION_DESIGNATOR_INVALID     \
         }
 
@@ -226,6 +226,8 @@ static inline bool verity_settings_data_covers(const VeritySettings *verity, Par
                 verity->data_path;
 }
 
+int verity_settings_copy(VeritySettings *dest, const VeritySettings *source);
+
 int dissected_image_load_verity_sig_partition(DissectedImage *m, int fd, VeritySettings *verity);
 
 bool dissected_image_verity_candidate(const DissectedImage *image, PartitionDesignator d);
index 00bc581a7dcbb37b4ccdffa4e752d1ea6eda003c..9b2615173a81e352647d083bb90442f8fc4c2e5f 100644 (file)
@@ -22,6 +22,7 @@ libudevd_core_sources = files(
         'net/link-config.c',
         'udev-builtin.c',
         'udev-builtin-btrfs.c',
+        'udev-builtin-dissect_image.c',
         'udev-builtin-factory_reset.c',
         'udev-builtin-hwdb.c',
         'udev-builtin-input_id.c',
diff --git a/src/udev/udev-builtin-dissect_image.c b/src/udev/udev-builtin-dissect_image.c
new file mode 100644 (file)
index 0000000..9ba58cd
--- /dev/null
@@ -0,0 +1,379 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "blockdev-util.h"
+#include "device-util.h"
+#include "dissect-image.h"
+#include "fd-util.h"
+#include "hexdecoct.h"
+#include "image-policy.h"
+#include "initrd-util.h"
+#include "loop-util.h"
+#include "proc-cmdline.h"
+#include "udev-builtin.h"
+
+static ImagePolicy *arg_image_policy = NULL;
+static VeritySettings arg_verity_settings = VERITY_SETTINGS_DEFAULT;
+
+static int acquire_image_policy(ImagePolicy **ret) {
+        int r;
+
+        assert(ret);
+
+        _cleanup_free_ char *value = NULL;
+        r = proc_cmdline_get_key("systemd.image_policy", /* flags= */ 0, &value);
+        if (r < 0)
+                return log_error_errno(r, "Failed to read systemd.image_policy= kernel command line switch: %m");
+        if (r == 0) {
+                *ret = NULL;
+                return 0;
+        }
+
+        r = image_policy_from_string(value, ret);
+        if (r < 0)
+                return log_error_errno(r, "Failed to parse image policy '%s': %m", value);
+
+        if (DEBUG_LOGGING) {
+                _cleanup_free_ char *s = NULL;
+
+                (void) image_policy_to_string(*ret, /* simplify= */ true, &s);
+                log_debug("Loaded image policy: %s", strna(s));
+        }
+
+        return 1;
+}
+
+static int acquire_verity_settings(VeritySettings *ret) {
+        _cleanup_(verity_settings_done) VeritySettings verity = VERITY_SETTINGS_DEFAULT;
+        int r;
+
+        assert(ret);
+
+        _cleanup_free_ char *h = NULL;
+        r = proc_cmdline_get_key("roothash", /* flags= */ 0, &h);
+        if (r < 0)
+                return log_error_errno(r, "Failed to read roothash= kernel command line switch: %m");
+        if (r > 0)
+                verity.designator = PARTITION_ROOT;
+
+        _cleanup_free_ char *uh = NULL;
+        r = proc_cmdline_get_key("usrhash", /* flags= */ 0, &uh);
+        if (r < 0)
+                return log_error_errno(r, "Failed to read usrhash= kernel command line switch: %m");
+        if (r > 0) {
+                if (h)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Both roothash= and usrhash= specified, refusing.");
+
+                h = TAKE_PTR(uh);
+                verity.designator = PARTITION_USR;
+        }
+
+        if (h) {
+                r = unhexmem(h, &verity.root_hash, &verity.root_hash_size);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to parse root hash from kernel command line switch: %m");
+        }
+
+        *ret = TAKE_GENERIC(verity, VeritySettings, VERITY_SETTINGS_DEFAULT);
+        return 0;
+}
+
+static int verb_probe(UdevEvent *event, sd_device *dev) {
+        int r;
+
+        assert(event);
+        assert(dev);
+
+        /* This is invoked on 'main' block devices to probe the partition table. We will generate some
+         * properties with general image information, and then a bunch of properties for each partition, with
+         * the partition index in the variable name. These fields will be copied into partition block devices
+         * when the dissect_image builtin is later called with the "copy" verb, i.e. in verb_copy() below. */
+
+        const char *devnode;
+        r = sd_device_get_devname(dev, &devnode);
+        if (r < 0)
+                return log_device_debug_errno(dev, r, "Failed to get device node: %m");
+        if (block_device_is_whole_disk(dev) <= 0) {
+                log_device_debug(dev, "Must be invoked on whole block device (was invoked in '%s), ignoring.", devnode);
+                return 0;
+        }
+
+        r = blockdev_partscan_enabled(dev);
+        if (r < 0)
+                return log_device_debug_errno(dev, r, "Failed to determine if block device '%s' supports partitions: %m", devnode);
+        if (r == 0) {
+                log_device_debug(dev, "Invoked on block device '%s' that lacks partition scanning, ignoring.", devnode);
+                return 0;
+        }
+
+        _cleanup_(loop_device_unrefp) LoopDevice *loop = NULL;
+        r = loop_device_open(dev, O_RDONLY, LOCK_SH, &loop);
+        if (ERRNO_IS_NEG_DEVICE_ABSENT(r)) {
+                log_device_debug(dev, "Device absent while opening block device '%s', ignoring.", devnode);
+                return 0;
+        }
+        if (r < 0)
+                return log_device_debug_errno(dev, r, "Failed to open block device '%s: %m", devnode);
+
+        const ImagePolicy *image_policy = arg_image_policy ?: &image_policy_host;
+        _cleanup_(dissected_image_unrefp) DissectedImage *image = NULL;
+        r = dissect_loop_device(
+                        loop,
+                        &arg_verity_settings,
+                        /* mount_options= */ NULL,
+                        image_policy,
+                        DISSECT_IMAGE_READ_ONLY|
+                        DISSECT_IMAGE_GPT_ONLY|
+                        DISSECT_IMAGE_USR_NO_ROOT|
+                        DISSECT_IMAGE_ALLOW_EMPTY,
+                        &image);
+        if (r == -ERFKILL && !in_initrd()) {
+                /* If we transitioned into the main system and we couldn't dissect the image with the full
+                 * policy, let's see if it works if we set the policies for /usr/ and the root fs out of the
+                 * policy. After all, we already made our choices, there's no point in insisting on the
+                 * policy here. */
+
+                static const PartitionDesignator ignore_designators[] = {
+                        PARTITION_ROOT,
+                        PARTITION_ROOT_VERITY,
+                        PARTITION_ROOT_VERITY_SIG,
+                        PARTITION_USR,
+                        PARTITION_USR_VERITY,
+                        PARTITION_USR_VERITY_SIG,
+                };
+
+                _cleanup_(image_policy_freep) ImagePolicy *image_policy_mangled = NULL;
+                r = image_policy_ignore_designators(
+                                image_policy,
+                                ignore_designators,
+                                ELEMENTSOF(ignore_designators),
+                                &image_policy_mangled);
+                if (r < 0)
+                        return log_device_debug_errno(dev, r, "Failed to remove root/usr partitions from image policy: %m");
+
+                if (image_policy_equal(image_policy, image_policy_mangled))
+                        r = -ERFKILL; /* restore original error, if this didn't change anything */
+                else {
+                        if (DEBUG_LOGGING) {
+                                _cleanup_free_ char *a = NULL, *b = NULL;
+
+                                (void) image_policy_to_string(image_policy, /* simplify= */ false, &a);
+                                (void) image_policy_to_string(image_policy_mangled, /* simplify= */ false, &b);
+
+                                log_device_debug_errno(dev, ERFKILL, "Couldn't dissect block device with regular policy '%s', retrying with policy where root/usr are set to ignore '%s'.", a, b);
+                        }
+
+                        r = dissect_loop_device(
+                                        loop,
+                                        &arg_verity_settings,
+                                        /* mount_options= */ NULL,
+                                        image_policy_mangled,
+                                        DISSECT_IMAGE_READ_ONLY|
+                                        DISSECT_IMAGE_GPT_ONLY|
+                                        DISSECT_IMAGE_USR_NO_ROOT|
+                                        DISSECT_IMAGE_ALLOW_EMPTY,
+                                        &image);
+                }
+        }
+        if (IN_SET(r, -ENOPKG, -ENOMSG, -ENXIO, -ENOTUNIQ)) {
+                log_device_debug_errno(dev, r, "Device does not carry a GPT disk label with suitable partitions, ignoring.");
+                return 0;
+        }
+        if (r == -ERFKILL) {
+                log_device_debug_errno(dev, r, "Device carries GPT disk label that doesn't match our image policy, ignoring.");
+                return 0;
+        }
+        if (r < 0)
+                return log_device_debug_errno(dev, r, "Failed to dissect disk image: %m");
+
+        /* Let's try to load verity data from the image now, so that we can attach it to the device via udev
+         * properties */
+        _cleanup_(verity_settings_done) VeritySettings verity = VERITY_SETTINGS_DEFAULT;
+        r = verity_settings_copy(&verity, &arg_verity_settings);
+        if (r < 0)
+                return r;
+
+        r = dissected_image_load_verity_sig_partition(image, loop->fd, &verity);
+        if (r < 0)
+                return log_device_debug_errno(dev, r, "Failed to load verity signature data from image: %m");
+
+        /* Marker that we determined this to be a suitable image */
+        (void) udev_builtin_add_property(event, "ID_DISSECT_IMAGE", "1");
+
+        /* Output the primary architecture this image is intended for */
+        Architecture a = dissected_image_architecture(image);
+        if (a >= 0)
+                (void) udev_builtin_add_property(event, "ID_DISSECT_IMAGE_ARCHITECTURE", architecture_to_string(a));
+
+        /* And now output the intended designator and architecture (if it applies) for all partitions we
+         * found and think belong to this system */
+        FOREACH_ELEMENT(p, image->partitions) {
+                PartitionDesignator d = p - image->partitions;
+                if (!p->found)
+                        continue;
+
+                assert(p->partno > 0);
+
+                /* Indicate designator for partition */
+                _cleanup_free_ char *df = NULL;
+                if (asprintf(&df, "ID_DISSECT_PART%i_DESIGNATOR", p->partno) < 0)
+                        return log_oom_debug();
+                (void) udev_builtin_add_property(event, df, partition_designator_to_string(d));
+
+                if (p->architecture >= 0) {
+                        _cleanup_free_ char *f = NULL;
+                        if (asprintf(&f, "ID_DISSECT_PART%i_ARCHITECTURE", p->partno) < 0)
+                                return log_oom_debug();
+                        (void) udev_builtin_add_property(event, f, architecture_to_string(p->architecture));
+                }
+
+                /* Indicate whether this partition has verity protection */
+                PartitionDesignator dv = partition_verity_of(d);
+                if (dv >= 0 && image->partitions[dv].found) {
+                        _cleanup_free_ char *f = NULL;
+                        if (asprintf(&f, "ID_DISSECT_PART%i_HAS_VERITY", p->partno) < 0)
+                                return log_oom_debug();
+
+                        (void) udev_builtin_add_property(event, f, "1");
+                }
+
+                dv = partition_verity_sig_of(d);
+                if (dv >= 0 && image->partitions[dv].found) {
+                        _cleanup_free_ char *f = NULL;
+                        if (asprintf(&f, "ID_DISSECT_PART%i_HAS_VERITY_SIG", p->partno) < 0)
+                                return log_oom_debug();
+
+                        (void) udev_builtin_add_property(event, f, "1");
+                }
+
+                if (d == verity.designator) {
+                        if (verity.root_hash_size > 0) {
+                                _cleanup_free_ char *f = NULL;
+                                if (asprintf(&f, "ID_DISSECT_PART%i_ROOTHASH", p->partno) < 0)
+                                        return log_oom_debug();
+
+                                _cleanup_free_ char *h = hexmem(verity.root_hash, verity.root_hash_size);
+                                if (!h)
+                                        return log_oom_debug();
+
+                                (void) udev_builtin_add_property(event, f, h);
+                        }
+
+                        if (verity.root_hash_sig_size > 0) {
+                                _cleanup_free_ char *f = NULL;
+                                if (asprintf(&f, "ID_DISSECT_PART%i_ROOTHASH_SIG", p->partno) < 0)
+                                        return log_oom_debug();
+
+                                _cleanup_free_ char *h = NULL;
+                                if (base64mem(verity.root_hash_sig, verity.root_hash_sig_size, &h) < 0)
+                                        return log_oom_debug();
+
+                                (void) udev_builtin_add_property(event, f, h);
+                        }
+                }
+        }
+
+        return 0;
+}
+
+static int verb_copy(UdevEvent *event, sd_device *dev) {
+        int r;
+
+        assert(event);
+        assert(dev);
+
+        /* This is called for the partition block devices, and will copy the per-partition properties we
+         * probed on the main block device into the partition device */
+
+        const char *devnode;
+        r = sd_device_get_devname(dev, &devnode);
+        if (r < 0)
+                return log_device_debug_errno(dev, r, "Failed to get device node: %m");
+
+        if (!device_in_subsystem(dev, "block"))
+                return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EINVAL), "Invoked on non-block device '%s', refusing: %m", devnode);
+        if (!device_is_devtype(dev, "partition"))
+                return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EINVAL), "Invoked on non-partition block device '%s', refusing: %m", devnode);
+
+        sd_device *parent;
+        r = sd_device_get_parent(dev, &parent);
+        if (r < 0)
+                return log_error_errno(r, "Failed to get parent of device '%s': %m", devnode);
+
+        if (!device_in_subsystem(parent, "block"))
+                return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EINVAL), "Parent of block device '%s' is not a block device, refusing: %m", devnode);
+        if (!device_is_devtype(parent, "disk"))
+                return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EINVAL), "Parent of block device '%s' is not a whole block device, refusing: %m", devnode);
+
+        const char *partn;
+        r = sd_device_get_property_value(dev, "PARTN", &partn);
+        if (r < 0)
+                return log_device_debug_errno(dev, r, "Failed to get partition number of partition block device '%s': %m", devnode);
+
+        FOREACH_STRING(f, "_DESIGNATOR", "_ARCHITECTURE", "_HAS_VERITY", "_HAS_VERITY_SIG", "_ROOTHASH", "_ROOTHASH_SIG") {
+                /* The property on the parent device contains the partition number */
+                _cleanup_free_ char *p = strjoin("ID_DISSECT_PART", partn, f);
+                if (!p)
+                        return log_oom_debug();
+
+                const char *v;
+                r = sd_device_get_property_value(parent, p, &v);
+                if (r == -ENOENT)
+                        continue;
+                if (r < 0)
+                        return log_device_debug_errno(dev, r, "Failed to get '%s' property of parent of '%s': %m", p, devnode);
+
+                /* When we copy this property to the partition we drop the partition number, so that we have
+                 * a constant field name */
+                _cleanup_free_ char *c = strjoin("ID_DISSECT_PART", f);
+                if (!c)
+                        return log_oom_debug();
+
+                (void) udev_builtin_add_property(event, c, v);
+        }
+
+        return 0;
+}
+
+static int builtin_dissect_image(UdevEvent *event, int argc, char *argv[]) {
+        sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
+
+        if (argc != 2)
+                return log_device_warning_errno(
+                                dev, SYNTHETIC_ERRNO(EINVAL), "%s: expected single argument.", argv[0]);
+
+        if (streq(argv[1], "probe"))
+                return verb_probe(event, dev);
+        if (streq(argv[1], "copy"))
+                return verb_copy(event, dev);
+
+        return log_device_warning_errno(
+                        dev, SYNTHETIC_ERRNO(EINVAL), "%s: unknown verb '%s'", argv[0], argv[1]);
+}
+
+static int builtin_dissect_image_init(void) {
+        int r;
+
+        r = acquire_image_policy(&arg_image_policy);
+        if (r < 0)
+                return r;
+
+        r = acquire_verity_settings(&arg_verity_settings);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+static void builtin_dissect_image_exit(void) {
+        arg_image_policy = image_policy_free(arg_image_policy);
+        verity_settings_done(&arg_verity_settings);
+}
+
+const UdevBuiltin udev_builtin_dissect_image = {
+        .name = "dissect_image",
+        .cmd = builtin_dissect_image,
+        .init = builtin_dissect_image_init,
+        .exit = builtin_dissect_image_exit,
+        .help = "Dissect Disk Images",
+        .run_once = true,
+};
index 8df25e16036e45758ad1a455a938f5e8f9f3b76f..692b244cb8124f5ac7e5afc06a35b7e2fa4941ab 100644 (file)
@@ -15,6 +15,7 @@ static const UdevBuiltin *const builtins[_UDEV_BUILTIN_MAX] = {
         [UDEV_BUILTIN_BLKID]         = &udev_builtin_blkid,
 #endif
         [UDEV_BUILTIN_BTRFS]         = &udev_builtin_btrfs,
+        [UDEV_BUILTIN_DISSECT_IMAGE] = &udev_builtin_dissect_image,
         [UDEV_BUILTIN_FACTORY_RESET] = &udev_builtin_factory_reset,
         [UDEV_BUILTIN_HWDB]          = &udev_builtin_hwdb,
         [UDEV_BUILTIN_INPUT_ID]      = &udev_builtin_input_id,
index f38f8bd93ede1b7624bbdb008a1ad82ef0d773b1..1467f36cd6d879d95371311f000c2dfcc000e9c5 100644 (file)
@@ -37,6 +37,7 @@ typedef struct UdevBuiltin {
 extern const UdevBuiltin udev_builtin_blkid;
 #endif
 extern const UdevBuiltin udev_builtin_btrfs;
+extern const UdevBuiltin udev_builtin_dissect_image;
 extern const UdevBuiltin udev_builtin_factory_reset;
 extern const UdevBuiltin udev_builtin_hwdb;
 extern const UdevBuiltin udev_builtin_input_id;
index d01d8d98753d0d30de4792918d3622699ac59908..d744f959ae918edec0499c4ddc5e2a38d386443b 100644 (file)
@@ -40,6 +40,7 @@ typedef enum UdevBuiltinCommand {
         UDEV_BUILTIN_BLKID,
 #endif
         UDEV_BUILTIN_BTRFS,
+        UDEV_BUILTIN_DISSECT_IMAGE,
         UDEV_BUILTIN_FACTORY_RESET,
         UDEV_BUILTIN_HWDB,
         UDEV_BUILTIN_INPUT_ID,
@@ -64,6 +65,7 @@ typedef enum UdevReloadFlags {
         UDEV_RELOAD_BUILTIN_BLKID         = 1u << UDEV_BUILTIN_BLKID,
 #endif
         UDEV_RELOAD_BUILTIN_BTRFS         = 1u << UDEV_BUILTIN_BTRFS,
+        UDEV_RELOAD_BUILTIN_DISSECT_IMAGE = 1u << UDEV_BUILTIN_DISSECT_IMAGE,
         UDEV_RELOAD_BUILTIN_FACTORY_RESET = 1u << UDEV_BUILTIN_FACTORY_RESET,
         UDEV_RELOAD_BUILTIN_HWDB          = 1u << UDEV_BUILTIN_HWDB,
         UDEV_RELOAD_BUILTIN_INPUT_ID      = 1u << UDEV_BUILTIN_INPUT_ID,