libblkid: improve handling of ISO files with partition tables

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>
This commit is contained in:
Daniel Drake 2019-09-24 17:46:16 +08:00
parent 775022bdd7
commit 7ef86a0891
5 changed files with 142 additions and 0 deletions

View 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);

View File

@ -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

View File

@ -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__

71
tests/ts/blkid/iso-partitions Executable file
View File

@ -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

Binary file not shown.