diff --git a/libblkid/src/superblocks/iso9660.c b/libblkid/src/superblocks/iso9660.c index 730939f70..fd7f7cf81 100644 --- a/libblkid/src/superblocks/iso9660.c +++ b/libblkid/src/superblocks/iso9660.c @@ -19,7 +19,9 @@ #include #include +#include "iso9660.h" #include "superblocks.h" +#include "sysfs.h" struct iso9660_date { unsigned char year[4]; @@ -43,11 +45,13 @@ struct iso_volume_descriptor { unsigned char unused[8]; unsigned char space_size[8]; unsigned char escape_sequences[8]; - unsigned char unused1[222]; + unsigned char unused1[32]; + unsigned char logical_block_size[4]; + unsigned char unused2[186]; unsigned char publisher_id[128]; - unsigned char unused2[128]; + unsigned char unused3[128]; unsigned char application_id[128]; - unsigned char unused3[111]; + unsigned char unused4[111]; struct iso9660_date created; struct iso9660_date modified; } __attribute__((packed)); @@ -169,15 +173,22 @@ static int is_str_empty(const unsigned char *str, size_t len) /* * The ISO format specifically avoids the first 32kb to allow for a * partition table to be added, if desired. - * When an ISO contains a partition table, the usual thing to do is to - * have a partition that points at the iso filesystem. In such case, + * When an ISO contains a partition table, it is common to have a partition + * that points at the iso mani filesystem. In such case, * we want to only probe the iso metadata for the corresponding partition * device, avoiding returning the metadata for the parent block device. */ -static bool isofs_belongs_to_device(blkid_probe pr) +static bool isofs_belongs_to_device(blkid_probe pr, struct iso_volume_descriptor *iso) { - dev_t devno; + dev_t devno, disk_devno, isopart_devno; blkid_partlist ls; + blkid_loff_t pr_offset; + int isopart_partno = -1; + int nparts, i; + int num_sectors, sector_size; + long iso_size; + blkid_partition par; + struct path_cxt *pc; /* Get device number, but if that fails, assume we aren't dealing * with partitions, and continue probing. */ @@ -191,10 +202,49 @@ static bool isofs_belongs_to_device(blkid_probe pr) if (!ls) return true; - /* Check that the device we're working with corresponds to an - * entry in the partition table. If so, this is the correct - * device to return the iso metadata on. */ - return blkid_partlist_devno_to_partition(ls, devno) != NULL; + /* Calculate size of ISO9660 filesystem */ + num_sectors = isonum_733(iso->space_size, false); + sector_size = isonum_723(iso->logical_block_size, false); + iso_size = ((long)num_sectors * (long)sector_size) >> 9; + + /* Look for a partition that matches the ISO9660 filesystem start sector + * and size. */ + pr_offset = blkid_probe_get_offset(pr); + nparts = blkid_partlist_numof_partitions(ls); + for (i = 0; i < nparts; i++) { + par = blkid_partlist_get_partition(ls, i); + if (blkid_partition_get_start(par) == pr_offset && + blkid_partition_get_size(par) == iso_size) { + isopart_partno = blkid_partition_get_partno(par); + break; + } + } + + /* If we didn't find a matching partition, consider the current + * device as an appropriate owner of the ISO9660 filesystem. */ + if (isopart_partno == -1) + return true; + + /* A partition matching the ISO9660 filesystem was found. Look for + * a devno that corresponds to this partition. */ + disk_devno = blkid_probe_get_wholedisk_devno(pr); + pc = ul_new_sysfs_path(disk_devno, NULL, NULL); + if (!pc) + return true; + + isopart_devno = sysfs_blkdev_partno_to_devno(pc, isopart_partno); + ul_unref_path(pc); + + /* If no ISO9660 partition devno was found, consider the current device + * as an appropriate owner of the filesystem. This can happen for CD/DVDs, + * where partitions may exist in the table, but are not usually probed by + * the kernel. */ + if (!isopart_devno) + return true; + + /* We found a partition that matches the ISO9660 filesystem. Check that it + * corresponds to the device that we are probing. */ + return devno == isopart_devno; } /* iso9660 [+ Microsoft Joliet Extension] */ @@ -214,7 +264,7 @@ static int probe_iso9660(blkid_probe pr, const struct blkid_idmag *mag) /* Check if the iso metadata should be returned on a different device * instead of this one. */ - if (!isofs_belongs_to_device(pr)) + if (!isofs_belongs_to_device(pr, iso)) return 1; memcpy(label, iso->volume_id, sizeof(label)); diff --git a/tests/expected/blkid/iso-partitions-grub b/tests/expected/blkid/iso-partitions-grub new file mode 100644 index 000000000..9a403a86a --- /dev/null +++ b/tests/expected/blkid/iso-partitions-grub @@ -0,0 +1,10 @@ +ID_FS_BLOCK_SIZE=2048 +ID_FS_UUID=2019-12-16-06-07-35-00 +ID_FS_UUID_ENC=2019-12-16-06-07-35-00 +ID_FS_BOOT_SYSTEM_ID=EL TORITO SPECIFICATION +ID_FS_LABEL=ISOIMAGE +ID_FS_LABEL_ENC=ISOIMAGE +ID_FS_TYPE=iso9660 +ID_FS_USAGE=filesystem +ID_PART_TABLE_UUID=4b46ece2-6efb-4bf9-90a6-858c95e5581d +ID_PART_TABLE_TYPE=gpt diff --git a/tests/ts/blkid/iso-partitions b/tests/ts/blkid/iso-partitions index 4aaa164f7..cfa0a93ff 100755 --- a/tests/ts/blkid/iso-partitions +++ b/tests/ts/blkid/iso-partitions @@ -69,4 +69,23 @@ udevadm settle $TS_CMD_BLKID -p -o udev ${TS_DEVICE} >> $TS_OUTPUT ts_finalize_subtest +# This image (created by grub-mkrescue) has a partition table where the +# partitions do not point to the ISO9660 filesystem. +xz -dc ${TS_SELF}/iso-partitions-grub.img.xz > ${TS_DEVICE} +udevadm settle + +ts_init_subtest "grub" +$TS_CMD_PARTX -a ${TS_DEVICE} &>/dev/null +udevadm settle + +# Check that the ISO metadata is shown on the main disk device +$TS_CMD_BLKID -p -o udev ${TS_DEVICE} >> $TS_OUTPUT + +# substitute major/minor number before comparison +sed -i \ + -e 's/^\(ID_PART_ENTRY_DISK\)=.*/\1=__ts_majorminor__/' \ + $TS_OUTPUT + +ts_finalize_subtest + ts_finalize diff --git a/tests/ts/blkid/iso-partitions-grub.img.xz b/tests/ts/blkid/iso-partitions-grub.img.xz new file mode 100644 index 000000000..7fe886a3f Binary files /dev/null and b/tests/ts/blkid/iso-partitions-grub.img.xz differ