udev: also trigger loop device for boot disk when partition scanning is unsupported

Previously, probe_gpt_sector_size_mismatch() would bail out early when
the GPT sector size matched the device sector size. However, some
devices (e.g. certain CD-ROM drives) do not support kernel partition
scanning even when sector sizes match. In that case, the kernel still
cannot parse the partition table, and we need to set up a loop device to
expose the partitions — just as we do for the sector size mismatch case.

Check blockdev_partscan_enabled() when sector sizes match, and only skip
the boot partition check if partition scanning is actually supported.

Also rename the function, udev property, and log messages to reflect the
broader scope:

- probe_gpt_sector_size_mismatch() -> probe_gpt_boot_disk_needs_loop()
- ID_PART_GPT_AUTO_ROOT_DISK_SECTOR_SIZE_MISMATCH -> ID_PART_GPT_AUTO_ROOT_DISK_NEEDS_LOOP
This commit is contained in:
Daan De Meyer
2026-04-04 22:24:47 +00:00
parent 51e04b7e90
commit 4e0eabd401
2 changed files with 28 additions and 14 deletions

View File

@@ -89,10 +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", \
# If the kernel cannot parse the GPT partition table on the boot disk (e.g. due to a sector size
# mismatch on a CD-ROM booted via El Torito, or because the device does not support partition
# scanning), trigger a loop device to expose the partitions.
SUBSYSTEM=="block", ENV{ID_PART_GPT_AUTO_ROOT_DISK_NEEDS_LOOP}=="1", \
ENV{SYSTEMD_WANTS}+="systemd-loop@.service"
LABEL="systemd_end"

View File

@@ -422,7 +422,7 @@ notloop:
return 0;
}
static int probe_gpt_sector_size_mismatch(UdevEvent *event, int fd) {
static int probe_gpt_boot_disk_needs_loop(UdevEvent *event, int fd) {
sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
int r;
@@ -431,7 +431,11 @@ static int probe_gpt_sector_size_mismatch(UdevEvent *event, int fd) {
* 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. */
* in this case.
*
* Even if the sector sizes match, if the device does not support partition scanning (e.g. some
* CD-ROM drives), the kernel still can't parse the partition table. In that case, if the disk
* contains the ESP we booted from, we still need a loop device to expose the partitions. */
_cleanup_free_ void *entries = NULL;
uint32_t n_entries, entry_size;
@@ -446,11 +450,21 @@ static int probe_gpt_sector_size_mismatch(UdevEvent *event, int fd) {
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;
bool sector_size_mismatch = (uint32_t) gpt_ssz != device_ssz;
log_device_debug(dev, "GPT sector size %zi does not match device sector size %" PRIu32 ".",
gpt_ssz, device_ssz);
if (!sector_size_mismatch) {
r = blockdev_partscan_enabled(dev);
if (r < 0)
return log_device_debug_errno(dev, r, "Failed to check if partition scanning is enabled: %m");
if (r > 0)
return 0;
}
if (sector_size_mismatch)
log_device_debug(dev, "GPT sector size %zi does not match device sector size %" PRIu32 ".",
gpt_ssz, device_ssz);
else
log_device_debug(dev, "Device does not support partition scanning.");
sd_id128_t loader_part_uuid;
r = efi_loader_get_device_part_uuid(&loader_part_uuid);
@@ -471,10 +485,10 @@ static int probe_gpt_sector_size_mismatch(UdevEvent *event, int fd) {
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");
log_device_debug(dev, "Found boot partition (ESP/XBOOTLDR) on disk where kernel cannot scan partitions.");
udev_builtin_add_property(event, "ID_PART_GPT_AUTO_ROOT_DISK_NEEDS_LOOP", "1");
udev_builtin_add_propertyf(event, "ID_PART_GPT_SECTOR_SIZE", "%zi", gpt_ssz);
return 1; /* mismatch detected and handled */
return 1; /* boot disk needs loop device */
}
return 0;
@@ -548,7 +562,7 @@ static int builtin_blkid(UdevEvent *event, int argc, char *argv[]) {
}
if (offset == 0) {
r = probe_gpt_sector_size_mismatch(event, fd);
r = probe_gpt_boot_disk_needs_loop(event, fd);
if (r > 0)
return 0;
}