+143
-63
@@ -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,179 @@ 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.");
|
||||
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");
|
||||
|
||||
r = block_device_is_whole_disk(dev);
|
||||
if (r < 0)
|
||||
return log_device_debug_errno(dev, r, "Unable to determine if device '%s' is a whole-block device: %m", devnode);
|
||||
if (r == 0) {
|
||||
log_device_debug(dev, "Invoked on device '%s' which is not a whole-disk block device, ignoring.", devnode);
|
||||
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");
|
||||
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;
|
||||
}
|
||||
|
||||
log_device_debug(dev, "No loader partition UUID EFI variable set, not using partition data to search for default root block device.");
|
||||
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 */
|
||||
|
||||
/* 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.");
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user