Skip to content

Commit

Permalink
udf: Fix handling of Partition Descriptors
Browse files Browse the repository at this point in the history
Current handling of Partition Descriptors in Volume Descriptor Sequence
is buggy in several ways. Firstly, it does not take descriptor sequence
numbers into account at all, thus any volume making serious use of them
would be unmountable. Secondly, it does not handle Volume Descriptor
Pointers or Volume Descriptor Sequence without Terminating Descriptor.

Fix these problems by properly remembering all Partition Descriptors in
the Volume Descriptor Sequence and their sequence numbers. This is made
more complicated by the fact that we don't know number of partitions in
advance and sequence numbers have to be tracked on per-partition basis.

Reported-by: Pali Rohár <pali.rohar@gmail.com>
Acked-by: Pali Rohár <pali.rohar@gmail.com>
Signed-off-by: Jan Kara <jack@suse.cz>
  • Loading branch information
jankara committed Feb 27, 2018
1 parent 18cf478 commit 7b78fd0
Showing 1 changed file with 73 additions and 32 deletions.
105 changes: 73 additions & 32 deletions fs/udf/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,7 @@ enum {
VDS_POS_PRIMARY_VOL_DESC,
VDS_POS_UNALLOC_SPACE_DESC,
VDS_POS_LOGICAL_VOL_DESC,
VDS_POS_PARTITION_DESC,
VDS_POS_IMP_USE_VOL_DESC,
VDS_POS_TERMINATING_DESC,
VDS_POS_LENGTH
};

Expand Down Expand Up @@ -1593,18 +1591,57 @@ static void udf_load_logicalvolint(struct super_block *sb, struct kernel_extent_
sbi->s_lvid_bh = NULL;
}

static struct udf_vds_record *get_volume_descriptor_record(
struct udf_vds_record *vds, uint16_t ident)
/*
* Step for reallocation of table of partition descriptor sequence numbers.
* Must be power of 2.
*/
#define PART_DESC_ALLOC_STEP 32

struct desc_seq_scan_data {
struct udf_vds_record vds[VDS_POS_LENGTH];
unsigned int size_part_descs;
struct udf_vds_record *part_descs_loc;
};

static struct udf_vds_record *handle_partition_descriptor(
struct buffer_head *bh,
struct desc_seq_scan_data *data)
{
struct partitionDesc *desc = (struct partitionDesc *)bh->b_data;
int partnum;

partnum = le16_to_cpu(desc->partitionNumber);
if (partnum >= data->size_part_descs) {
struct udf_vds_record *new_loc;
unsigned int new_size = ALIGN(partnum, PART_DESC_ALLOC_STEP);

new_loc = kzalloc(sizeof(*new_loc) * new_size, GFP_KERNEL);
if (!new_loc)
return ERR_PTR(-ENOMEM);
memcpy(new_loc, data->part_descs_loc,
data->size_part_descs * sizeof(*new_loc));
kfree(data->part_descs_loc);
data->part_descs_loc = new_loc;
data->size_part_descs = new_size;
}
return &(data->part_descs_loc[partnum]);
}


static struct udf_vds_record *get_volume_descriptor_record(uint16_t ident,
struct buffer_head *bh, struct desc_seq_scan_data *data)
{
switch (ident) {
case TAG_IDENT_PVD: /* ISO 13346 3/10.1 */
return &vds[VDS_POS_PRIMARY_VOL_DESC];
return &(data->vds[VDS_POS_PRIMARY_VOL_DESC]);
case TAG_IDENT_IUVD: /* ISO 13346 3/10.4 */
return &vds[VDS_POS_IMP_USE_VOL_DESC];
return &(data->vds[VDS_POS_IMP_USE_VOL_DESC]);
case TAG_IDENT_LVD: /* ISO 13346 3/10.6 */
return &vds[VDS_POS_LOGICAL_VOL_DESC];
return &(data->vds[VDS_POS_LOGICAL_VOL_DESC]);
case TAG_IDENT_USD: /* ISO 13346 3/10.8 */
return &vds[VDS_POS_UNALLOC_SPACE_DESC];
return &(data->vds[VDS_POS_UNALLOC_SPACE_DESC]);
case TAG_IDENT_PD: /* ISO 13346 3/10.5 */
return handle_partition_descriptor(bh, data);
}
return NULL;
}
Expand All @@ -1624,7 +1661,6 @@ static noinline int udf_process_sequence(
struct kernel_lb_addr *fileset)
{
struct buffer_head *bh = NULL;
struct udf_vds_record vds[VDS_POS_LENGTH];
struct udf_vds_record *curr;
struct generic_desc *gd;
struct volDescPtr *vdp;
Expand All @@ -1633,8 +1669,15 @@ static noinline int udf_process_sequence(
uint16_t ident;
int ret;
unsigned int indirections = 0;

memset(vds, 0, sizeof(struct udf_vds_record) * VDS_POS_LENGTH);
struct desc_seq_scan_data data;
unsigned int i;

memset(data.vds, 0, sizeof(struct udf_vds_record) * VDS_POS_LENGTH);
data.size_part_descs = PART_DESC_ALLOC_STEP;
data.part_descs_loc = kzalloc(sizeof(*data.part_descs_loc) *
data.size_part_descs, GFP_KERNEL);
if (!data.part_descs_loc)
return -ENOMEM;

/*
* Read the main descriptor sequence and find which descriptors
Expand Down Expand Up @@ -1672,19 +1715,21 @@ static noinline int udf_process_sequence(
case TAG_IDENT_IUVD: /* ISO 13346 3/10.4 */
case TAG_IDENT_LVD: /* ISO 13346 3/10.6 */
case TAG_IDENT_USD: /* ISO 13346 3/10.8 */
curr = get_volume_descriptor_record(vds, ident);
case TAG_IDENT_PD: /* ISO 13346 3/10.5 */
curr = get_volume_descriptor_record(ident, bh, &data);
if (IS_ERR(curr)) {
brelse(bh);
return PTR_ERR(curr);
}
/* Descriptor we don't care about? */
if (!curr)
break;
if (vdsn >= curr->volDescSeqNum) {
curr->volDescSeqNum = vdsn;
curr->block = block;
}
break;
case TAG_IDENT_PD: /* ISO 13346 3/10.5 */
curr = &vds[VDS_POS_PARTITION_DESC];
if (!curr->block)
curr->block = block;
break;
case TAG_IDENT_TD: /* ISO 13346 3/10.9 */
vds[VDS_POS_TERMINATING_DESC].block = block;
done = true;
break;
}
Expand All @@ -1694,31 +1739,27 @@ static noinline int udf_process_sequence(
* Now read interesting descriptors again and process them
* in a suitable order
*/
if (!vds[VDS_POS_PRIMARY_VOL_DESC].block) {
if (!data.vds[VDS_POS_PRIMARY_VOL_DESC].block) {
udf_err(sb, "Primary Volume Descriptor not found!\n");
return -EAGAIN;
}
ret = udf_load_pvoldesc(sb, vds[VDS_POS_PRIMARY_VOL_DESC].block);
ret = udf_load_pvoldesc(sb, data.vds[VDS_POS_PRIMARY_VOL_DESC].block);
if (ret < 0)
return ret;

if (vds[VDS_POS_LOGICAL_VOL_DESC].block) {
if (data.vds[VDS_POS_LOGICAL_VOL_DESC].block) {
ret = udf_load_logicalvol(sb,
vds[VDS_POS_LOGICAL_VOL_DESC].block,
fileset);
data.vds[VDS_POS_LOGICAL_VOL_DESC].block,
fileset);
if (ret < 0)
return ret;
}

if (vds[VDS_POS_PARTITION_DESC].block) {
/*
* We rescan the whole descriptor sequence to find
* partition descriptor blocks and process them.
*/
for (block = vds[VDS_POS_PARTITION_DESC].block;
block < vds[VDS_POS_TERMINATING_DESC].block;
block++) {
ret = udf_load_partdesc(sb, block);
/* Now handle prevailing Partition Descriptors */
for (i = 0; i < data.size_part_descs; i++) {
if (data.part_descs_loc[i].block) {
ret = udf_load_partdesc(sb,
data.part_descs_loc[i].block);
if (ret < 0)
return ret;
}
Expand Down

0 comments on commit 7b78fd0

Please sign in to comment.