diff --git a/rules.d/99-systemd.rules.in b/rules.d/99-systemd.rules.in index bebc4d7d09..da2d311ce4 100644 --- a/rules.d/99-systemd.rules.in +++ b/rules.d/99-systemd.rules.in @@ -89,4 +89,10 @@ SUBSYSTEM=="module", KERNEL=="configfs", TAG+="systemd", ENV{SYSTEMD_WANTS}+="sy SUBSYSTEM=="tpmrm", KERNEL=="tpmrm[0-9]*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="tpm2.target" SUBSYSTEM=="tpm", KERNEL=="tpm[0-9]*", TAG+="systemd" +# If the GPT sector size doesn't match the device's native sector size (e.g. 512-byte GPT on a +# 2048-byte CD-ROM booted via El Torito), trigger a loop device to re-expose it with the correct +# sector size so the kernel can parse the partition table. +SUBSYSTEM=="block", ENV{ID_PART_GPT_AUTO_ROOT_DISK_SECTOR_SIZE_MISMATCH}=="1", \ + ENV{SYSTEMD_WANTS}+="systemd-loop@.service" + LABEL="systemd_end" diff --git a/src/udev/udev-builtin-blkid.c b/src/udev/udev-builtin-blkid.c index 6ec02674b9..b5fb87437c 100644 --- a/src/udev/udev-builtin-blkid.c +++ b/src/udev/udev-builtin-blkid.c @@ -20,6 +20,7 @@ #include "blockdev-util.h" #include "device-util.h" #include "devnum-util.h" +#include "efi-api.h" #include "efi-loader.h" #include "errno-util.h" #include "fd-util.h" @@ -421,6 +422,64 @@ notloop: return 0; } +static int probe_gpt_sector_size_mismatch(UdevEvent *event, int fd) { + sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev); + int r; + + /* Probe the GPT sector size. For CD-ROMs booted via El Torito, the GPT may use a different + * sector size than the device (e.g. 512-byte GPT on a 2048-byte CD-ROM). If there's a mismatch, + * check if this is the boot disk by comparing GPT partition UUIDs with the ESP/XBOOTLDR UUID + * exported by the boot loader. If it matches, set a property so that udev rules can set up a + * loop device with the correct sector size — the kernel can't parse the partition table itself + * in this case. */ + + _cleanup_free_ void *entries = NULL; + uint32_t n_entries, entry_size; + ssize_t gpt_ssz = gpt_probe(fd, /* ret_header= */ NULL, &entries, &n_entries, &entry_size); + if (gpt_ssz < 0) + return log_device_debug_errno(dev, gpt_ssz, "Failed to probe GPT: %m"); + if (gpt_ssz == 0) + return 0; + + uint32_t device_ssz; + r = blockdev_get_sector_size(fd, &device_ssz); + if (r < 0) + return log_device_debug_errno(dev, r, "Failed to get device sector size: %m"); + + if ((uint32_t) gpt_ssz == device_ssz) + return 0; + + log_device_debug(dev, "GPT sector size %zi does not match device sector size %" PRIu32 ".", + gpt_ssz, device_ssz); + + sd_id128_t loader_part_uuid; + r = efi_loader_get_device_part_uuid(&loader_part_uuid); + if (r < 0) { + if (r != -ENOENT && !ERRNO_IS_NEG_NOT_SUPPORTED(r)) + return log_device_debug_errno(dev, r, "Failed to get loader partition UUID: %m"); + + return 0; + } + + for (uint32_t i = 0; i < n_entries; i++) { + const GptPartitionEntry *entry = (const GptPartitionEntry *) ((const uint8_t *) entries + (size_t) entry_size * i); + + sd_id128_t type = efi_guid_to_id128(entry->partition_type_guid); + if (!sd_id128_in_set(type, SD_GPT_ESP, SD_GPT_XBOOTLDR)) + continue; + + if (!sd_id128_equal(efi_guid_to_id128(entry->unique_partition_guid), loader_part_uuid)) + continue; + + log_device_debug(dev, "Found boot partition (ESP/XBOOTLDR) on disk with sector size mismatch."); + udev_builtin_add_property(event, "ID_PART_GPT_AUTO_ROOT_DISK_SECTOR_SIZE_MISMATCH", "1"); + udev_builtin_add_propertyf(event, "ID_PART_GPT_SECTOR_SIZE", "%zi", gpt_ssz); + return 1; /* mismatch detected and handled */ + } + + return 0; +} + static int builtin_blkid(UdevEvent *event, int argc, char *argv[]) { sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev); const char *devnode, *root_partition = NULL, *data, *name; @@ -476,17 +535,6 @@ static int builtin_blkid(UdevEvent *event, int argc, char *argv[]) { } } - sym_blkid_probe_set_superblocks_flags(pr, - BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID | - BLKID_SUBLKS_TYPE | BLKID_SUBLKS_SECTYPE | -#ifdef BLKID_SUBLKS_FSINFO /* since util-linux 2.39 */ - BLKID_SUBLKS_FSINFO | -#endif - BLKID_SUBLKS_USAGE | BLKID_SUBLKS_VERSION); - - if (noraid) - sym_blkid_probe_filter_superblocks_usage(pr, BLKID_FLTR_NOTIN, BLKID_USAGE_RAID); - r = sd_device_get_devname(dev, &devnode); if (r < 0) return log_device_debug_errno(dev, r, "Failed to get device name: %m"); @@ -499,6 +547,23 @@ static int builtin_blkid(UdevEvent *event, int argc, char *argv[]) { return ignore ? 0 : fd; } + if (offset == 0) { + r = probe_gpt_sector_size_mismatch(event, fd); + if (r > 0) + return 0; + } + + sym_blkid_probe_set_superblocks_flags(pr, + BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID | + BLKID_SUBLKS_TYPE | BLKID_SUBLKS_SECTYPE | +#ifdef BLKID_SUBLKS_FSINFO /* since util-linux 2.39 */ + BLKID_SUBLKS_FSINFO | +#endif + BLKID_SUBLKS_USAGE | BLKID_SUBLKS_VERSION); + + if (noraid) + sym_blkid_probe_filter_superblocks_usage(pr, BLKID_FLTR_NOTIN, BLKID_USAGE_RAID); + errno = 0; r = sym_blkid_probe_set_device(pr, fd, offset, 0); if (r < 0)