[PATCH 1/2] udf: fix hole append when File Tail exists

From: Steve Magnani
Date: Sat Jan 09 2021 - 17:42:10 EST


From: Steven J. Magnani <magnani@xxxxxxxx>

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 <magnani@xxxxxxxx>
---
--- 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;