summaryrefslogtreecommitdiffstats
path: root/libblkid/src/superblocks/udf.c
diff options
context:
space:
mode:
authorPali Rohár2017-11-12 21:55:21 +0100
committerPali Rohár2017-11-12 21:55:21 +0100
commitf5f269ece5a98f99a52e1dfa5e4d61fee83bf242 (patch)
tree630e8ff8440a51c2d673c0cfa4dbd3dfddd36b5f /libblkid/src/superblocks/udf.c
parentnsenter: revert changes committed by accident (diff)
downloadkernel-qcow2-util-linux-f5f269ece5a98f99a52e1dfa5e4d61fee83bf242.tar.gz
kernel-qcow2-util-linux-f5f269ece5a98f99a52e1dfa5e4d61fee83bf242.tar.xz
kernel-qcow2-util-linux-f5f269ece5a98f99a52e1dfa5e4d61fee83bf242.zip
libblkid: udf: Optimize and fix probing when block size > 2048 bytes
Optimize probing and detecting for UDF. Do not read and try to detect VRS (Volume Recognition Sequence) on same blocks more times. For specific VSD (Volume Structure Descriptor) length do it only once. Fix probing of devices which has block size larger then 2048 bytes. It is not truth that VSD is always 2048 bytes long. Its size is minimum of the disk block size and 2048 bytes. See ECMA-167 sections 2/8.4 and 2/9.1. Therefore when block size is larger then 2048 bytes, VRS needs to be scanned again. In commit 501aeb60a4914d8e4b273eb1529d70bc6ffaa077 was removed check for empty VSD identifier because it caused that UDF image with block size of the 4096 bytes was not detected. Reason was that VRS was improperly scanned as VSD was 4096 bytes long, with 2048 bytes zero padding. Now when processing of devices with block size larger then 2048 bytes is fixed we can correctly stop scanning VRS at first invalid VSD as specified in ECMA-167 section 2/8.3.1.
Diffstat (limited to 'libblkid/src/superblocks/udf.c')
-rw-r--r--libblkid/src/superblocks/udf.c101
1 files changed, 64 insertions, 37 deletions
diff --git a/libblkid/src/superblocks/udf.c b/libblkid/src/superblocks/udf.c
index 0c4bca12a..73bf9a8af 100644
--- a/libblkid/src/superblocks/udf.c
+++ b/libblkid/src/superblocks/udf.c
@@ -167,12 +167,13 @@ static int probe_udf(blkid_probe pr,
uint32_t lvid_count = 0;
uint32_t lvid_loc = 0;
uint32_t bs;
- uint32_t pbs[5];
uint32_t b;
uint16_t type;
uint32_t count;
uint32_t loc;
size_t i;
+ uint32_t vsd_len;
+ int vsd_2048_valid = -1;
int have_label = 0;
int have_uuid = 0;
int have_logvolid = 0;
@@ -183,55 +184,81 @@ static int probe_udf(blkid_probe pr,
/* The block size of a UDF filesystem is that of the underlying
* storage; we check later on for the special case of image files,
* which may have any block size valid for UDF filesystem */
+ uint32_t pbs[] = { 0, 512, 1024, 2048, 4096 };
pbs[0] = blkid_probe_get_sectorsize(pr);
- pbs[1] = 512;
- pbs[2] = 1024;
- pbs[3] = 2048;
- pbs[4] = 4096;
-
- /* check for a Volume Structure Descriptor (VSD); each is
- * 2048 bytes long */
- for (b = 0; b < 0x8000; b += 0x800) {
- vsd = (struct volume_structure_descriptor *)
- blkid_probe_get_buffer(pr,
- UDF_VSD_OFFSET + b,
- sizeof(*vsd));
- if (!vsd)
- return errno ? -errno : 1;
- if (vsd->id[0] != '\0')
- goto nsr;
- }
- return 1;
-nsr:
- /* search the list of VSDs for a NSR descriptor */
- for (b = 0; b < 64; b++) {
- vsd = (struct volume_structure_descriptor *)
- blkid_probe_get_buffer(pr,
- UDF_VSD_OFFSET + ((uint64_t) b * 0x800),
- sizeof(*vsd));
- if (!vsd)
- return errno ? -errno : 1;
- if (memcmp(vsd->id, "NSR02", 5) == 0)
- goto anchor;
- if (memcmp(vsd->id, "NSR03", 5) == 0)
- goto anchor;
- }
- return 1;
+ for (i = 0; i < ARRAY_SIZE(pbs); i++) {
+ /* Do not try with block size same as sector size two times */
+ if (i != 0 && pbs[0] == pbs[i])
+ continue;
+
+ /* ECMA-167 2/8.4, 2/9.1: Each VSD is either 2048 bytes long or
+ * its size is same as blocksize (for blocksize > 2048 bytes)
+ * plus padded with zeros */
+ vsd_len = pbs[i] > 2048 ? pbs[i] : 2048;
+
+ /* Process 2048 bytes long VSD only once */
+ if (vsd_len == 2048) {
+ if (vsd_2048_valid == 0)
+ continue;
+ else if (vsd_2048_valid == 1)
+ goto anchor;
+ }
+
+ /* Check for a Volume Structure Descriptor (VSD) */
+ for (b = 0; b < 64; b++) {
+ vsd = (struct volume_structure_descriptor *)
+ blkid_probe_get_buffer(pr,
+ UDF_VSD_OFFSET + b * vsd_len,
+ sizeof(*vsd));
+ if (!vsd)
+ return errno ? -errno : 1;
+ if (vsd->id[0] == '\0')
+ break;
+ if (memcmp(vsd->id, "NSR02", 5) == 0 ||
+ memcmp(vsd->id, "NSR03", 5) == 0)
+ goto anchor;
+ else if (memcmp(vsd->id, "BEA01", 5) != 0 &&
+ memcmp(vsd->id, "BOOT2", 5) != 0 &&
+ memcmp(vsd->id, "CD001", 5) != 0 &&
+ memcmp(vsd->id, "CDW02", 5) != 0 &&
+ memcmp(vsd->id, "TEA01", 5) != 0)
+ /* ECMA-167 2/8.3.1: The volume recognition sequence is
+ * terminated by the first sector which is not a valid
+ * descriptor.
+ * UDF-2.60 2.1.7: UDF 2.00 and lower revisions do not
+ * have requirement that NSR descritor is in Extended Area
+ * (between BEA01 and TEA01) and that there is only one
+ * Extended Area. So do not stop scanning after TEA01. */
+ break;
+ }
+
+ if (vsd_len == 2048)
+ vsd_2048_valid = 0;
+
+ /* NSR was not found, try with next block size */
+ continue;
anchor:
- /* read Anchor Volume Descriptor (AVDP), checking block size */
- for (i = 0; i < ARRAY_SIZE(pbs); i++) {
+ if (vsd_len == 2048)
+ vsd_2048_valid = 1;
+
+ /* Read Anchor Volume Descriptor (AVDP), detect block size */
vd = (struct volume_descriptor *)
blkid_probe_get_buffer(pr, 256 * pbs[i], sizeof(*vd));
if (!vd)
return errno ? -errno : 1;
+ /* Check that we read correct sector and detected correct block size */
+ if (le32_to_cpu(vd->tag.location) != 256)
+ continue;
+
type = le16_to_cpu(vd->tag.id);
if (type == TAG_ID_AVDP)
goto real_blksz;
+
}
- return 0;
+ return 1;
real_blksz:
/* Use the actual block size from here on out */