OSDN Git Service

power: supply: ltc2941-battery-gauge: fix use-after-free
[sagit-ice-cold/kernel_xiaomi_msm8998.git] / fs / udf / inode.c
index 8d0b3ad..3876448 100644 (file)
@@ -479,13 +479,15 @@ static struct buffer_head *udf_getblk(struct inode *inode, long block,
        return NULL;
 }
 
-/* Extend the file by 'blocks' blocks, return the number of extents added */
+/* Extend the file with new blocks totaling 'new_block_bytes',
+ * return the number of extents added
+ */
 static int udf_do_extend_file(struct inode *inode,
                              struct extent_position *last_pos,
                              struct kernel_long_ad *last_ext,
-                             sector_t blocks)
+                             loff_t new_block_bytes)
 {
-       sector_t add;
+       uint32_t add;
        int count = 0, fake = !(last_ext->extLength & UDF_EXTENT_LENGTH_MASK);
        struct super_block *sb = inode->i_sb;
        struct kernel_lb_addr prealloc_loc = {};
@@ -495,7 +497,7 @@ static int udf_do_extend_file(struct inode *inode,
 
        /* The previous extent is fake and we should not extend by anything
         * - there's nothing to do... */
-       if (!blocks && fake)
+       if (!new_block_bytes && fake)
                return 0;
 
        iinfo = UDF_I(inode);
@@ -526,13 +528,12 @@ static int udf_do_extend_file(struct inode *inode,
        /* Can we merge with the previous extent? */
        if ((last_ext->extLength & UDF_EXTENT_FLAG_MASK) ==
                                        EXT_NOT_RECORDED_NOT_ALLOCATED) {
-               add = ((1 << 30) - sb->s_blocksize -
-                       (last_ext->extLength & UDF_EXTENT_LENGTH_MASK)) >>
-                       sb->s_blocksize_bits;
-               if (add > blocks)
-                       add = blocks;
-               blocks -= add;
-               last_ext->extLength += add << sb->s_blocksize_bits;
+               add = (1 << 30) - sb->s_blocksize -
+                       (last_ext->extLength & UDF_EXTENT_LENGTH_MASK);
+               if (add > new_block_bytes)
+                       add = new_block_bytes;
+               new_block_bytes -= add;
+               last_ext->extLength += add;
        }
 
        if (fake) {
@@ -544,28 +545,27 @@ static int udf_do_extend_file(struct inode *inode,
                                last_ext->extLength, 1);
 
        /* Managed to do everything necessary? */
-       if (!blocks)
+       if (!new_block_bytes)
                goto out;
 
        /* All further extents will be NOT_RECORDED_NOT_ALLOCATED */
        last_ext->extLocation.logicalBlockNum = 0;
        last_ext->extLocation.partitionReferenceNum = 0;
-       add = (1 << (30-sb->s_blocksize_bits)) - 1;
-       last_ext->extLength = EXT_NOT_RECORDED_NOT_ALLOCATED |
-                               (add << sb->s_blocksize_bits);
+       add = (1 << 30) - sb->s_blocksize;
+       last_ext->extLength = EXT_NOT_RECORDED_NOT_ALLOCATED | add;
 
        /* Create enough extents to cover the whole hole */
-       while (blocks > add) {
-               blocks -= add;
+       while (new_block_bytes > add) {
+               new_block_bytes -= add;
                err = udf_add_aext(inode, last_pos, &last_ext->extLocation,
                                   last_ext->extLength, 1);
                if (err)
                        return err;
                count++;
        }
-       if (blocks) {
+       if (new_block_bytes) {
                last_ext->extLength = EXT_NOT_RECORDED_NOT_ALLOCATED |
-                       (blocks << sb->s_blocksize_bits);
+                       new_block_bytes;
                err = udf_add_aext(inode, last_pos, &last_ext->extLocation,
                                   last_ext->extLength, 1);
                if (err)
@@ -596,6 +596,24 @@ out:
        return count;
 }
 
+/* Extend the final block of the file to final_block_len bytes */
+static void udf_do_extend_final_block(struct inode *inode,
+                                     struct extent_position *last_pos,
+                                     struct kernel_long_ad *last_ext,
+                                     uint32_t final_block_len)
+{
+       struct super_block *sb = inode->i_sb;
+       uint32_t added_bytes;
+
+       added_bytes = final_block_len -
+                     (last_ext->extLength & (sb->s_blocksize - 1));
+       last_ext->extLength += added_bytes;
+       UDF_I(inode)->i_lenExtents += added_bytes;
+
+       udf_write_aext(inode, last_pos, &last_ext->extLocation,
+                       last_ext->extLength, 1);
+}
+
 static int udf_extend_file(struct inode *inode, loff_t newsize)
 {
 
@@ -605,10 +623,12 @@ static int udf_extend_file(struct inode *inode, loff_t newsize)
        int8_t etype;
        struct super_block *sb = inode->i_sb;
        sector_t first_block = newsize >> sb->s_blocksize_bits, offset;
+       unsigned long partial_final_block;
        int adsize;
        struct udf_inode_info *iinfo = UDF_I(inode);
        struct kernel_long_ad extent;
-       int err;
+       int err = 0;
+       int within_final_block;
 
        if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
                adsize = sizeof(struct short_ad);
@@ -618,18 +638,8 @@ static int udf_extend_file(struct inode *inode, loff_t newsize)
                BUG();
 
        etype = inode_bmap(inode, first_block, &epos, &eloc, &elen, &offset);
+       within_final_block = (etype != -1);
 
-       /* File has extent covering the new size (could happen when extending
-        * inside a block)? */
-       if (etype != -1)
-               return 0;
-       if (newsize & (sb->s_blocksize - 1))
-               offset++;
-       /* Extended file just to the boundary of the last file block? */
-       if (offset == 0)
-               return 0;
-
-       /* Truncate is extending the file by 'offset' blocks */
        if ((!epos.bh && epos.offset == udf_file_entry_alloc_offset(inode)) ||
            (epos.bh && epos.offset == sizeof(struct allocExtDesc))) {
                /* File has no extents at all or has empty last
@@ -643,7 +653,22 @@ static int udf_extend_file(struct inode *inode, loff_t newsize)
                                      &extent.extLength, 0);
                extent.extLength |= etype << 30;
        }
-       err = udf_do_extend_file(inode, &epos, &extent, offset);
+
+       partial_final_block = newsize & (sb->s_blocksize - 1);
+
+       /* File has extent covering the new size (could happen when extending
+        * inside a block)?
+        */
+       if (within_final_block) {
+               /* Extending file within the last file block */
+               udf_do_extend_final_block(inode, &epos, &extent,
+                                         partial_final_block);
+       } else {
+               loff_t add = ((loff_t)offset << sb->s_blocksize_bits) |
+                            partial_final_block;
+               err = udf_do_extend_file(inode, &epos, &extent, add);
+       }
+
        if (err < 0)
                goto out;
        err = 0;
@@ -748,6 +773,7 @@ static sector_t inode_getblk(struct inode *inode, sector_t block,
        /* Are we beyond EOF? */
        if (etype == -1) {
                int ret;
+               loff_t hole_len;
                isBeyondEOF = true;
                if (count) {
                        if (c)
@@ -763,7 +789,8 @@ static sector_t inode_getblk(struct inode *inode, sector_t block,
                        startnum = (offset > 0);
                }
                /* Create extents for the hole between EOF and offset */
-               ret = udf_do_extend_file(inode, &prev_epos, laarr, offset);
+               hole_len = (loff_t)offset << inode->i_blkbits;
+               ret = udf_do_extend_file(inode, &prev_epos, laarr, hole_len);
                if (ret < 0) {
                        brelse(prev_epos.bh);
                        brelse(cur_epos.bh);
@@ -1206,7 +1233,7 @@ int udf_setsize(struct inode *inode, loff_t newsize)
 {
        int err;
        struct udf_inode_info *iinfo;
-       int bsize = 1 << inode->i_blkbits;
+       int bsize = i_blocksize(inode);
 
        if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
              S_ISLNK(inode->i_mode)))
@@ -1235,8 +1262,8 @@ int udf_setsize(struct inode *inode, loff_t newsize)
                        return err;
                }
 set_size:
-               truncate_setsize(inode, newsize);
                up_write(&iinfo->i_data_sem);
+               truncate_setsize(inode, newsize);
        } else {
                if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
                        down_write(&iinfo->i_data_sem);
@@ -1253,9 +1280,9 @@ set_size:
                                          udf_get_block);
                if (err)
                        return err;
+               truncate_setsize(inode, newsize);
                down_write(&iinfo->i_data_sem);
                udf_clear_extent_cache(inode);
-               truncate_setsize(inode, newsize);
                udf_truncate_extents(inode);
                up_write(&iinfo->i_data_sem);
        }
@@ -1364,6 +1391,12 @@ reread:
 
        iinfo->i_alloc_type = le16_to_cpu(fe->icbTag.flags) &
                                                        ICBTAG_FLAG_AD_MASK;
+       if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_SHORT &&
+           iinfo->i_alloc_type != ICBTAG_FLAG_AD_LONG &&
+           iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) {
+               ret = -EIO;
+               goto out;
+       }
        iinfo->i_unique = 0;
        iinfo->i_lenEAttr = 0;
        iinfo->i_lenExtents = 0;
@@ -2047,14 +2080,29 @@ void udf_write_aext(struct inode *inode, struct extent_position *epos,
                epos->offset += adsize;
 }
 
+/*
+ * Only 1 indirect extent in a row really makes sense but allow upto 16 in case
+ * someone does some weird stuff.
+ */
+#define UDF_MAX_INDIR_EXTS 16
+
 int8_t udf_next_aext(struct inode *inode, struct extent_position *epos,
                     struct kernel_lb_addr *eloc, uint32_t *elen, int inc)
 {
        int8_t etype;
+       unsigned int indirections = 0;
 
        while ((etype = udf_current_aext(inode, epos, eloc, elen, inc)) ==
               (EXT_NEXT_EXTENT_ALLOCDECS >> 30)) {
                int block;
+
+               if (++indirections > UDF_MAX_INDIR_EXTS) {
+                       udf_err(inode->i_sb,
+                               "too many indirect extents in inode %lu\n",
+                               inode->i_ino);
+                       return -1;
+               }
+
                epos->block = *eloc;
                epos->offset = sizeof(struct allocExtDesc);
                brelse(epos->bh);