| From cfe4c1b25dd6d2f056afc00b7c98bcb3dd0b1fc3 Mon Sep 17 00:00:00 2001 |
| From: Jan Kara <jack@suse.cz> |
| Date: Wed, 7 Dec 2022 17:25:10 +0100 |
| Subject: udf: Fix preallocation discarding at indirect extent boundary |
| |
| From: Jan Kara <jack@suse.cz> |
| |
| 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 | 45 +++++++++++++-------------------------------- |
| 1 file changed, 13 insertions(+), 32 deletions(-) |
| |
| --- a/fs/udf/truncate.c |
| +++ b/fs/udf/truncate.c |
| @@ -120,60 +120,41 @@ void udf_truncate_tail_extent(struct ino |
| |
| 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, |