udf: Fix handling of Partition Descriptors
authorJan Kara <jack@suse.cz>
Fri, 9 Feb 2018 14:56:34 +0000 (15:56 +0100)
committerJan Kara <jack@suse.cz>
Tue, 27 Feb 2018 09:25:33 +0000 (10:25 +0100)
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>
fs/udf/super.c

index 3e9925acd23509bbba4ee045afc129e1e0ff728e..28ee2ac2503d17a9042275a925488da8bc4e623c 100644 (file)
@@ -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
 };
 
@@ -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;
 }
@@ -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;
@@ -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
@@ -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;
                }
@@ -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;
                }