udf: Cleanup anchor block detection.

UDF anchor block detection is complicated by several things - there are several
places where the anchor point can be, some of them relative to the last
recorded block which some devices report wrongly. Moreover some devices on some
media seem to have 7 spare blocks sectors for every 32 blocks (at least as far
as I understand the old code) so we have to count also with that possibility.

This patch splits anchor block detection into several functions so that it is
clearer what we actually try to do. We fix several bugs of the type "for such
and such media, we fail to check block blah" as a result of the cleanup.

Signed-off-by: Jan Kara <jack@suse.cz>
This commit is contained in:
Jan Kara 2008-04-07 15:59:23 +02:00
parent 38b74a53e5
commit 423cf6dc04
2 changed files with 102 additions and 145 deletions

View File

@ -678,44 +678,45 @@ static int udf_vrs(struct super_block *sb, int silent)
}
/*
* udf_find_anchor
*
* PURPOSE
* Find an anchor volume descriptor.
*
* PRE-CONDITIONS
* sb Pointer to _locked_ superblock.
* lastblock Last block on media.
*
* POST-CONDITIONS
* <return> 1 if not found, 0 if ok
*
* HISTORY
* July 1, 1997 - Andrew E. Mileski
* Written, tested, and released.
* Check whether there is an anchor block in the given block
*/
static void udf_find_anchor(struct super_block *sb)
static int udf_check_anchor_block(struct super_block *sb, sector_t block,
bool varconv)
{
int lastblock;
struct buffer_head *bh = NULL;
tag *t;
uint16_t ident;
uint32_t location;
if (varconv)
bh = sb_bread(sb, udf_fixed_to_variable(block));
else
bh = sb_bread(sb, block);
if (!bh)
return 0;
t = (tag *)bh->b_data;
ident = le16_to_cpu(t->tagIdent);
location = le32_to_cpu(t->tagLocation);
brelse(bh);
if (ident != TAG_IDENT_AVDP)
return 0;
return location == block;
}
/* Search for an anchor volume descriptor pointer */
static sector_t udf_scan_anchors(struct super_block *sb, bool varconv,
sector_t lastblock)
{
sector_t last[4];
int i;
struct udf_sb_info *sbi;
struct udf_sb_info *sbi = UDF_SB(sb);
sbi = UDF_SB(sb);
lastblock = sbi->s_last_block;
if (lastblock) {
int varlastblock = udf_variable_to_fixed(lastblock);
int last[] = { lastblock, lastblock - 2,
lastblock - 150, lastblock - 152,
varlastblock, varlastblock - 2,
varlastblock - 150, varlastblock - 152 };
lastblock = 0;
/* Search for an anchor volume descriptor pointer */
last[0] = lastblock;
last[1] = last[0] - 2;
last[2] = last[0] - 150;
last[3] = last[0] - 152;
/* according to spec, anchor is in either:
* block 256
@ -723,104 +724,74 @@ static void udf_find_anchor(struct super_block *sb)
* lastblock
* however, if the disc isn't closed, it could be 512 */
for (i = 0; !lastblock && i < ARRAY_SIZE(last); i++) {
ident = location = 0;
if (last[i] >= 0) {
bh = sb_bread(sb, last[i]);
if (bh) {
tag *t = (tag *)bh->b_data;
ident = le16_to_cpu(t->tagIdent);
location = le32_to_cpu(t->tagLocation);
brelse(bh);
}
}
for (i = 0; i < ARRAY_SIZE(last); i++) {
if (last[i] < 0)
continue;
if (ident == TAG_IDENT_AVDP) {
if (location == last[i] - sbi->s_session) {
lastblock = last[i] - sbi->s_session;
sbi->s_anchor[0] = lastblock;
sbi->s_anchor[1] = lastblock - 256;
} else if (location ==
udf_variable_to_fixed(last[i]) -
sbi->s_session) {
UDF_SET_FLAG(sb, UDF_FLAG_VARCONV);
lastblock =
udf_variable_to_fixed(last[i]) -
sbi->s_session;
sbi->s_anchor[0] = lastblock;
sbi->s_anchor[1] = lastblock - 256 -
sbi->s_session;
} else {
udf_debug("Anchor found at block %d, "
"location mismatch %d.\n",
last[i], location);
}
} else if (ident == TAG_IDENT_FE ||
ident == TAG_IDENT_EFE) {
lastblock = last[i];
sbi->s_anchor[3] = 512;
} else {
ident = location = 0;
if (last[i] >= 256) {
bh = sb_bread(sb, last[i] - 256);
if (bh) {
tag *t = (tag *)bh->b_data;
ident = le16_to_cpu(
t->tagIdent);
location = le32_to_cpu(
t->tagLocation);
brelse(bh);
}
}
if (ident == TAG_IDENT_AVDP &&
location == last[i] - 256 -
sbi->s_session) {
lastblock = last[i];
if (udf_check_anchor_block(sb, last[i], varconv)) {
sbi->s_anchor[0] = last[i];
sbi->s_anchor[1] = last[i] - 256;
} else {
ident = location = 0;
if (last[i] >= 312 + sbi->s_session) {
bh = sb_bread(sb,
last[i] - 312 -
sbi->s_session);
if (bh) {
tag *t = (tag *)
bh->b_data;
ident = le16_to_cpu(
t->tagIdent);
location = le32_to_cpu(
t->tagLocation);
brelse(bh);
return last[i];
}
if (last[i] < 256)
continue;
if (udf_check_anchor_block(sb, last[i] - 256, varconv)) {
sbi->s_anchor[1] = last[i] - 256;
return last[i];
}
}
if (ident == TAG_IDENT_AVDP &&
location == udf_variable_to_fixed(last[i]) - 256) {
UDF_SET_FLAG(sb,
UDF_FLAG_VARCONV);
lastblock = udf_variable_to_fixed(last[i]);
sbi->s_anchor[1] = lastblock - 256;
}
}
}
if (udf_check_anchor_block(sb, sbi->s_session + 256, varconv)) {
sbi->s_anchor[0] = sbi->s_session + 256;
return last[0];
}
if (udf_check_anchor_block(sb, sbi->s_session + 512, varconv)) {
sbi->s_anchor[0] = sbi->s_session + 512;
return last[0];
}
return 0;
}
if (!lastblock) {
/* We haven't found the lastblock. check 312 */
bh = sb_bread(sb, 312 + sbi->s_session);
if (bh) {
tag *t = (tag *)bh->b_data;
ident = le16_to_cpu(t->tagIdent);
location = le32_to_cpu(t->tagLocation);
brelse(bh);
/*
* Find an anchor volume descriptor. The function expects sbi->s_lastblock to
* be the last block on the media.
*
* Return 1 if not found, 0 if ok
*
*/
static void udf_find_anchor(struct super_block *sb)
{
sector_t lastblock;
struct buffer_head *bh = NULL;
uint16_t ident;
int i;
struct udf_sb_info *sbi = UDF_SB(sb);
if (ident == TAG_IDENT_AVDP && location == 256)
lastblock = udf_scan_anchors(sb, 0, sbi->s_last_block);
if (lastblock)
goto check_anchor;
/* No anchor found? Try VARCONV conversion of block numbers */
/* Firstly, we try to not convert number of the last block */
lastblock = udf_scan_anchors(sb, 1,
udf_variable_to_fixed(sbi->s_last_block));
if (lastblock) {
UDF_SET_FLAG(sb, UDF_FLAG_VARCONV);
}
goto check_anchor;
}
/* Secondly, we try with converted number of the last block */
lastblock = udf_scan_anchors(sb, 1, sbi->s_last_block);
if (lastblock)
UDF_SET_FLAG(sb, UDF_FLAG_VARCONV);
check_anchor:
/*
* Check located anchors and the anchor block supplied via
* mount options
*/
for (i = 0; i < ARRAY_SIZE(sbi->s_anchor); i++) {
if (!sbi->s_anchor[i])
continue;
@ -830,9 +801,7 @@ static void udf_find_anchor(struct super_block *sb)
sbi->s_anchor[i] = 0;
else {
brelse(bh);
if ((ident != TAG_IDENT_AVDP) &&
(i || (ident != TAG_IDENT_FE &&
ident != TAG_IDENT_EFE)))
if (ident != TAG_IDENT_AVDP)
sbi->s_anchor[i] = 0;
}
}
@ -1225,17 +1194,6 @@ static int udf_load_partdesc(struct super_block *sb, sector_t block)
if (ret)
goto out_bh;
if (!sbi->s_last_block) {
sbi->s_last_block = udf_get_last_block(sb);
udf_find_anchor(sb);
if (!sbi->s_last_block) {
udf_debug("Unable to determine Lastblock (For "
"Virtual Partition)\n");
ret = 1;
goto out_bh;
}
}
ret = udf_load_vat(sb, i, type1_idx);
out_bh:
/* In case loading failed, we handle cleanup in udf_fill_super */
@ -1778,7 +1736,6 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent)
sbi->s_last_block = uopt.lastblock;
sbi->s_anchor[0] = sbi->s_anchor[1] = 0;
sbi->s_anchor[2] = uopt.anchor;
sbi->s_anchor[3] = 256;
if (udf_check_valid(sb, uopt.novrs, silent)) {
/* read volume recognition sequences */

View File

@ -100,7 +100,7 @@ struct udf_sb_info {
/* Sector headers */
__s32 s_session;
__u32 s_anchor[4];
__u32 s_anchor[3];
__u32 s_last_block;
struct buffer_head *s_lvid_bh;