Skip to content

Commit 8281125

Browse files
jankaragregkh
authored andcommitted
udf: Fix extending file within last block
commit 1f3868f upstream. 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: Jan Kara <jack@suse.cz> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent df1a259 commit 8281125

1 file changed

Lines changed: 17 additions & 15 deletions

File tree

fs/udf/inode.c

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -589,13 +589,17 @@ static int udf_do_extend_file(struct inode *inode,
589589
static void udf_do_extend_final_block(struct inode *inode,
590590
struct extent_position *last_pos,
591591
struct kernel_long_ad *last_ext,
592-
uint32_t final_block_len)
592+
uint32_t new_elen)
593593
{
594-
struct super_block *sb = inode->i_sb;
595594
uint32_t added_bytes;
596595

597-
added_bytes = final_block_len -
598-
(last_ext->extLength & (sb->s_blocksize - 1));
596+
/*
597+
* Extent already large enough? It may be already rounded up to block
598+
* size...
599+
*/
600+
if (new_elen <= (last_ext->extLength & UDF_EXTENT_LENGTH_MASK))
601+
return;
602+
added_bytes = (last_ext->extLength & UDF_EXTENT_LENGTH_MASK) - new_elen;
599603
last_ext->extLength += added_bytes;
600604
UDF_I(inode)->i_lenExtents += added_bytes;
601605

@@ -612,12 +616,12 @@ static int udf_extend_file(struct inode *inode, loff_t newsize)
612616
int8_t etype;
613617
struct super_block *sb = inode->i_sb;
614618
sector_t first_block = newsize >> sb->s_blocksize_bits, offset;
615-
unsigned long partial_final_block;
619+
loff_t new_elen;
616620
int adsize;
617621
struct udf_inode_info *iinfo = UDF_I(inode);
618622
struct kernel_long_ad extent;
619623
int err = 0;
620-
int within_final_block;
624+
bool within_last_ext;
621625

622626
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
623627
adsize = sizeof(struct short_ad);
@@ -633,9 +637,9 @@ static int udf_extend_file(struct inode *inode, loff_t newsize)
633637
udf_discard_prealloc(inode);
634638

635639
etype = inode_bmap(inode, first_block, &epos, &eloc, &elen, &offset);
636-
within_final_block = (etype != -1);
640+
within_last_ext = (etype != -1);
637641
/* We don't expect extents past EOF... */
638-
WARN_ON_ONCE(etype != -1 &&
642+
WARN_ON_ONCE(within_last_ext &&
639643
elen > ((loff_t)offset + 1) << inode->i_blkbits);
640644

641645
if ((!epos.bh && epos.offset == udf_file_entry_alloc_offset(inode)) ||
@@ -652,19 +656,17 @@ static int udf_extend_file(struct inode *inode, loff_t newsize)
652656
extent.extLength |= etype << 30;
653657
}
654658

655-
partial_final_block = newsize & (sb->s_blocksize - 1);
659+
new_elen = ((loff_t)offset << inode->i_blkbits) |
660+
(newsize & (sb->s_blocksize - 1));
656661

657662
/* File has extent covering the new size (could happen when extending
658663
* inside a block)?
659664
*/
660-
if (within_final_block) {
665+
if (within_last_ext) {
661666
/* Extending file within the last file block */
662-
udf_do_extend_final_block(inode, &epos, &extent,
663-
partial_final_block);
667+
udf_do_extend_final_block(inode, &epos, &extent, new_elen);
664668
} else {
665-
loff_t add = ((loff_t)offset << sb->s_blocksize_bits) |
666-
partial_final_block;
667-
err = udf_do_extend_file(inode, &epos, &extent, add);
669+
err = udf_do_extend_file(inode, &epos, &extent, new_elen);
668670
}
669671

670672
if (err < 0)

0 commit comments

Comments
 (0)