Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs

Pull UDF fixes from Jan Kara:
 "Make UDF more robust in presence of corrupted filesystem"

* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs:
  udf: Fortify loading of sparing table
  udf: Avoid run away loop when partition table length is corrupted
  udf: Use 'ret' instead of abusing 'i' in udf_load_logicalvol()
This commit is contained in:
Linus Torvalds 2012-06-28 11:43:45 -07:00
Родитель 9a7c6b73c4 1df2ae31c7
Коммит 221d3ebf3a
1 изменённых файлов: 64 добавлений и 38 удалений

Просмотреть файл

@ -56,6 +56,7 @@
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/bitmap.h> #include <linux/bitmap.h>
#include <linux/crc-itu-t.h> #include <linux/crc-itu-t.h>
#include <linux/log2.h>
#include <asm/byteorder.h> #include <asm/byteorder.h>
#include "udf_sb.h" #include "udf_sb.h"
@ -1215,16 +1216,65 @@ out_bh:
return ret; return ret;
} }
static int udf_load_sparable_map(struct super_block *sb,
struct udf_part_map *map,
struct sparablePartitionMap *spm)
{
uint32_t loc;
uint16_t ident;
struct sparingTable *st;
struct udf_sparing_data *sdata = &map->s_type_specific.s_sparing;
int i;
struct buffer_head *bh;
map->s_partition_type = UDF_SPARABLE_MAP15;
sdata->s_packet_len = le16_to_cpu(spm->packetLength);
if (!is_power_of_2(sdata->s_packet_len)) {
udf_err(sb, "error loading logical volume descriptor: "
"Invalid packet length %u\n",
(unsigned)sdata->s_packet_len);
return -EIO;
}
if (spm->numSparingTables > 4) {
udf_err(sb, "error loading logical volume descriptor: "
"Too many sparing tables (%d)\n",
(int)spm->numSparingTables);
return -EIO;
}
for (i = 0; i < spm->numSparingTables; i++) {
loc = le32_to_cpu(spm->locSparingTable[i]);
bh = udf_read_tagged(sb, loc, loc, &ident);
if (!bh)
continue;
st = (struct sparingTable *)bh->b_data;
if (ident != 0 ||
strncmp(st->sparingIdent.ident, UDF_ID_SPARING,
strlen(UDF_ID_SPARING)) ||
sizeof(*st) + le16_to_cpu(st->reallocationTableLen) >
sb->s_blocksize) {
brelse(bh);
continue;
}
sdata->s_spar_map[i] = bh;
}
map->s_partition_func = udf_get_pblock_spar15;
return 0;
}
static int udf_load_logicalvol(struct super_block *sb, sector_t block, static int udf_load_logicalvol(struct super_block *sb, sector_t block,
struct kernel_lb_addr *fileset) struct kernel_lb_addr *fileset)
{ {
struct logicalVolDesc *lvd; struct logicalVolDesc *lvd;
int i, j, offset; int i, offset;
uint8_t type; uint8_t type;
struct udf_sb_info *sbi = UDF_SB(sb); struct udf_sb_info *sbi = UDF_SB(sb);
struct genericPartitionMap *gpm; struct genericPartitionMap *gpm;
uint16_t ident; uint16_t ident;
struct buffer_head *bh; struct buffer_head *bh;
unsigned int table_len;
int ret = 0; int ret = 0;
bh = udf_read_tagged(sb, block, block, &ident); bh = udf_read_tagged(sb, block, block, &ident);
@ -1232,15 +1282,20 @@ static int udf_load_logicalvol(struct super_block *sb, sector_t block,
return 1; return 1;
BUG_ON(ident != TAG_IDENT_LVD); BUG_ON(ident != TAG_IDENT_LVD);
lvd = (struct logicalVolDesc *)bh->b_data; lvd = (struct logicalVolDesc *)bh->b_data;
table_len = le32_to_cpu(lvd->mapTableLength);
i = udf_sb_alloc_partition_maps(sb, le32_to_cpu(lvd->numPartitionMaps)); if (sizeof(*lvd) + table_len > sb->s_blocksize) {
if (i != 0) { udf_err(sb, "error loading logical volume descriptor: "
ret = i; "Partition table too long (%u > %lu)\n", table_len,
sb->s_blocksize - sizeof(*lvd));
goto out_bh; goto out_bh;
} }
ret = udf_sb_alloc_partition_maps(sb, le32_to_cpu(lvd->numPartitionMaps));
if (ret)
goto out_bh;
for (i = 0, offset = 0; for (i = 0, offset = 0;
i < sbi->s_partitions && offset < le32_to_cpu(lvd->mapTableLength); i < sbi->s_partitions && offset < table_len;
i++, offset += gpm->partitionMapLength) { i++, offset += gpm->partitionMapLength) {
struct udf_part_map *map = &sbi->s_partmaps[i]; struct udf_part_map *map = &sbi->s_partmaps[i];
gpm = (struct genericPartitionMap *) gpm = (struct genericPartitionMap *)
@ -1275,38 +1330,9 @@ static int udf_load_logicalvol(struct super_block *sb, sector_t block,
} else if (!strncmp(upm2->partIdent.ident, } else if (!strncmp(upm2->partIdent.ident,
UDF_ID_SPARABLE, UDF_ID_SPARABLE,
strlen(UDF_ID_SPARABLE))) { strlen(UDF_ID_SPARABLE))) {
uint32_t loc; if (udf_load_sparable_map(sb, map,
struct sparingTable *st; (struct sparablePartitionMap *)gpm) < 0)
struct sparablePartitionMap *spm = goto out_bh;
(struct sparablePartitionMap *)gpm;
map->s_partition_type = UDF_SPARABLE_MAP15;
map->s_type_specific.s_sparing.s_packet_len =
le16_to_cpu(spm->packetLength);
for (j = 0; j < spm->numSparingTables; j++) {
struct buffer_head *bh2;
loc = le32_to_cpu(
spm->locSparingTable[j]);
bh2 = udf_read_tagged(sb, loc, loc,
&ident);
map->s_type_specific.s_sparing.
s_spar_map[j] = bh2;
if (bh2 == NULL)
continue;
st = (struct sparingTable *)bh2->b_data;
if (ident != 0 || strncmp(
st->sparingIdent.ident,
UDF_ID_SPARING,
strlen(UDF_ID_SPARING))) {
brelse(bh2);
map->s_type_specific.s_sparing.
s_spar_map[j] = NULL;
}
}
map->s_partition_func = udf_get_pblock_spar15;
} else if (!strncmp(upm2->partIdent.ident, } else if (!strncmp(upm2->partIdent.ident,
UDF_ID_METADATA, UDF_ID_METADATA,
strlen(UDF_ID_METADATA))) { strlen(UDF_ID_METADATA))) {