Commit 423cf6dc authored by Jan Kara's avatar Jan Kara

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: default avatarJan Kara <jack@suse.cz>
parent 38b74a53
...@@ -678,44 +678,45 @@ static int udf_vrs(struct super_block *sb, int silent) ...@@ -678,44 +678,45 @@ static int udf_vrs(struct super_block *sb, int silent)
} }
/* /*
* udf_find_anchor * Check whether there is an anchor block in the given block
*
* 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.
*/ */
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; struct buffer_head *bh = NULL;
tag *t;
uint16_t ident; uint16_t ident;
uint32_t location; uint32_t location;
int i;
struct udf_sb_info *sbi;
sbi = UDF_SB(sb); if (varconv)
lastblock = sbi->s_last_block; bh = sb_bread(sb, udf_fixed_to_variable(block));
else
bh = sb_bread(sb, block);
if (lastblock) { if (!bh)
int varlastblock = udf_variable_to_fixed(lastblock); return 0;
int last[] = { lastblock, lastblock - 2,
lastblock - 150, lastblock - 152,
varlastblock, varlastblock - 2,
varlastblock - 150, varlastblock - 152 };
lastblock = 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 */ /* 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 = UDF_SB(sb);
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: /* according to spec, anchor is in either:
* block 256 * block 256
...@@ -723,104 +724,74 @@ static void udf_find_anchor(struct super_block *sb) ...@@ -723,104 +724,74 @@ static void udf_find_anchor(struct super_block *sb)
* lastblock * lastblock
* however, if the disc isn't closed, it could be 512 */ * however, if the disc isn't closed, it could be 512 */
for (i = 0; !lastblock && i < ARRAY_SIZE(last); i++) { for (i = 0; i < ARRAY_SIZE(last); i++) {
ident = location = 0; if (last[i] < 0)
if (last[i] >= 0) { continue;
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);
}
}
if (ident == TAG_IDENT_AVDP) { if (udf_check_anchor_block(sb, last[i], varconv)) {
if (location == last[i] - sbi->s_session) { sbi->s_anchor[0] = last[i];
lastblock = last[i] - sbi->s_session; sbi->s_anchor[1] = last[i] - 256;
sbi->s_anchor[0] = lastblock; return last[i];
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 && if (last[i] < 256)
location == last[i] - 256 - continue;
sbi->s_session) {
lastblock = last[i]; if (udf_check_anchor_block(sb, last[i] - 256, varconv)) {
sbi->s_anchor[1] = last[i] - 256; sbi->s_anchor[1] = last[i] - 256;
} else { return last[i];
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);
} }
} }
if (ident == TAG_IDENT_AVDP && if (udf_check_anchor_block(sb, sbi->s_session + 256, varconv)) {
location == udf_variable_to_fixed(last[i]) - 256) { sbi->s_anchor[0] = sbi->s_session + 256;
UDF_SET_FLAG(sb, return last[0];
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 + 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 */ * Find an anchor volume descriptor. The function expects sbi->s_lastblock to
bh = sb_bread(sb, 312 + sbi->s_session); * be the last block on the media.
if (bh) { *
tag *t = (tag *)bh->b_data; * Return 1 if not found, 0 if ok
ident = le16_to_cpu(t->tagIdent); *
location = le32_to_cpu(t->tagLocation); */
brelse(bh); 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);
lastblock = udf_scan_anchors(sb, 0, sbi->s_last_block);
if (lastblock)
goto check_anchor;
if (ident == TAG_IDENT_AVDP && location == 256) /* 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); 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++) { for (i = 0; i < ARRAY_SIZE(sbi->s_anchor); i++) {
if (!sbi->s_anchor[i]) if (!sbi->s_anchor[i])
continue; continue;
...@@ -830,9 +801,7 @@ static void udf_find_anchor(struct super_block *sb) ...@@ -830,9 +801,7 @@ static void udf_find_anchor(struct super_block *sb)
sbi->s_anchor[i] = 0; sbi->s_anchor[i] = 0;
else { else {
brelse(bh); brelse(bh);
if ((ident != TAG_IDENT_AVDP) && if (ident != TAG_IDENT_AVDP)
(i || (ident != TAG_IDENT_FE &&
ident != TAG_IDENT_EFE)))
sbi->s_anchor[i] = 0; sbi->s_anchor[i] = 0;
} }
} }
...@@ -1225,17 +1194,6 @@ static int udf_load_partdesc(struct super_block *sb, sector_t block) ...@@ -1225,17 +1194,6 @@ static int udf_load_partdesc(struct super_block *sb, sector_t block)
if (ret) if (ret)
goto out_bh; 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); ret = udf_load_vat(sb, i, type1_idx);
out_bh: out_bh:
/* In case loading failed, we handle cleanup in udf_fill_super */ /* 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) ...@@ -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_last_block = uopt.lastblock;
sbi->s_anchor[0] = sbi->s_anchor[1] = 0; sbi->s_anchor[0] = sbi->s_anchor[1] = 0;
sbi->s_anchor[2] = uopt.anchor; sbi->s_anchor[2] = uopt.anchor;
sbi->s_anchor[3] = 256;
if (udf_check_valid(sb, uopt.novrs, silent)) { if (udf_check_valid(sb, uopt.novrs, silent)) {
/* read volume recognition sequences */ /* read volume recognition sequences */
......
...@@ -100,7 +100,7 @@ struct udf_sb_info { ...@@ -100,7 +100,7 @@ struct udf_sb_info {
/* Sector headers */ /* Sector headers */
__s32 s_session; __s32 s_session;
__u32 s_anchor[4]; __u32 s_anchor[3];
__u32 s_last_block; __u32 s_last_block;
struct buffer_head *s_lvid_bh; struct buffer_head *s_lvid_bh;
......
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