From 4fdb688c7071f8d5acca0f1f340ea276e9a61dce Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Sat, 4 Apr 2020 21:20:22 +0100 Subject: btrfs: fix lost i_size update after cloning inline extent When not using the NO_HOLES feature we were not marking the destination's file range as written after cloning an inline extent into it. This can lead to a data loss if the current destination file size is smaller than the source file's size. Example: $ mkfs.btrfs -f -O ^no-holes /dev/sdc $ mount /mnt/sdc /mnt $ echo "hello world" > /mnt/foo $ cp --reflink=always /mnt/foo /mnt/bar $ rm -f /mnt/foo $ umount /mnt $ mount /mnt/sdc /mnt $ cat /mnt/bar $ $ stat -c %s /mnt/bar 0 # -> the file is empty, since we deleted foo, the data lost is forever Fix that by calling btrfs_inode_set_file_extent_range() after cloning an inline extent. A test case for fstests will follow soon. Link: https://lore.kernel.org/linux-btrfs/20200404193846.GA432065@latitude/ Reported-by: Johannes Hirte Fixes: 9ddc959e802bf ("btrfs: use the file extent tree infrastructure") Tested-by: Johannes Hirte Signed-off-by: Filipe Manana Signed-off-by: David Sterba --- fs/btrfs/reflink.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs/btrfs') diff --git a/fs/btrfs/reflink.c b/fs/btrfs/reflink.c index d1973141d3bb..040009d1cc31 100644 --- a/fs/btrfs/reflink.c +++ b/fs/btrfs/reflink.c @@ -264,6 +264,7 @@ copy_inline_extent: size); inode_add_bytes(dst, datal); set_bit(BTRFS_INODE_NEEDS_FULL_SYNC, &BTRFS_I(dst)->runtime_flags); + ret = btrfs_inode_set_file_extent_range(BTRFS_I(dst), 0, aligned_end); out: if (!ret && !trans) { /* -- cgit v1.2.3