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
This commit is contained in:
Lennart Poettering
2025-04-03 11:08:57 +02:00
parent e04c7a92cc
commit 5d3243bd46
+125 -61
View File
@@ -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");
/* 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.");
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;
}
/* 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. */
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;
}
_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;
label = blkid_partition_get_name(pp); /* returns NULL if empty */
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. */
if (sd_id128_equal(id, esp_or_xbootldr))
found_esp_or_xbootldr = true;
} else if (sd_id128_equal(type, SD_GPT_ROOT_NATIVE)) {
unsigned long long flags;
flags = blkid_partition_get_flags(pp);
if (flags & SD_GPT_FLAG_NO_AUTO)
pp = blkid_partlist_get_partition(pl, i);
if (!pp)
continue;
/* systemd-sysupdate expects empty partitions to be marked with an "_empty" label, hence ignore them here. */
if (streq_ptr(label, "_empty"))
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;
}
/* 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. */
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 (sd_id128_is_null(root_id) || strverscmp_improved(label, root_label) > 0) {
root_id = id;
label = blkid_partition_get_name(pp); /* returns NULL if empty */
if (free_and_strdup(&root_label, label) < 0)
return log_oom_debug();
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. */
if (sd_id128_equal(id, esp_or_xbootldr))
found_esp_or_xbootldr = true;
} else if (sd_id128_equal(type, SD_GPT_ROOT_NATIVE)) {
unsigned long long flags;
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. */
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();
}
}
}
}