Btrfs: fix missing inode i_size update after zero range operation
PATCH 1/2 Btrfs: fix missing inode i_size update after zero range operation
From: Filipe Manana <[email protected]>
For a fallocate's zero range operation that targets a range with an end that is not aligned to the sector size, we can end up not updating the inode's i_size. This happens when the last page of the range maps to an unwritten (prealloc) extent and before that last page we have either a hole or a written extent. This is because in this scenario we relied on a call to btrfs_prealloc_file_range() to update the inode's i_size, however it can only update the i_size to the "down aligned" end of the range.
Example:
$ mkfs.btrfs -f /dev/sdc $ mount /dev/sdc /mnt $ xfs_io -f -c "pwrite -S 0xff 0 428K" /mnt/foobar $ xfs_io -c "falloc -k 428K 4K" /mnt/foobar $ xfs_io -c "fzero 0 430K" /mnt/foobar $ du --bytes /mnt/foobar 438272 /mnt/foobar
The inode's i_size was left as 428Kb (438272 bytes) when it should have been updated to 430Kb (440320 bytes).
Fix this by always updating the inode's i_size explicitly after zeroing the range.
--- fs/btrfs/file.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
fs/btrfs/file.c +4 -1
@@ -3026,9 +3026,12 @@ static int btrfs_zero_range(struct inode *inode,unlock_extent_cached (&BTRFS_I (inode)->io_tree, lockstart, lockend, &cached_state,GFP_KERNEL );/* btrfs_prealloc_file_range releases reserved space on error */ -if (ret) +if (ret) { space_reserved = false; + goto out; + } } + ret =btrfs_fallocate_update_isize (inode, offset + len, mode); out:if (ret && space_reserved) btrfs_free_reserved_data_space(inode, data_reserved,
Signature · 1 line
2.11.0
Quoted 21 lines · On Thu, Jan 18, 2018 at 11:34:20AM +0000, [email protected] wrote:
From: Filipe Manana <[email protected]>
For a fallocate's zero range operation that targets a range with an end that is not aligned to the sector size, we can end up not updating the inode's
i_size. This happens when the last page of the range maps to an unwritten (prealloc) extent and before that last page we have either a hole or a written extent. This is because in this scenario we relied on a call tobtrfs_prealloc_file_range()to update the inode'si_size, however it can only update thei_sizeto the "down aligned" end of the range.Example:
$ mkfs.btrfs -f /dev/sdc $ mount /dev/sdc /mnt $ xfs_io -f -c "pwrite -S 0xff 0 428K" /mnt/foobar $ xfs_io -c "falloc -k 428K 4K" /mnt/foobar $ xfs_io -c "fzero 0 430K" /mnt/foobar $ du --bytes /mnt/foobar 438272 /mnt/foobar
Quoted 6 lines
The inode's
i_sizewas left as 428Kb (438272 bytes) when it should have been updated to 430Kb (440320 bytes).
Fix this by always updating the inode'si_sizeexplicitly after zeroing the range.Signed-off-by Filipe Manana <[email protected]>
Added to 4.16 queue, thanks.