Skip to content
  • Filipe Manana's avatar
    Btrfs: incremental send, fix emission of invalid clone operations · 3c850b45
    Filipe Manana authored
    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 ac765f83
    ("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: 040ee612
    
     ("Btrfs: send, improve clone range")
    Signed-off-by: default avatarFilipe Manana <fdmanana@suse.com>
    Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
    3c850b45