From 5d3243bd468cee0dc9ec9e46c5c57f6cc7b1dba2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 13 Mar 2025 22:03:04 +0100 Subject: [PATCH] udev-builtin-blkid: look for ESP/XBOOTLDR only in initrd; afterwards just look at / So far the gpt-auto symlinks would point to: 1. the disk the ESP/XBOOTLDR is located on 2. or to a loopback device whose filename field is set to "rootdisk" or "rootdisk.raw" This makes sense in the initrd. But once we transition to the host this is quite confusing, since the symlinks might point to a different place than what we actually ended up transitioning too: the actual backing device of the root file system might be different from what gpt-auto found. Let's clean this up: let's avoid any ambiguities here: let's extend the rules above with one more rule: 3. if we left the initrd, we'll make gpt-auto point to the selected root file system, if it otherwise would have been a candidate. Or in other words, the ID_PART_GPT_AUTO_ROOT_DISK=1 udev property now always makes sense: in the initrd it points to the future root disk, and on the host to the actual root disk. Fixes: #34319 --- src/udev/udev-builtin-blkid.c | 182 +++++++++++++++++++++++----------- 1 file changed, 123 insertions(+), 59 deletions(-) diff --git a/src/udev/udev-builtin-blkid.c b/src/udev/udev-builtin-blkid.c index aa562158256..363f07c24ae 100644 --- a/src/udev/udev-builtin-blkid.c +++ b/src/udev/udev-builtin-blkid.c @@ -22,12 +22,14 @@ #include "alloc-util.h" #include "blkid-util.h" +#include "blockdev-util.h" #include "device-util.h" #include "devnum-util.h" #include "efi-loader.h" #include "errno-util.h" #include "fd-util.h" #include "gpt.h" +#include "initrd-util.h" #include "parse-util.h" #include "string-util.h" #include "strv.h" @@ -129,101 +131,163 @@ static int find_gpt_root(UdevEvent *event, blkid_probe pr, const char *loop_back #if defined(SD_GPT_ROOT_NATIVE) && ENABLE_EFI sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev); - _cleanup_free_ char *root_label = NULL; - bool found_esp_or_xbootldr = false, need_esp_or_xbootldr; - sd_id128_t root_id = SD_ID128_NULL, esp_or_xbootldr = SD_ID128_NULL; int r; assert(event); 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 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. */ + /* In the initrd: 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 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. + * + * After the initrd→host transition: look at the current block device mounted at / and set the same + * properties to its whole block device. */ 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_NEG_NOT_SUPPORTED(r)) - return log_debug_errno(r, "Unable to determine loader partition UUID: %m"); + sd_id128_t esp_or_xbootldr = SD_ID128_NULL; + bool need_esp_or_xbootldr; + dev_t root_devno = 0; + if (in_initrd()) { + /* In the initrd look at the boot loader provided data (and loopback backing fname) to find + * our *future* root */ - log_device_debug(dev, "No loader partition UUID EFI variable set, not using partition data to search for default root block device."); + r = efi_loader_get_device_part_uuid(&esp_or_xbootldr); + if (r < 0) { + if (r != -ENOENT && !ERRNO_IS_NEG_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 to 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; + } - /* 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."); + /* 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; + } else { + /* On the main system look at the *current* root instead */ + + r = blockdev_get_root(LOG_DEBUG, &root_devno); + if (r < 0) { + log_device_debug_errno(dev, r, "Unable to determine current root block device, skipping gpt-auto probing: %m"); + return 0; + } + if (r == 0) { + log_device_debug(dev, "Root block device not backed by a (single) whole block device, skipping gpt-auto probing."); 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 */ + dev_t whole_devno; + r = block_get_whole_disk(root_devno, &whole_devno); + if (r < 0) + return log_device_debug_errno(dev, r, "Failed to find whole block device for root block device: %m"); + + dev_t this_devno; + r = sd_device_get_devnum(dev, &this_devno); + if (r < 0) + return log_device_debug_errno(dev, r, "Failed to get device major/minor of device: %m"); + + if (whole_devno != this_devno) { + log_device_debug(dev, "This device is not the current root block device."); + return 0; + } + + /* We don't need to check ESP/XBOOTLDR UUID, we *know* what our root disk is */ 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 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++) { - blkid_partition pp; - const char *label; - sd_id128_t type, id; + sd_id128_t root_id = SD_ID128_NULL; + bool found_esp_or_xbootldr = false; - pp = blkid_partlist_get_partition(pl, i); - if (!pp) - continue; + if (root_devno != 0) { + /* If we already know the root partition, let's verify its type ID and then directly query + * its ID */ - r = blkid_partition_get_uuid_id128(pp, &id); - if (r < 0) { - log_device_debug_errno(dev, r, "Failed to get partition UUID, ignoring: %m"); - continue; + blkid_partition root_partition = blkid_partlist_devno_to_partition(pl, root_devno); + if (root_partition) { + sd_id128_t type; + r = blkid_partition_get_type_id128(root_partition, &type); + if (r < 0) + log_device_debug_errno(dev, r, "Failed to get root partition type UUID, ignoring: %m"); + else if (sd_id128_equal(type, SD_GPT_ROOT_NATIVE)) { + r = blkid_partition_get_uuid_id128(root_partition, &root_id); + if (r < 0) + log_device_debug_errno(dev, r, "Failed to get partition UUID, ignoring: %m"); + } } + } else { + /* We do not know the root partition, let's search for it. */ + + _cleanup_free_ char *root_label = NULL; + int nvals = blkid_partlist_numof_partitions(pl); + for (int i = 0; i < nvals; i++) { + blkid_partition pp; + const char *label; + sd_id128_t type, id; + + pp = blkid_partlist_get_partition(pl, i); + if (!pp) + continue; - r = blkid_partition_get_type_id128(pp, &type); - if (r < 0) { - log_device_debug_errno(dev, r, "Failed to get partition type UUID, ignoring: %m"); - continue; - } + r = blkid_partition_get_uuid_id128(pp, &id); + if (r < 0) { + log_device_debug_errno(dev, r, "Failed to get partition UUID, ignoring: %m"); + continue; + } - label = blkid_partition_get_name(pp); /* returns NULL if empty */ + r = blkid_partition_get_type_id128(pp, &type); + if (r < 0) { + log_device_debug_errno(dev, r, "Failed to get partition type UUID, ignoring: %m"); + continue; + } - if (need_esp_or_xbootldr && sd_id128_in_set(type, SD_GPT_ESP, SD_GPT_XBOOTLDR)) { + label = blkid_partition_get_name(pp); /* returns NULL if empty */ - /* We found an ESP or XBOOTLDR, let's see if it matches the ESP/XBOOTLDR we booted from. */ - if (sd_id128_equal(id, esp_or_xbootldr)) - found_esp_or_xbootldr = true; + if (need_esp_or_xbootldr && sd_id128_in_set(type, SD_GPT_ESP, SD_GPT_XBOOTLDR)) { - } else if (sd_id128_equal(type, SD_GPT_ROOT_NATIVE)) { - unsigned long long flags; + /* We found an ESP or XBOOTLDR, let's see if it matches the ESP/XBOOTLDR we booted from. */ + if (sd_id128_equal(id, esp_or_xbootldr)) + found_esp_or_xbootldr = true; - flags = blkid_partition_get_flags(pp); - if (flags & SD_GPT_FLAG_NO_AUTO) - continue; + } else if (sd_id128_equal(type, SD_GPT_ROOT_NATIVE)) { + unsigned long long flags; - /* systemd-sysupdate expects empty partitions to be marked with an "_empty" label, hence ignore them here. */ - if (streq_ptr(label, "_empty")) - continue; + flags = blkid_partition_get_flags(pp); + if (flags & SD_GPT_FLAG_NO_AUTO) + continue; + + /* systemd-sysupdate expects empty partitions to be marked with an "_empty" label, hence ignore them here. */ + if (streq_ptr(label, "_empty")) + continue; - /* We found a suitable root partition, let's remember the first one, or the one with - * the newest version, as determined by comparing the partition labels. */ + /* We found a suitable root partition, let's remember the first one, or the one with + * the newest version, as determined by comparing the partition labels. */ - if (sd_id128_is_null(root_id) || strverscmp_improved(label, root_label) > 0) { - root_id = id; + if (sd_id128_is_null(root_id) || strverscmp_improved(label, root_label) > 0) { + root_id = id; - if (free_and_strdup(&root_label, label) < 0) - return log_oom_debug(); + if (free_and_strdup(&root_label, label) < 0) + return log_oom_debug(); + } } } } -- 2.47.3