Commit 1f3868f0 authored by Jan Kara's avatar Jan Kara

udf: Fix extending file within last block

When extending file within last block it can happen that the extent is
already rounded to the blocksize and thus contains the offset we want to
grow up to. In such case we would mistakenly expand the last extent and
make it one block longer than it should be, exposing unallocated block
in a file and causing data corruption. Fix the problem by properly
detecting this case and bailing out.

CC: stable@vger.kernel.org
Signed-off-by: default avatarJan Kara <jack@suse.cz>
parent 16d05565
...@@ -585,13 +585,17 @@ static int udf_do_extend_file(struct inode *inode, ...@@ -585,13 +585,17 @@ static int udf_do_extend_file(struct inode *inode,
static void udf_do_extend_final_block(struct inode *inode, static void udf_do_extend_final_block(struct inode *inode,
struct extent_position *last_pos, struct extent_position *last_pos,
struct kernel_long_ad *last_ext, struct kernel_long_ad *last_ext,
uint32_t final_block_len) uint32_t new_elen)
{ {
struct super_block *sb = inode->i_sb;
uint32_t added_bytes; uint32_t added_bytes;
added_bytes = final_block_len - /*
(last_ext->extLength & (sb->s_blocksize - 1)); * Extent already large enough? It may be already rounded up to block
* size...
*/
if (new_elen <= (last_ext->extLength & UDF_EXTENT_LENGTH_MASK))
return;
added_bytes = (last_ext->extLength & UDF_EXTENT_LENGTH_MASK) - new_elen;
last_ext->extLength += added_bytes; last_ext->extLength += added_bytes;
UDF_I(inode)->i_lenExtents += added_bytes; UDF_I(inode)->i_lenExtents += added_bytes;
...@@ -608,12 +612,12 @@ static int udf_extend_file(struct inode *inode, loff_t newsize) ...@@ -608,12 +612,12 @@ static int udf_extend_file(struct inode *inode, loff_t newsize)
int8_t etype; int8_t etype;
struct super_block *sb = inode->i_sb; struct super_block *sb = inode->i_sb;
sector_t first_block = newsize >> sb->s_blocksize_bits, offset; sector_t first_block = newsize >> sb->s_blocksize_bits, offset;
unsigned long partial_final_block; loff_t new_elen;
int adsize; int adsize;
struct udf_inode_info *iinfo = UDF_I(inode); struct udf_inode_info *iinfo = UDF_I(inode);
struct kernel_long_ad extent; struct kernel_long_ad extent;
int err = 0; int err = 0;
int within_final_block; bool within_last_ext;
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT) if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
adsize = sizeof(struct short_ad); adsize = sizeof(struct short_ad);
...@@ -629,9 +633,9 @@ static int udf_extend_file(struct inode *inode, loff_t newsize) ...@@ -629,9 +633,9 @@ static int udf_extend_file(struct inode *inode, loff_t newsize)
udf_discard_prealloc(inode); udf_discard_prealloc(inode);
etype = inode_bmap(inode, first_block, &epos, &eloc, &elen, &offset); etype = inode_bmap(inode, first_block, &epos, &eloc, &elen, &offset);
within_final_block = (etype != -1); within_last_ext = (etype != -1);
/* We don't expect extents past EOF... */ /* We don't expect extents past EOF... */
WARN_ON_ONCE(etype != -1 && WARN_ON_ONCE(within_last_ext &&
elen > ((loff_t)offset + 1) << inode->i_blkbits); elen > ((loff_t)offset + 1) << inode->i_blkbits);
if ((!epos.bh && epos.offset == udf_file_entry_alloc_offset(inode)) || if ((!epos.bh && epos.offset == udf_file_entry_alloc_offset(inode)) ||
...@@ -648,19 +652,17 @@ static int udf_extend_file(struct inode *inode, loff_t newsize) ...@@ -648,19 +652,17 @@ static int udf_extend_file(struct inode *inode, loff_t newsize)
extent.extLength |= etype << 30; extent.extLength |= etype << 30;
} }
partial_final_block = newsize & (sb->s_blocksize - 1); new_elen = ((loff_t)offset << inode->i_blkbits) |
(newsize & (sb->s_blocksize - 1));
/* File has extent covering the new size (could happen when extending /* File has extent covering the new size (could happen when extending
* inside a block)? * inside a block)?
*/ */
if (within_final_block) { if (within_last_ext) {
/* Extending file within the last file block */ /* Extending file within the last file block */
udf_do_extend_final_block(inode, &epos, &extent, udf_do_extend_final_block(inode, &epos, &extent, new_elen);
partial_final_block);
} else { } else {
loff_t add = ((loff_t)offset << sb->s_blocksize_bits) | err = udf_do_extend_file(inode, &epos, &extent, new_elen);
partial_final_block;
err = udf_do_extend_file(inode, &epos, &extent, add);
} }
if (err < 0) if (err < 0)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment