mirror of
https://github.com/systemd/systemd.git
synced 2025-03-19 22:50:17 +03:00
Merge 3daeb3243feb9a60d50a1f47d6710fabc199b0d5 into 104587314ff25a5c35390eeb42308f083e1e0488
This commit is contained in:
commit
56b383e3c5
44
rules.d/90-image-dissect.rules
Normal file
44
rules.d/90-image-dissect.rules
Normal file
@ -0,0 +1,44 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is part of systemd.
|
||||
#
|
||||
# systemd is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation; either version 2.1 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
ACTION=="remove", GOTO="image_dissect_end"
|
||||
SUBSYSTEM!="block", GOTO="image_dissect_end"
|
||||
|
||||
# Add symlink to GPT root disk – in two flavours: one which takes the factory
|
||||
# reset state into account, and one which does not. The former is useful for
|
||||
# wipe-rootfs-on-factory-reset scenarios where we should not be tempted to use
|
||||
# the root fs before factory reset is complete. The latter is useful for
|
||||
# wipe-only-/var-on-factory-reset where we should use it (because that's where
|
||||
# repart.d/ definitions are placed which tell us what to wipe).
|
||||
ENV{ID_PART_GPT_AUTO_ROOT}!="1", GOTO="gpt_auto_root_end"
|
||||
IMPORT{builtin}="factory_reset status"
|
||||
ENV{ID_FS_TYPE}!="crypto_LUKS", ENV{ID_FACTORY_RESET}!="on", SYMLINK+="gpt-auto-root"
|
||||
ENV{ID_FS_TYPE}!="crypto_LUKS", ENV{ID_FACTORY_RESET}=="on|complete", SYMLINK+="gpt-auto-root-ignore-factory-reset"
|
||||
ENV{ID_FS_TYPE}=="crypto_LUKS", ENV{ID_FACTORY_RESET}!="on", SYMLINK+="gpt-auto-root-luks"
|
||||
ENV{ID_FS_TYPE}=="crypto_LUKS", ENV{ID_FACTORY_RESET}=="on|complete", SYMLINK+="gpt-auto-root-luks-ignore-factory-reset"
|
||||
LABEL="gpt_auto_root_end"
|
||||
|
||||
# Note we don't need to condition the gpt-auto-root LUKS symlink for
|
||||
# auto-discovered LUKS devices, because it's sufficient if we do this for the
|
||||
# underlying partition block device, which is covered by the above.
|
||||
ENV{DM_UUID}=="CRYPT-*", ENV{DM_NAME}=="root", SYMLINK+="gpt-auto-root", IMPORT{builtin}="factory_reset status"
|
||||
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", ENV{ID_FACTORY_RESET}=="", IMPORT{builtin}="factory_reset status"
|
||||
ENV{DEVTYPE}=="disk", ENV{ID_PART_GPT_AUTO_ROOT_DISK}=="1", ENV{ID_FACTORY_RESET}!="on", IMPORT{builtin}="dissect_image probe"
|
||||
|
||||
# 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
|
||||
ENV{DEVTYPE}=="partition", ENV{ID_DISSECT_PART_DESIGNATOR}!="|root", ENV{ID_FS_TYPE}!="crypto_LUKS", SYMLINK+="gpt-auto-$env{ID_DISSECT_PART_DESIGNATOR}"
|
||||
ENV{DEVTYPE}=="partition", ENV{ID_DISSECT_PART_DESIGNATOR}!="|root", ENV{ID_FS_TYPE}=="crypto_LUKS", SYMLINK+="gpt-auto-$env{ID_DISSECT_PART_DESIGNATOR}-luks"
|
||||
|
||||
LABEL="image_dissect_end"
|
@ -34,26 +34,6 @@ SUBSYSTEM=="block", ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}=="1", ENV{SYSTEMD_READ
|
||||
# we are probably still calling mke2fs or mkswap on it.
|
||||
SUBSYSTEM=="block", ENV{DM_UUID}=="CRYPT-*", ENV{ID_PART_TABLE_TYPE}=="", ENV{ID_FS_USAGE}=="", ENV{SYSTEMD_READY}="0"
|
||||
|
||||
# Add symlink to GPT root disk – in two flavours: one which takes the factory
|
||||
# reset state into account, and one which does not. The former is useful for
|
||||
# wipe-rootfs-on-factory-reset scenarios where we should not be tempted to use
|
||||
# the root fs before factory reset is complete. The latter is useful for
|
||||
# wipe-only-/var-on-factory-reset where we should use it (because that's where
|
||||
# repart.d/ definitions are placed which tell us what to wipe).
|
||||
SUBSYSTEM!="block", GOTO="gpt_auto_root_end"
|
||||
ENV{ID_PART_GPT_AUTO_ROOT}!="1", GOTO="gpt_auto_root_end"
|
||||
IMPORT{builtin}="factory_reset status"
|
||||
ENV{ID_FS_TYPE}!="crypto_LUKS", ENV{ID_FACTORY_RESET}!="on", SYMLINK+="gpt-auto-root"
|
||||
ENV{ID_FS_TYPE}!="crypto_LUKS", ENV{ID_FACTORY_RESET}=="on|complete", SYMLINK+="gpt-auto-root-ignore-factory-reset"
|
||||
ENV{ID_FS_TYPE}=="crypto_LUKS", ENV{ID_FACTORY_RESET}!="on", SYMLINK+="gpt-auto-root-luks"
|
||||
ENV{ID_FS_TYPE}=="crypto_LUKS", ENV{ID_FACTORY_RESET}=="on|complete", SYMLINK+="gpt-auto-root-luks-ignore-factory-reset"
|
||||
LABEL="gpt_auto_root_end"
|
||||
# Note we don't need to condition the gpt-auto-root LUKS symlink for
|
||||
# auto-discovered LUKS devices, because it's sufficient if we do this for the
|
||||
# underlying partition block device, which is covered by the above.
|
||||
SUBSYSTEM=="block", ENV{DM_UUID}=="CRYPT-*", ENV{DM_NAME}=="root", SYMLINK+="gpt-auto-root", IMPORT{builtin}="factory_reset status"
|
||||
SUBSYSTEM=="block", ENV{DM_UUID}=="CRYPT-*", ENV{DM_NAME}=="root", ENV{ID_FACTORY_RESET}=="on|complete", SYMLINK+="gpt-auto-root-ignore-factory-reset"
|
||||
|
||||
# Ignore raid devices that are not yet assembled and started
|
||||
SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", KERNEL=="md*", TEST!="md/array_state", ENV{SYSTEMD_READY}="0"
|
||||
SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", KERNEL=="md*", ATTR{md/array_state}=="|clear|inactive", ENV{SYSTEMD_READY}="0"
|
||||
|
@ -32,6 +32,7 @@ rules = [
|
||||
'80-net-setup-link.rules',
|
||||
'81-net-bridge.rules',
|
||||
'81-net-dhcp.rules',
|
||||
'90-image-dissect.rules',
|
||||
'90-iocost.rules',
|
||||
)],
|
||||
|
||||
|
@ -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',
|
||||
|
205
src/udev/udev-builtin-dissect_image.c
Normal file
205
src/udev/udev-builtin-dissect_image.c
Normal file
@ -0,0 +1,205 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "device-util.h"
|
||||
#include "dissect-image.h"
|
||||
#include "fd-util.h"
|
||||
#include "image-policy.h"
|
||||
#include "loop-util.h"
|
||||
#include "proc-cmdline.h"
|
||||
#include "udev-builtin.h"
|
||||
|
||||
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_debug_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_debug_errno(r, "Failed to parse image policy '%s': %m", value);
|
||||
|
||||
if (DEBUG_LOGGING) {
|
||||
_cleanup_free_ char *s = NULL;
|
||||
|
||||
image_policy_to_string(*ret, /* simplify= */ true, &s);
|
||||
log_debug("Loaded image policy: %s", strna(s));
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
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 (!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, "disk"))
|
||||
return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EINVAL), "Invoked on partition block device '%s', refusing: %m", devnode);
|
||||
|
||||
_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_errno(dev, r, "Device absent while opening block device '%s', ignoring: %m", devnode);
|
||||
return 0;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_device_debug_errno(dev, r, "Failed to open block device '%s: %m", devnode);
|
||||
|
||||
/* Load image policy from kernel command line, similar to what systemd-gpt-auto-generator does */
|
||||
_cleanup_(image_policy_freep) ImagePolicy *image_policy = NULL;
|
||||
(void) acquire_image_policy(&image_policy);
|
||||
|
||||
_cleanup_(dissected_image_unrefp) DissectedImage *image = NULL;
|
||||
r = dissect_loop_device(
|
||||
loop,
|
||||
/* verity= */ NULL,
|
||||
/* mount_options= */ NULL,
|
||||
image_policy ?: &image_policy_host,
|
||||
DISSECT_IMAGE_READ_ONLY|
|
||||
DISSECT_IMAGE_GPT_ONLY|
|
||||
DISSECT_IMAGE_USR_NO_ROOT|
|
||||
DISSECT_IMAGE_ALLOW_EMPTY,
|
||||
&image);
|
||||
if (IN_SET(r, -ENOPKG, -ENOMSG)) {
|
||||
log_device_debug_errno(dev, r, "Device does not carry a GPT disk label with suitable partitions, skipping.");
|
||||
return 0;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_device_debug_errno(dev, r, "Failed to dissect disk 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) {
|
||||
if (!p->found)
|
||||
continue;
|
||||
|
||||
assert(p->partno > 0);
|
||||
|
||||
_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(p - image->partitions));
|
||||
|
||||
if (p->architecture >= 0) {
|
||||
_cleanup_free_ char *af = NULL;
|
||||
if (asprintf(&af, "ID_DISSECT_PART%i_ARCHITECTURE", p->partno) < 0)
|
||||
return log_oom_debug();
|
||||
|
||||
(void) udev_builtin_add_property(event, af, architecture_to_string(p->architecture));
|
||||
}
|
||||
}
|
||||
|
||||
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 *sysnum;
|
||||
r = sd_device_get_sysnum(dev, &sysnum);
|
||||
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") {
|
||||
/* The property on the parent device contains the partition number */
|
||||
_cleanup_free_ char *p = strjoin("ID_DISSECT_PART", sysnum, 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 'probe' or 'copy', got '%s'.", argv[0], argv[1]);
|
||||
|
||||
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 vern '%s'", argv[0], argv[1]);
|
||||
}
|
||||
|
||||
const UdevBuiltin udev_builtin_dissect_image = {
|
||||
.name = "dissect_image",
|
||||
.cmd = builtin_dissect_image,
|
||||
.help = "Dissect Disk Images",
|
||||
.run_once = true,
|
||||
};
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user