Commit 44499602 authored by Peter A. Felvegi's avatar Peter A. Felvegi Committed by Jan Kara

udf: fix for pathetic mount times in case of invalid file system

The UDF driver was not strict enough about checking the IDs in the
VSDs when mounting, which resulted in reading through all the sectors
of the block device in some unfortunate cases. Eg, trying to mount my
uninitialized 200G SSD partition (all 0xFF bytes) took ~350 minutes to
fail, because the code expected some of the valid IDs or a zero byte.
During this, the mount couldn't be killed, sync from the cmdline
blocked, and the machine froze into the shutdown. Valid filesystems
(extX, btrfs, ntfs) were rejected by the mere accident of having a
zero byte at just the right place in some of their sectors, close
enough to the beginning not to generate excess I/O. The fix adds a
hard limit on the VSD sector offset, adds the two missing VSD IDs, and
stops scanning when encountering an invalid ID. Also replaced the
magic number 32768 with a more meaningful #define, and supressed the
bogus message about failing to read the first sector if no UDF fs was
detected.
Signed-off-by: default avatarPeter A. Felvegi <petschy@gmail.com>
Signed-off-by: default avatarJan Kara <jack@suse.cz>
parent 2046fd18
...@@ -76,6 +76,9 @@ ...@@ -76,6 +76,9 @@
#define UDF_DEFAULT_BLOCKSIZE 2048 #define UDF_DEFAULT_BLOCKSIZE 2048
#define VSD_FIRST_SECTOR_OFFSET 32768
#define VSD_MAX_SECTOR_OFFSET 0x800000
enum { UDF_MAX_LINKS = 0xffff }; enum { UDF_MAX_LINKS = 0xffff };
/* These are the "meat" - everything else is stuffing */ /* These are the "meat" - everything else is stuffing */
...@@ -685,7 +688,7 @@ static int udf_remount_fs(struct super_block *sb, int *flags, char *options) ...@@ -685,7 +688,7 @@ static int udf_remount_fs(struct super_block *sb, int *flags, char *options)
static loff_t udf_check_vsd(struct super_block *sb) static loff_t udf_check_vsd(struct super_block *sb)
{ {
struct volStructDesc *vsd = NULL; struct volStructDesc *vsd = NULL;
loff_t sector = 32768; loff_t sector = VSD_FIRST_SECTOR_OFFSET;
int sectorsize; int sectorsize;
struct buffer_head *bh = NULL; struct buffer_head *bh = NULL;
int nsr02 = 0; int nsr02 = 0;
...@@ -703,8 +706,18 @@ static loff_t udf_check_vsd(struct super_block *sb) ...@@ -703,8 +706,18 @@ static loff_t udf_check_vsd(struct super_block *sb)
udf_debug("Starting at sector %u (%ld byte sectors)\n", udf_debug("Starting at sector %u (%ld byte sectors)\n",
(unsigned int)(sector >> sb->s_blocksize_bits), (unsigned int)(sector >> sb->s_blocksize_bits),
sb->s_blocksize); sb->s_blocksize);
/* Process the sequence (if applicable) */ /* Process the sequence (if applicable). The hard limit on the sector
for (; !nsr02 && !nsr03; sector += sectorsize) { * offset is arbitrary, hopefully large enough so that all valid UDF
* filesystems will be recognised. There is no mention of an upper
* bound to the size of the volume recognition area in the standard.
* The limit will prevent the code to read all the sectors of a
* specially crafted image (like a bluray disc full of CD001 sectors),
* potentially causing minutes or even hours of uninterruptible I/O
* activity. This actually happened with uninitialised SSD partitions
* (all 0xFF) before the check for the limit and all valid IDs were
* added */
for (; !nsr02 && !nsr03 && sector < VSD_MAX_SECTOR_OFFSET;
sector += sectorsize) {
/* Read a block */ /* Read a block */
bh = udf_tread(sb, sector >> sb->s_blocksize_bits); bh = udf_tread(sb, sector >> sb->s_blocksize_bits);
if (!bh) if (!bh)
...@@ -714,10 +727,7 @@ static loff_t udf_check_vsd(struct super_block *sb) ...@@ -714,10 +727,7 @@ static loff_t udf_check_vsd(struct super_block *sb)
vsd = (struct volStructDesc *)(bh->b_data + vsd = (struct volStructDesc *)(bh->b_data +
(sector & (sb->s_blocksize - 1))); (sector & (sb->s_blocksize - 1)));
if (vsd->stdIdent[0] == 0) { if (!strncmp(vsd->stdIdent, VSD_STD_ID_CD001,
brelse(bh);
break;
} else if (!strncmp(vsd->stdIdent, VSD_STD_ID_CD001,
VSD_STD_ID_LEN)) { VSD_STD_ID_LEN)) {
switch (vsd->structType) { switch (vsd->structType) {
case 0: case 0:
...@@ -753,6 +763,17 @@ static loff_t udf_check_vsd(struct super_block *sb) ...@@ -753,6 +763,17 @@ static loff_t udf_check_vsd(struct super_block *sb)
else if (!strncmp(vsd->stdIdent, VSD_STD_ID_NSR03, else if (!strncmp(vsd->stdIdent, VSD_STD_ID_NSR03,
VSD_STD_ID_LEN)) VSD_STD_ID_LEN))
nsr03 = sector; nsr03 = sector;
else if (!strncmp(vsd->stdIdent, VSD_STD_ID_BOOT2,
VSD_STD_ID_LEN))
; /* nothing */
else if (!strncmp(vsd->stdIdent, VSD_STD_ID_CDW02,
VSD_STD_ID_LEN))
; /* nothing */
else {
/* invalid id : end of volume recognition area */
brelse(bh);
break;
}
brelse(bh); brelse(bh);
} }
...@@ -760,7 +781,8 @@ static loff_t udf_check_vsd(struct super_block *sb) ...@@ -760,7 +781,8 @@ static loff_t udf_check_vsd(struct super_block *sb)
return nsr03; return nsr03;
else if (nsr02) else if (nsr02)
return nsr02; return nsr02;
else if (sector - (sbi->s_session << sb->s_blocksize_bits) == 32768) else if (!bh && sector - (sbi->s_session << sb->s_blocksize_bits) ==
VSD_FIRST_SECTOR_OFFSET)
return -1; return -1;
else else
return 0; return 0;
...@@ -1270,6 +1292,9 @@ static int udf_load_partdesc(struct super_block *sb, sector_t block) ...@@ -1270,6 +1292,9 @@ static int udf_load_partdesc(struct super_block *sb, sector_t block)
* PHYSICAL partitions are already set up * PHYSICAL partitions are already set up
*/ */
type1_idx = i; type1_idx = i;
#ifdef UDFFS_DEBUG
map = NULL; /* supress 'maybe used uninitialized' warning */
#endif
for (i = 0; i < sbi->s_partitions; i++) { for (i = 0; i < sbi->s_partitions; i++) {
map = &sbi->s_partmaps[i]; map = &sbi->s_partmaps[i];
...@@ -1891,7 +1916,9 @@ static int udf_load_vrs(struct super_block *sb, struct udf_options *uopt, ...@@ -1891,7 +1916,9 @@ static int udf_load_vrs(struct super_block *sb, struct udf_options *uopt,
return 0; return 0;
} }
if (nsr_off == -1) if (nsr_off == -1)
udf_debug("Failed to read byte 32768. Assuming open disc. Skipping validity check\n"); udf_debug("Failed to read sector at offset %d. "
"Assuming open disc. Skipping validity "
"check\n", VSD_FIRST_SECTOR_OFFSET);
if (!sbi->s_last_block) if (!sbi->s_last_block)
sbi->s_last_block = udf_get_last_block(sb); sbi->s_last_block = udf_get_last_block(sb);
} else { } else {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment