udf: Fix preallocation discarding at indirect extent boundary
authorJan Kara <jack@suse.cz>
Wed, 7 Dec 2022 16:25:10 +0000 (17:25 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 21 Dec 2022 16:48:05 +0000 (17:48 +0100)
commit cfe4c1b25dd6d2f056afc00b7c98bcb3dd0b1fc3 upstream.

When preallocation extent is the first one in the extent block, the
code would corrupt extent tree header instead. Fix the problem and use
udf_delete_aext() for deleting extent to avoid some code duplication.

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

index 532cda9..a9790fb 100644 (file)
@@ -120,60 +120,41 @@ void udf_truncate_tail_extent(struct inode *inode)
 
 void udf_discard_prealloc(struct inode *inode)
 {
-       struct extent_position epos = { NULL, 0, {0, 0} };
+       struct extent_position epos = {};
+       struct extent_position prev_epos = {};
        struct kernel_lb_addr eloc;
        uint32_t elen;
        uint64_t lbcount = 0;
        int8_t etype = -1, netype;
-       int adsize;
        struct udf_inode_info *iinfo = UDF_I(inode);
 
        if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB ||
            inode->i_size == iinfo->i_lenExtents)
                return;
 
-       if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
-               adsize = sizeof(struct short_ad);
-       else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
-               adsize = sizeof(struct long_ad);
-       else
-               adsize = 0;
-
        epos.block = iinfo->i_location;
 
        /* Find the last extent in the file */
-       while ((netype = udf_next_aext(inode, &epos, &eloc, &elen, 1)) != -1) {
-               etype = netype;
+       while ((netype = udf_next_aext(inode, &epos, &eloc, &elen, 0)) != -1) {
+               brelse(prev_epos.bh);
+               prev_epos = epos;
+               if (prev_epos.bh)
+                       get_bh(prev_epos.bh);
+
+               etype = udf_next_aext(inode, &epos, &eloc, &elen, 1);
                lbcount += elen;
        }
        if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) {
-               epos.offset -= adsize;
                lbcount -= elen;
-               extent_trunc(inode, &epos, &eloc, etype, elen, 0);
-               if (!epos.bh) {
-                       iinfo->i_lenAlloc =
-                               epos.offset -
-                               udf_file_entry_alloc_offset(inode);
-                       mark_inode_dirty(inode);
-               } else {
-                       struct allocExtDesc *aed =
-                               (struct allocExtDesc *)(epos.bh->b_data);
-                       aed->lengthAllocDescs =
-                               cpu_to_le32(epos.offset -
-                                           sizeof(struct allocExtDesc));
-                       if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) ||
-                           UDF_SB(inode->i_sb)->s_udfrev >= 0x0201)
-                               udf_update_tag(epos.bh->b_data, epos.offset);
-                       else
-                               udf_update_tag(epos.bh->b_data,
-                                              sizeof(struct allocExtDesc));
-                       mark_buffer_dirty_inode(epos.bh, inode);
-               }
+               udf_delete_aext(inode, prev_epos);
+               udf_free_blocks(inode->i_sb, inode, &eloc, 0,
+                               DIV_ROUND_UP(elen, 1 << inode->i_blkbits));
        }
        /* This inode entry is in-memory only and thus we don't have to mark
         * the inode dirty */
        iinfo->i_lenExtents = lbcount;
        brelse(epos.bh);
+       brelse(prev_epos.bh);
 }
 
 static void udf_update_alloc_ext_desc(struct inode *inode,