udf: Discard preallocation before extending file with a hole
authorJan Kara <jack@suse.cz>
Wed, 7 Dec 2022 17:17:34 +0000 (18:17 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 7 Jan 2023 11:07:10 +0000 (12:07 +0100)
commit 16d0556568148bdcaa45d077cac9f8f7077cf70a upstream.

When extending file with a hole, we tried to preserve existing
preallocation for the file. However that is not very useful and
complicates code because the previous extent may need to be rounded to
block boundary as well (which we forgot to do thus causing data
corruption for sequence like:

xfs_io -f -c "pwrite 0x75e63 11008" -c "truncate 0x7b24b" \
  -c "truncate 0xabaa3" -c "pwrite 0xac70b 22954" \
  -c "pwrite 0x93a43 11358" -c "pwrite 0xb8e65 52211" file

with 512-byte block size. Just discard preallocation before extending
file to simplify things and also fix this data corruption.

CC: stable@vger.kernel.org
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/udf/inode.c

index fab5a9506bcf2d326dd4bd8b0faa66eb3ba6fc1a..30d9c2e414557664352e18b7cf50be440aaa70e8 100644 (file)
@@ -442,6 +442,12 @@ static int udf_get_block(struct inode *inode, sector_t block,
                iinfo->i_next_alloc_goal++;
        }
 
+       /*
+        * Block beyond EOF and prealloc extents? Just discard preallocation
+        * as it is not useful and complicates things.
+        */
+       if (((loff_t)block) << inode->i_blkbits > iinfo->i_lenExtents)
+               udf_discard_prealloc(inode);
        udf_clear_extent_cache(inode);
        phys = inode_getblk(inode, block, &err, &new);
        if (!phys)
@@ -491,8 +497,6 @@ static int udf_do_extend_file(struct inode *inode,
        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 = {};
-       int prealloc_len = 0;
        struct udf_inode_info *iinfo;
        int err;
 
@@ -513,19 +517,6 @@ static int udf_do_extend_file(struct inode *inode,
                        ~(sb->s_blocksize - 1);
        }
 
-       /* Last extent are just preallocated blocks? */
-       if ((last_ext->extLength & UDF_EXTENT_FLAG_MASK) ==
-                                               EXT_NOT_RECORDED_ALLOCATED) {
-               /* Save the extent so that we can reattach it to the end */
-               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->extLocation.logicalBlockNum = 0;
-               last_ext->extLocation.partitionReferenceNum = 0;
-       }
-
        /* Can we merge with the previous extent? */
        if ((last_ext->extLength & UDF_EXTENT_FLAG_MASK) ==
                                        EXT_NOT_RECORDED_NOT_ALLOCATED) {
@@ -553,7 +544,7 @@ static int udf_do_extend_file(struct inode *inode,
                 * more extents, we may need to enter possible following
                 * empty indirect extent.
                 */
-               if (new_block_bytes || prealloc_len)
+               if (new_block_bytes)
                        udf_next_aext(inode, last_pos, &tmploc, &tmplen, 0);
        }
 
@@ -587,17 +578,6 @@ static int udf_do_extend_file(struct inode *inode,
        }
 
 out:
-       /* Do we have some preallocated blocks saved? */
-       if (prealloc_len) {
-               err = udf_add_aext(inode, last_pos, &prealloc_loc,
-                                  prealloc_len, 1);
-               if (err)
-                       return err;
-               last_ext->extLocation = prealloc_loc;
-               last_ext->extLength = prealloc_len;
-               count++;
-       }
-
        /* last_pos should point to the last written extent... */
        if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
                last_pos->offset -= sizeof(struct short_ad);
@@ -650,8 +630,17 @@ static int udf_extend_file(struct inode *inode, loff_t newsize)
        else
                BUG();
 
+       /*
+        * When creating hole in file, just don't bother with preserving
+        * preallocation. It likely won't be very useful anyway.
+        */
+       udf_discard_prealloc(inode);
+
        etype = inode_bmap(inode, first_block, &epos, &eloc, &elen, &offset);
        within_final_block = (etype != -1);
+       /* We don't expect extents past EOF... */
+       WARN_ON_ONCE(etype != -1 &&
+                    elen > ((loff_t)offset + 1) << inode->i_blkbits);
 
        if ((!epos.bh && epos.offset == udf_file_entry_alloc_offset(inode)) ||
            (epos.bh && epos.offset == sizeof(struct allocExtDesc))) {
@@ -783,10 +772,11 @@ static sector_t inode_getblk(struct inode *inode, sector_t block,
                return newblock;
        }
 
-       /* Are we beyond EOF? */
+       /* Are we beyond EOF and preallocated extent? */
        if (etype == -1) {
                int ret;
                loff_t hole_len;
+
                isBeyondEOF = true;
                if (count) {
                        if (c)