On Sun 30-06-19 21:39:35, Steven J. Magnani wrote: > In some cases, using the 'truncate' command to extend a UDF file results > in a mismatch between the length of the file's extents (specifically, due > to incorrect length of the final NOT_ALLOCATED extent) and the information > (file) length. The discrepancy can prevent other operating systems > (i.e., Windows 10) from opening the file. > > Two particular errors have been observed when extending a file: > > 1. The final extent is larger than it should be, having been rounded up > to a multiple of the block size. > > B. The final extent is not shorter than it should be, due to not having > been updated when the file's information length was increased. > > Change since v1: > Simplified udf_do_extend_file() API, partially by factoring out its > handling of the "extending within the last file block" corner case. > > Fixes: 2c948b3f86e5 ("udf: Avoid IO in udf_clear_inode") > Signed-off-by: Steven J. Magnani <st...@digidescorp.com>
Thanks for the patch! I have added it with some small modifications to my tree. Below are the changes I did. > --- a/fs/udf/inode.c 2019-05-24 21:17:33.659704533 -0500 > +++ b/fs/udf/inode.c 2019-06-29 21:10:48.938562957 -0500 > @@ -470,13 +470,15 @@ static struct buffer_head *udf_getblk(st > return NULL; > } > > -/* Extend the file by 'blocks' blocks, return the number of extents added */ > +/* Extend the file with new blocks totaling 'new_block_bytes', > + * return the number of extents added > + */ > static int udf_do_extend_file(struct inode *inode, > struct extent_position *last_pos, > struct kernel_long_ad *last_ext, > - sector_t blocks) > + loff_t new_block_bytes) > { > - sector_t add; > + unsigned long add; I've changed the type here to uint32_t since that's what we usually use for extent size. > +/* Extend the final block of the file to final_block_len bytes */ > +static int udf_do_extend_final_block(struct inode *inode, Changed return type to void since the function doesn't return anything useful. > + struct extent_position *last_pos, > + struct kernel_long_ad *last_ext, > + unsigned long final_block_len) > +{ > + struct super_block *sb = inode->i_sb; > + struct kernel_lb_addr tmploc; > + uint32_t tmplen; > + struct udf_inode_info *iinfo; > + unsigned long added_bytes; > + > + iinfo = UDF_I(inode); > + > + added_bytes = final_block_len - > + (last_ext->extLength & (sb->s_blocksize - 1)); > + last_ext->extLength += added_bytes; > + iinfo->i_lenExtents += added_bytes; > + > + udf_write_aext(inode, last_pos, &last_ext->extLocation, > + last_ext->extLength, 1); > + /* > + * We've rewritten the last extent but there may be empty > + * indirect extent after it - enter it. > + */ > + udf_next_aext(inode, last_pos, &tmploc, &tmplen, 0); > + > + /* 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); > + else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG) > + last_pos->offset -= sizeof(struct long_ad); > + else > + return -EIO; I've dropped the updates of last_pos here. This function is used from a single place and passed epos isn't used in any way after the function returns. > + > + return 0; > +} > + > static int udf_extend_file(struct inode *inode, loff_t newsize) > { > > @@ -605,10 +643,12 @@ static int udf_extend_file(struct inode > int8_t etype; > struct super_block *sb = inode->i_sb; > sector_t first_block = newsize >> sb->s_blocksize_bits, offset; > + unsigned long partial_final_block; Again uint32_t here. > @@ -643,7 +673,22 @@ static int udf_extend_file(struct inode > &extent.extLength, 0); > extent.extLength |= etype << 30; > } > - err = udf_do_extend_file(inode, &epos, &extent, offset); > + > + partial_final_block = newsize & (sb->s_blocksize - 1); > + > + /* File has extent covering the new size (could happen when extending > + * inside a block)? > + */ > + if (within_final_block) { > + /* Extending file within the last file block */ > + err = udf_do_extend_final_block(inode, &epos, &extent, > + partial_final_block); > + } else { > + loff_t add = (offset << sb->s_blocksize_bits) | Typed 'offset' to loff_t before shifting. Otherwise the shift could overflow for systems with 32-bit sector_t. > + partial_final_block; > + err = udf_do_extend_file(inode, &epos, &extent, add); > + } > + > if (err < 0) > goto out; > err = 0; ... > @@ -760,7 +806,8 @@ static sector_t inode_getblk(struct inod > startnum = (offset > 0); > } > /* Create extents for the hole between EOF and offset */ > - ret = udf_do_extend_file(inode, &prev_epos, laarr, offset); > + hole_len = offset << inode->i_sb->s_blocksize_bits; The same as above. > + ret = udf_do_extend_file(inode, &prev_epos, laarr, hole_len); > if (ret < 0) { > *err = ret; > newblock = 0; Honza -- Jan Kara <j...@suse.com> SUSE Labs, CR