]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
udev: also trigger loop device for boot disk when partition scanning is unsupported
authorDaan De Meyer <daan@amutable.com>
Sat, 4 Apr 2026 22:24:47 +0000 (22:24 +0000)
committerDaan De Meyer <daan@amutable.com>
Sat, 4 Apr 2026 22:24:47 +0000 (22:24 +0000)
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

rules.d/99-systemd.rules.in
src/udev/udev-builtin-blkid.c

index da2d311ce493414e2d5df0f55998b872862cc398..98f483503e2119f7874e390133defac78471dffe 100644 (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"
index b5fb87437c0ace861f6129b8595f8f3812313385..49bb0fbff37ea6592a806453f47fca8e6ecffd97 100644 (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;
+
+        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;
+        }
 
-        log_device_debug(dev, "GPT sector size %zi does not match device sector size %" PRIu32 ".",
-                         gpt_ssz, device_ssz);
+        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;
         }