Skip to content

Commit 1f3868f

Browse files
committed
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: [email protected] Signed-off-by: Jan Kara <[email protected]>
1 parent 16d0556 commit 1f3868f

File tree

1 file changed

+17
-15
lines changed

1 file changed

+17
-15
lines changed

fs/udf/inode.c

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -585,13 +585,17 @@ static int udf_do_extend_file(struct inode *inode,
585585
static void udf_do_extend_final_block(struct inode *inode,
586586
struct extent_position *last_pos,
587587
struct kernel_long_ad *last_ext,
588-
uint32_t final_block_len)
588+
uint32_t new_elen)
589589
{
590-
struct super_block *sb = inode->i_sb;
591590
uint32_t added_bytes;
592591

593-
added_bytes = final_block_len -
594-
(last_ext->extLength & (sb->s_blocksize - 1));
592+
/*
593+
* Extent already large enough? It may be already rounded up to block
594+
* size...
595+
*/
596+
if (new_elen <= (last_ext->extLength & UDF_EXTENT_LENGTH_MASK))
597+
return;
598+
added_bytes = (last_ext->extLength & UDF_EXTENT_LENGTH_MASK) - new_elen;
595599
last_ext->extLength += added_bytes;
596600
UDF_I(inode)->i_lenExtents += added_bytes;
597601

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

618622
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
619623
adsize = sizeof(struct short_ad);
@@ -629,9 +633,9 @@ static int udf_extend_file(struct inode *inode, loff_t newsize)
629633
udf_discard_prealloc(inode);
630634

631635
etype = inode_bmap(inode, first_block, &epos, &eloc, &elen, &offset);
632-
within_final_block = (etype != -1);
636+
within_last_ext = (etype != -1);
633637
/* We don't expect extents past EOF... */
634-
WARN_ON_ONCE(etype != -1 &&
638+
WARN_ON_ONCE(within_last_ext &&
635639
elen > ((loff_t)offset + 1) << inode->i_blkbits);
636640

637641
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)
648652
extent.extLength |= etype << 30;
649653
}
650654

651-
partial_final_block = newsize & (sb->s_blocksize - 1);
655+
new_elen = ((loff_t)offset << inode->i_blkbits) |
656+
(newsize & (sb->s_blocksize - 1));
652657

653658
/* File has extent covering the new size (could happen when extending
654659
* inside a block)?
655660
*/
656-
if (within_final_block) {
661+
if (within_last_ext) {
657662
/* Extending file within the last file block */
658-
udf_do_extend_final_block(inode, &epos, &extent,
659-
partial_final_block);
663+
udf_do_extend_final_block(inode, &epos, &extent, new_elen);
660664
} else {
661-
loff_t add = ((loff_t)offset << sb->s_blocksize_bits) |
662-
partial_final_block;
663-
err = udf_do_extend_file(inode, &epos, &extent, add);
665+
err = udf_do_extend_file(inode, &epos, &extent, new_elen);
664666
}
665667

666668
if (err < 0)

0 commit comments

Comments
 (0)