]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
libblkid: improve handling of ISO files with partition tables
authorDaniel Drake <drake@endlessm.com>
Tue, 24 Sep 2019 09:46:16 +0000 (17:46 +0800)
committerDaniel Drake <drake@endlessm.com>
Wed, 25 Sep 2019 03:41:30 +0000 (11:41 +0800)
The ISO format specifically leaves the first 32kb blank so that it
can be used for other purposes, such as adding a partition table.
This is commonly used (e.g. by Endless and Fedora installation media) to
have partition 0 starting at sector 0 as a mountable iso9660 filesystem,
followed by more partitions (e.g. an EFI system partition).
Such layouts can be easily created by tools such as xorriso.

When plugging in a USB disk flashed with this type of ISO, blkid presents
a somewhat confusing view of the block devices. Taking the example of
a 'sda' disk with two partitions:
 1. The "iso partition"
 2. An unformatted partition

In such a setup, before the changes here, blkid will currently report the
ISO metadata attributes ID_FS_PUBLISHER_ID, ID_FS_UUID, ID_FS_LABEL, and
ID_FS_TYPE=iso9660 on both sda *and* sda1.

Since sda2 is unformatted, it won't have any ID_FS_ attributes of it's
own. And due to the following standard udev rule:

  # for partitions import parent information
  ENV{DEVTYPE}=="partition", IMPORT{parent}="ID_*"

sda2 will actually import all of the ID_FS_ stuff from the parent device
sda.

The result at this point is that three udev devices all have the same
ID_FS_ attribute values, leading to strange results such as three
devices all racing to own the link in /dev/disk/by-uuid, so you can't
reliably do a mount-by-UUID.

Clean up this situation by detecting such partitioned ISO disks
in the superblock probing setup. If files of this kind are detected,
we now only expose the ISO metadata attributes on the specific partition
that points to the ISO data (and not the parent disk).

Signed-off-by: Daniel Drake <drake@endlessm.com>
libblkid/src/superblocks/iso9660.c
tests/expected/blkid/iso-partitions-no_partitions [new file with mode: 0644]
tests/expected/blkid/iso-partitions-partitions [new file with mode: 0644]
tests/ts/blkid/iso-partitions [new file with mode: 0755]
tests/ts/blkid/iso-partitions.img.xz [new file with mode: 0644]

index 8dc2e53948f7840ab72dee88dbeb49308d617bbf..730939f7064814ab9610d44719bcbe6de4a0d6ba 100644 (file)
@@ -13,6 +13,7 @@
  */
 #include <stdio.h>
 #include <stdlib.h>
+#include <stdbool.h>
 #include <unistd.h>
 #include <string.h>
 #include <stdint.h>
@@ -165,6 +166,37 @@ static int is_str_empty(const unsigned char *str, size_t len)
        return 1;
 }
 
+/*
+ * 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,
+ * 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)
+{
+       dev_t devno;
+       blkid_partlist ls;
+
+       /* Get device number, but if that fails, assume we aren't dealing
+        * with partitions, and continue probing. */
+       devno = blkid_probe_get_devno(pr);
+       if (!devno)
+               return true;
+
+       /* Get partition table, but if that fails, assume we aren't dealing
+        * with partitions, and continue probing. */
+       ls = blkid_probe_get_partitions(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;
+}
+
 /* iso9660 [+ Microsoft Joliet Extension] */
 static int probe_iso9660(blkid_probe pr, const struct blkid_idmag *mag)
 {
@@ -180,6 +212,11 @@ static int probe_iso9660(blkid_probe pr, const struct blkid_idmag *mag)
        if (!iso)
                return errno ? -errno : 1;
 
+       /* Check if the iso metadata should be returned on a different device
+        * instead of this one. */
+       if (!isofs_belongs_to_device(pr))
+               return 1;
+
        memcpy(label, iso->volume_id, sizeof(label));
 
        blkid_probe_set_block_size(pr, 2048);
diff --git a/tests/expected/blkid/iso-partitions-no_partitions b/tests/expected/blkid/iso-partitions-no_partitions
new file mode 100644 (file)
index 0000000..dbfd57a
--- /dev/null
@@ -0,0 +1,9 @@
+ID_FS_BLOCK_SIZE=2048
+ID_FS_PUBLISHER_ID=UTIL-LINUX
+ID_FS_UUID=2019-09-24-09-31-05-00
+ID_FS_UUID_ENC=2019-09-24-09-31-05-00
+ID_FS_VERSION=Joliet\x20Extension
+ID_FS_LABEL=ISOIMAGE
+ID_FS_LABEL_ENC=ISOIMAGE
+ID_FS_TYPE=iso9660
+ID_FS_USAGE=filesystem
diff --git a/tests/expected/blkid/iso-partitions-partitions b/tests/expected/blkid/iso-partitions-partitions
new file mode 100644 (file)
index 0000000..e34ca39
--- /dev/null
@@ -0,0 +1,25 @@
+ID_PART_TABLE_TYPE=dos
+--
+ID_FS_BLOCK_SIZE=2048
+ID_FS_PUBLISHER_ID=UTIL-LINUX
+ID_FS_UUID=2019-09-24-09-31-05-00
+ID_FS_UUID_ENC=2019-09-24-09-31-05-00
+ID_FS_VERSION=Joliet\x20Extension
+ID_FS_LABEL=ISOIMAGE
+ID_FS_LABEL_ENC=ISOIMAGE
+ID_FS_TYPE=iso9660
+ID_FS_USAGE=filesystem
+ID_PART_TABLE_TYPE=dos
+ID_PART_ENTRY_SCHEME=dos
+ID_PART_ENTRY_TYPE=0x83
+ID_PART_ENTRY_NUMBER=1
+ID_PART_ENTRY_OFFSET=0
+ID_PART_ENTRY_SIZE=136
+ID_PART_ENTRY_DISK=__ts_majorminor__
+--
+ID_PART_ENTRY_SCHEME=dos
+ID_PART_ENTRY_TYPE=0xef
+ID_PART_ENTRY_NUMBER=2
+ID_PART_ENTRY_OFFSET=136
+ID_PART_ENTRY_SIZE=4096
+ID_PART_ENTRY_DISK=__ts_majorminor__
diff --git a/tests/ts/blkid/iso-partitions b/tests/ts/blkid/iso-partitions
new file mode 100755 (executable)
index 0000000..02fdd4e
--- /dev/null
@@ -0,0 +1,71 @@
+#!/bin/bash
+
+#
+# Copyright (C) 2019 Endless Mobile, Inc.
+#
+# This file is part of util-linux.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This file is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+
+TS_TOPDIR="${0%/*}/../.."
+TS_DESC="iso-partitions"
+
+. $TS_TOPDIR/functions.sh
+
+ts_init "$*"
+
+ts_check_test_command "$TS_CMD_BLKID"
+ts_check_test_command "$TS_CMD_PARTX"
+
+ts_skip_nonroot
+
+# set global variable TS_DEVICE
+ts_scsi_debug_init dev_size_mb=50
+
+# This image (created by xorriso) has partition 1 pointing to the ISO
+# area, followed by an unformatted second partition.
+xz -dc ${TS_SELF}/iso-partitions.img.xz > ${TS_DEVICE}
+udevadm settle
+
+ts_init_subtest "partitions"
+$TS_CMD_PARTX -a ${TS_DEVICE} &>/dev/null
+udevadm settle
+
+# Check that the ISO metadata is not shown on the main disk device
+$TS_CMD_BLKID -p -o udev ${TS_DEVICE} >> $TS_OUTPUT
+echo -- >> $TS_OUTPUT
+
+# Check that the ISO metadata is shown on the "ISO partition"
+$TS_CMD_BLKID -p -o udev ${TS_DEVICE}1 >> $TS_OUTPUT
+echo -- >> $TS_OUTPUT
+
+# Check that the ISO metadata is not shown on the other partition
+$TS_CMD_BLKID -p -o udev ${TS_DEVICE}2 >> $TS_OUTPUT
+
+# substitute major/minor number before comparison
+sed -i \
+  -e 's/^\(ID_PART_ENTRY_DISK\)=.*/\1=__ts_majorminor__/' \
+  $TS_OUTPUT
+
+ts_finalize_subtest
+
+# Remove the partition table and check that the ISO metadata is shown on the
+# main disk device.
+ts_init_subtest "no_partitions"
+dd if=/dev/zero of=${TS_DEVICE} bs=512 count=1 &>/dev/null
+udevadm settle
+$TS_CMD_PARTX -d ${TS_DEVICE} &>/dev/null
+udevadm settle
+$TS_CMD_BLKID -p -o udev ${TS_DEVICE} >> $TS_OUTPUT
+ts_finalize_subtest
+
+ts_finalize
diff --git a/tests/ts/blkid/iso-partitions.img.xz b/tests/ts/blkid/iso-partitions.img.xz
new file mode 100644 (file)
index 0000000..4eb3dd1
Binary files /dev/null and b/tests/ts/blkid/iso-partitions.img.xz differ