On Mon, May 20, 2019 at 09:57:00AM +0100, fdmanana@xxxxxxxxxx wrote:
> From: Filipe Manana <fdmanana@xxxxxxxx>
>
> When doing an incremental send we can now issue clone operations with a
> source range that ends at the source's file eof and with a destination
> range that ends at an offset smaller then the destination's file eof.
> If the eof of the source file is not aligned to the sector size of the
> filesystem, the receiver will get a -EINVAL error when trying to do the
> operation or, on older kernels, silently corrupt the destination file.
> The corruption happens on kernels without commit ac765f83f1397646
> ("Btrfs: fix data corruption due to cloning of eof block"), while the
> failure to clone happens on kernels with that commit.
>
> Example reproducer:
>
> $ mkfs.btrfs -f /dev/sdb
> $ mount /dev/sdb /mnt/sdb
>
> $ xfs_io -f -c "pwrite -S 0xb1 0 2M" /mnt/sdb/foo
> $ xfs_io -f -c "pwrite -S 0xc7 0 2M" /mnt/sdb/bar
> $ xfs_io -f -c "pwrite -S 0x4d 0 2M" /mnt/sdb/baz
> $ xfs_io -f -c "pwrite -S 0xe2 0 2M" /mnt/sdb/zoo
>
> $ btrfs subvolume snapshot -r /mnt/sdb /mnt/sdb/base
>
> $ btrfs send -f /tmp/base.send /mnt/sdb/base
>
> $ xfs_io -c "reflink /mnt/sdb/bar 1560K 500K 100K" /mnt/sdb/bar
> $ xfs_io -c "reflink /mnt/sdb/bar 1560K 0 100K" /mnt/sdb/zoo
> $ xfs_io -c "truncate 550K" /mnt/sdb/bar
>
> $ btrfs subvolume snapshot -r /mnt/sdb /mnt/sdb/incr
>
> $ btrfs send -f /tmp/incr.send -p /mnt/sdb/base /mnt/sdb/incr
>
> $ mkfs.btrfs -f /dev/sdc
> $ mount /dev/sdc /mnt/sdc
>
> $ btrfs receive -f /tmp/base.send /mnt/sdc
> $ btrfs receive -vv -f /tmp/incr.send /mnt/sdc
> (...)
> truncate bar size=563200
> utimes bar
> clone zoo - source=bar source offset=512000 offset=0 length=51200
> ERROR: failed to clone extents to zoo
> Invalid argument
>
> The failure happens because the clone source range ends at the eof of file
> bar, 563200, which is not aligned to the filesystems sector size (4Kb in
> this case), and the destination range ends at offset 0 + 51200, which is
> less then the size of the file zoo (2Mb).
>
> So fix this by detecting such case and instead of issuing a clone
> operation for the whole range, do a clone operation for smaller range
> that is sector size aligned followed by a write operation for the block
> containing the eof. Here we will always be pessimistic and assume the
> destination filesystem of the send stream has the largest possible sector
> size (64Kb), since we have no way of determining it.
>
> This fixes a recent regression introduced in kernel 5.2-rc1.
>
> Fixes: 040ee6120cb6706 ("Btrfs: send, improve clone range")
> Signed-off-by: Filipe Manana <fdmanana@xxxxxxxx>
Added to 5.2-rc queue, thanks.