From: Steven J. Magnani <magn...@ieee.org>

When an ALLOCATED_NOT_RECORDED extent ("File Tail") follows the
extents describing a file body, don't (improperly) try to make use of it
as part of appending a hole to the file.

Fixes: fa33cdbf3ece ("udf: Fix incorrect final NOT_ALLOCATED (hole) extent 
length")
Signed-off-by: Steven J. Magnani <magn...@ieee.org>
---
--- a/fs/udf/inode.c    2020-12-28 20:51:29.000000000 -0600
+++ b/fs/udf/inode.c    2021-01-02 17:00:39.794157840 -0600
@@ -520,8 +520,7 @@ static int udf_do_extend_file(struct ino
                prealloc_loc = last_ext->extLocation;
                prealloc_len = last_ext->extLength;
                /* Mark the extent as a hole */
-               last_ext->extLength = EXT_NOT_RECORDED_NOT_ALLOCATED |
-                       (last_ext->extLength & UDF_EXTENT_LENGTH_MASK);
+               last_ext->extLength = EXT_NOT_RECORDED_NOT_ALLOCATED;
                last_ext->extLocation.logicalBlockNum = 0;
                last_ext->extLocation.partitionReferenceNum = 0;
        }
@@ -626,7 +625,6 @@ static void udf_do_extend_final_block(st
 
 static int udf_extend_file(struct inode *inode, loff_t newsize)
 {
-
        struct extent_position epos;
        struct kernel_lb_addr eloc;
        uint32_t elen;
@@ -639,6 +637,7 @@ static int udf_extend_file(struct inode
        struct kernel_long_ad extent;
        int err = 0;
        int within_final_block;
+       loff_t hole_size = 0;
 
        if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
                adsize = sizeof(struct short_ad);
@@ -648,7 +647,8 @@ static int udf_extend_file(struct inode
                BUG();
 
        etype = inode_bmap(inode, first_block, &epos, &eloc, &elen, &offset);
-       within_final_block = (etype != -1);
+       within_final_block = (etype == (EXT_RECORDED_ALLOCATED >> 30)) ||
+                            (etype == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30));
 
        if ((!epos.bh && epos.offset == udf_file_entry_alloc_offset(inode)) ||
            (epos.bh && epos.offset == sizeof(struct allocExtDesc))) {
@@ -658,9 +658,15 @@ static int udf_extend_file(struct inode
                extent.extLocation.partitionReferenceNum = 0;
                extent.extLength = EXT_NOT_RECORDED_NOT_ALLOCATED;
        } else {
+               int8_t bmap_etype = etype;
                epos.offset -= adsize;
                etype = udf_next_aext(inode, &epos, &extent.extLocation,
                                      &extent.extLength, 0);
+               if ((bmap_etype == -1) &&
+                   (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30))) {
+                       /* offset is relative to prealloc extent end */
+                       hole_size = extent.extLength;
+               }
                extent.extLength |= etype << 30;
        }
 
@@ -674,9 +680,9 @@ static int udf_extend_file(struct inode
                udf_do_extend_final_block(inode, &epos, &extent,
                                          partial_final_block);
        } else {
-               loff_t add = ((loff_t)offset << sb->s_blocksize_bits) |
+               hole_size += ((loff_t)offset << sb->s_blocksize_bits) |
                             partial_final_block;
-               err = udf_do_extend_file(inode, &epos, &extent, add);
+               err = udf_do_extend_file(inode, &epos, &extent, hole_size);
        }
 
        if (err < 0)
@@ -700,7 +706,7 @@ static sector_t inode_getblk(struct inod
        loff_t lbcount = 0, b_off = 0;
        udf_pblk_t newblocknum, newblock;
        sector_t offset = 0;
-       int8_t etype;
+       int8_t etype = -1;
        struct udf_inode_info *iinfo = UDF_I(inode);
        udf_pblk_t goal = 0, pgoal = iinfo->i_location.logicalBlockNum;
        int lastblock = 0;
@@ -729,14 +735,22 @@ static sector_t inode_getblk(struct inod
                        cur_epos.bh = next_epos.bh;
                }
 
-               lbcount += elen;
-
                prev_epos.block = cur_epos.block;
                cur_epos.block = next_epos.block;
 
                prev_epos.offset = cur_epos.offset;
                cur_epos.offset = next_epos.offset;
 
+               /* Corner case: preallocated extent that stops short of
+                * desired block
+                */
+               if ((etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) &&
+                   ((lbcount + elen) <= b_off)) {
+                       etype = -1;
+                       break;
+               }
+
+               lbcount += elen;
                etype = udf_next_aext(inode, &next_epos, &eloc, &elen, 1);
                if (etype == -1)
                        break;

Reply via email to