From: Lennart Poettering Date: Mon, 10 Feb 2025 21:24:52 +0000 (+0100) Subject: udev-builtin-blkid: use loopback block device 'ref' field fo determining gpt-auto... X-Git-Tag: v258-rc1~1280^2~15 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f8825c1364fb1c3f3f5d96654e779fd51500f476;p=thirdparty%2Fsystemd.git udev-builtin-blkid: use loopback block device 'ref' field fo determining gpt-auto whole block device So far the gpt-auto logic only looked for the partition table of devices that the ESP/XBOOTLDR partition used to boot was on. This works great for local boots, but is more problematic if we boot a UKI via UEFI HTTP boot, because there is no ESP in play in that case. Let's introduce an alternative to communicate the intended default root disk to cover for this situation: any loopback block device whose backing file field (i.e. the userspace controlled freeform field we use for /dev/disk/by-loop-ref/ naming) is set to "rootdisk" will be consider for gpt-auto will be consider for gpt-auto. With this in place we should have nice automatic behaviour: 1. If we are booted locally we'll get the ESP/XBOOTLDR data, and derive the root disk from that. 2. If we are booted via UEFI HTTP boot we expect that the caller makes the loopback device appear with the right loop-ref identifier, and then will use that. --- diff --git a/man/systemd-gpt-auto-generator.xml b/man/systemd-gpt-auto-generator.xml index 2c37c8af039..69788baab45 100644 --- a/man/systemd-gpt-auto-generator.xml +++ b/man/systemd-gpt-auto-generator.xml @@ -50,19 +50,49 @@ If the units this generator creates are overridden, for example by units in directories with higher precedence, drop-ins and additional dependencies created by this generator might still be used. - This generator will only look for the root partition on the same physical disk where the EFI System - Partition (ESP) is located. Note that support from the boot loader is required: the EFI variable - LoaderDevicePartUUID of the 4a67b082-0a4c-41cf-b6c7-440b29bb8c4f - vendor UUID is used to determine from which partition, and hence the disk, from which the system was - booted. If the boot loader does not set this variable, this generator will not be able to detect the root - partition. See the Boot Loader Interface - for details. - - Similarly, this generator will only look for the other partitions on the same physical disk as the - root partition. In this case, boot loader support is not required. These partitions will not be searched - for on systems where the root file system is distributed on multiple disks, for example via btrfs RAID. + When run in the initial RAM disk (initrd) this generator can automatically search for the root file + system. Specifically: + + + It will look for the root partition on the same physical disk where the EFI System + Partition (ESP) is located. Note that support from the boot loader is required for this to work: the + EFI variable LoaderDevicePartUUID of the + 4a67b082-0a4c-41cf-b6c7-440b29bb8c4f vendor UUID is used to determine from which + partition (and hence disk) the system was booted. If the boot loader does not set this variable, this + generator will not be able to detect the root partition. See the Boot Loader Interface for + details. + + Alternatively, it will look for the root file system on a loopback block device whose + .lo_name field is set to one of the literal strings rootdisk or + rootdisk.raw. This field can be set via losetup's + string. For images downloaded via + systemd-import-generator8 + make sure to set the blockdev option and set the local name string to + rootdisk to achieve this effect. Note that discovery of the root file system on + loopback block devices like this is only done if root=gpt-auto is specified + explicitly on the kernel command line, unlike the discovery based on the boot loader reported ESP which + is also enabled if no root= parameter is specified at all. (The latter relies on + systemd-udevd.service's /dev/gpt-auto-root block device symlink + generation). + + When run on the host system (i.e. after successfully transitioning out of the initrd into the root + filesystem) this generator will look for all other partitions on the same physical disk as the root + partition. For this discovery, boot loader support is not required. Moreover, it is not required that the + root partition was automatically discovered by the initrd (as described above) for the discovery of the + non-root file partitions to take place. Or in other words: automatic discovery of the root file system + and of the non-root file systems are independent operations, that do not rely on each other, and are done + during two distinct phases of the boot process (one in the initrd, the other after). These partitions will + not be searched for on systems where the root file system is distributed on multiple disks, for example + via btrfs RAID. + + The root partition can be configured explicitly by symlinking + /run/systemd/volatile-root to /dev/block/$major:$minor. This is + especially useful if the root mount has been replaced by some form of volatile file system + (overlayfs). + systemd-gpt-auto-generator is useful for centralizing file system configuration in the partition table and making configuration in /etc/fstab or on the kernel command line unnecessary. @@ -225,11 +255,6 @@ bootup7. - The root partition can be specified by symlinking /run/systemd/volatile-root - to /dev/block/$major:$minor. This is especially useful if the root mount has been - replaced by some form of volatile file system (overlayfs). - - Mount and automount units for the EFI System Partition (ESP) and Extended Boot Loader Partition (XBOOTLDR) are generated on EFI systems. If the disk contains an XBOOTLDR partition, as defined in the Boot Loader @@ -297,10 +322,14 @@ rootflags= When root= is used with the special value - gpt-auto (or if the parameter is not used at all), automatic discovery of the root + gpt-auto, full automatic discovery of the root partition based on the GPT partition type is enabled. Any other value disables this logic. + If root= is not specified at all on the kernel command line automatic + discovery of the root partition via the boot loader reported ESP is also enabled, however in this + case discovery based on the loopback block device .lo_name field is not enabled. + The rootfstype= and rootflags= are used to select the file system type and options when the root file system is automatically discovered. diff --git a/man/systemd-import-generator.xml b/man/systemd-import-generator.xml index ba95ebe2c17..767f346bf26 100644 --- a/man/systemd-import-generator.xml +++ b/man/systemd-import-generator.xml @@ -125,6 +125,17 @@ device (via systemd-loop@.service) after completion. This permits booting from downloaded disk images. This is only supported for raw disk images. + Note when this option is used with the purpose of mounting a disk image conforming to the + Discoverable + Disk Image Specification as root file system, and the automatic GPT partition discovery + logic as implemented by + systemd-gpt-auto-generator8 + shall process it, it's essential to specify rootdisk as the local name for the + import. Moreover, root=gpt-auto must be specified on the kernel command line + explicitly. Also, prefix the systemd.pull= commmand line option with + rd. to ensure it is executed in the initial RAM disk (initrd) already, also + see below. + @@ -235,12 +246,14 @@ Boot into disk image (raw), with URL derived from UEFI HTTP network booting - rd.systemd.pull=raw,machine,verify=no,blockdev,bootorigin:rootdisk:image.raw.xz root=/dev/disk/by-loop-ref/rootdisk.raw-part2 + rd.systemd.pull=raw,machine,verify=no,blockdev,bootorigin:rootdisk:image.raw.xz root=gpt-auto This is similar to the previous example, but this time the source URL is automatically derived from the UEFI HTTP network boot URL. For example, if an UKI is booted from an URL http://example.com/image.efi this would result in a root disk being downloaded from - http://example.com/image.raw.xz. + http://example.com/image.raw.xz. Moreover this uses the + systemd-gpt-auto-generator8 + logic to mount the root file system from the disk image. @@ -263,6 +276,7 @@ systemd.system-credentials7 importctl1 systemd-loop@.service8 + systemd-gpt-auto-generator8 diff --git a/src/udev/udev-builtin-blkid.c b/src/udev/udev-builtin-blkid.c index 73417338f54..6fdd6c1044e 100644 --- a/src/udev/udev-builtin-blkid.c +++ b/src/udev/udev-builtin-blkid.c @@ -125,12 +125,13 @@ static void print_property(UdevEvent *event, const char *name, const char *value } } -static int find_gpt_root(UdevEvent *event, blkid_probe pr) { +static int find_gpt_root(UdevEvent *event, blkid_probe pr, const char *loop_backing_fname) { #if defined(SD_GPT_ROOT_NATIVE) && ENABLE_EFI - + sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev); + sd_id128_t esp_or_xbootldr = SD_ID128_NULL; _cleanup_free_ char *root_label = NULL; - bool found_esp_or_xbootldr = false; + bool found_esp_or_xbootldr = false, need_esp_or_xbootldr; sd_id128_t root_id = SD_ID128_NULL; int r; @@ -138,13 +139,42 @@ static int find_gpt_root(UdevEvent *event, blkid_probe pr) { assert(pr); /* Iterate through the partitions on this disk, and see if the UEFI ESP or XBOOTLDR partition we - * booted from is on it. If so, find the first root disk, and add a property indicating its partition - * UUID. */ + * booted from is on it. If so, find the newest root partition, and add a property indicating its + * partition UUID. We also do this if we are dealing with a loopback block device whose "backing + * filename" field is set to the string "root". In the latter case we do not search for ESP or + * XBOOTLDR. */ + + if (!device_is_devtype(dev, "disk")) { + log_device_debug(dev, "Skipping GPT root logic on partition block device."); + return 0; + } + + r = efi_loader_get_device_part_uuid(&esp_or_xbootldr); + if (r < 0) { + if (r != -ENOENT && !ERRNO_IS_NOT_SUPPORTED(r)) + return log_debug_errno(r, "Unable to determine loader partition UUID: %m"); + + log_device_debug(dev, "No loader partition UUID EFI variable set, not using partition data for search for default root block device."); + + /* NB: if an ESP/xbootldr field is set, we always use that. We do this in order to guarantee + * systematic behaviour. */ + if (!STRPTR_IN_SET(loop_backing_fname, "rootdisk", "rootdisk.raw")) { + log_device_debug(dev, "Device is not a loopback block device with reference string 'root', not considering block device as default root block device."); + return 0; + } + + /* OK, we have now sufficiently identified this device as the right root "whole" device, + * hence no need to bother with searching for ESP/XBOOTLDR */ + need_esp_or_xbootldr = false; + } else + /* We now know the the ESP/xbootldr UUID, but we cannot be sure yet it's on this block + * device, hence look for it among partitions now */ + need_esp_or_xbootldr = true; errno = 0; blkid_partlist pl = blkid_probe_get_partitions(pr); if (!pl) - return errno_or_else(ENOMEM); + return log_device_debug_errno(dev, errno_or_else(ENOMEM), "Failed to probe partitions: %m"); int nvals = blkid_partlist_numof_partitions(pl); for (int i = 0; i < nvals; i++) { @@ -158,27 +188,21 @@ static int find_gpt_root(UdevEvent *event, blkid_probe pr) { r = blkid_partition_get_uuid_id128(pp, &id); if (r < 0) { - log_debug_errno(r, "Failed to get partition UUID, ignoring: %m"); + log_device_debug_errno(dev, r, "Failed to get partition UUID, ignoring: %m"); continue; } r = blkid_partition_get_type_id128(pp, &type); if (r < 0) { - log_debug_errno(r, "Failed to get partition type UUID, ignoring: %m"); + log_device_debug_errno(dev, r, "Failed to get partition type UUID, ignoring: %m"); continue; } label = blkid_partition_get_name(pp); /* returns NULL if empty */ - if (sd_id128_in_set(type, SD_GPT_ESP, SD_GPT_XBOOTLDR)) { - sd_id128_t esp_or_xbootldr; + if (need_esp_or_xbootldr && sd_id128_in_set(type, SD_GPT_ESP, SD_GPT_XBOOTLDR)) { /* We found an ESP or XBOOTLDR, let's see if it matches the ESP/XBOOTLDR we booted from. */ - - r = efi_loader_get_device_part_uuid(&esp_or_xbootldr); - if (r < 0) - return r; - if (sd_id128_equal(id, esp_or_xbootldr)) found_esp_or_xbootldr = true; @@ -195,17 +219,20 @@ static int find_gpt_root(UdevEvent *event, blkid_probe pr) { if (sd_id128_is_null(root_id) || strverscmp_improved(label, root_label) > 0) { root_id = id; - r = free_and_strdup(&root_label, label); - if (r < 0) - return r; + if (free_and_strdup(&root_label, label) < 0) + return log_oom_debug(); } } } - /* We found the ESP/XBOOTLDR on this disk, and also found a root partition, nice! Let's export its - * UUID */ - if (found_esp_or_xbootldr && !sd_id128_is_null(root_id)) - udev_builtin_add_property(event, "ID_PART_GPT_AUTO_ROOT_UUID", SD_ID128_TO_UUID_STRING(root_id)); + if (!need_esp_or_xbootldr || found_esp_or_xbootldr) { + /* We found the ESP/XBOOTLDR on this disk (or we didn't need it) */ + udev_builtin_add_property(event, "ID_PART_GPT_AUTO_ROOT_DISK", "1"); + + /* We found a root partition, nice! Let's export its UUID. */ + if (!sd_id128_is_null(root_id)) + udev_builtin_add_property(event, "ID_PART_GPT_AUTO_ROOT_UUID", SD_ID128_TO_UUID_STRING(root_id)); + } #endif return 0; @@ -437,9 +464,6 @@ static int builtin_blkid(UdevEvent *event, int argc, char *argv[]) { udev_builtin_add_property(event, "ID_PART_GPT_AUTO_ROOT", "1"); } - if (is_gpt) - find_gpt_root(event, pr); - r = read_loopback_backing_inode( dev, fd, @@ -466,6 +490,9 @@ static int builtin_blkid(UdevEvent *event, int argc, char *argv[]) { } } + if (is_gpt) + find_gpt_root(event, pr, backing_fname); + return 0; }