Btrfs: send, allow clone operations within the same file
authorFilipe Manana <fdmanana@suse.com>
Wed, 30 Oct 2019 12:23:11 +0000 (12:23 +0000)
committerDavid Sterba <dsterba@suse.com>
Mon, 18 Nov 2019 16:51:48 +0000 (17:51 +0100)
For send we currently skip clone operations when the source and
destination files are the same. This is so because clone didn't support
this case in its early days, but support for it was added back in May
2013 by commit a96fbc72884fcb ("Btrfs: allow file data clone within a
file"). This change adds support for it.

Example:

  $ mkfs.btrfs -f /dev/sdd
  $ mount /dev/sdd /mnt/sdd

  $ xfs_io -f -c "pwrite -S 0xab -b 64K 0 64K" /mnt/sdd/foobar
  $ xfs_io -c "reflink /mnt/sdd/foobar 0 64K 64K" /mnt/sdd/foobar

  $ btrfs subvolume snapshot -r /mnt/sdd /mnt/sdd/snap

  $ mkfs.btrfs -f /dev/sde
  $ mount /dev/sde /mnt/sde

  $ btrfs send /mnt/sdd/snap | btrfs receive /mnt/sde

Without this change file foobar at the destination has a single 128Kb
extent:

  $ filefrag -v /mnt/sde/snap/foobar
  Filesystem type is: 9123683e
  File size of /mnt/sde/snap/foobar is 131072 (32 blocks of 4096 bytes)
   ext:     logical_offset:        physical_offset: length:   expected: flags:
     0:        0..      31:          0..        31:     32:             last,unknown_loc,delalloc,eof
  /mnt/sde/snap/foobar: 1 extent found

With this we get a single 64Kb extent that is shared at file offsets 0
and 64K, just like in the source filesystem:

  $ filefrag -v /mnt/sde/snap/foobar
  Filesystem type is: 9123683e
  File size of /mnt/sde/snap/foobar is 131072 (32 blocks of 4096 bytes)
   ext:     logical_offset:        physical_offset: length:   expected: flags:
     0:        0..      15:       3328..      3343:     16:             shared
     1:       16..      31:       3328..      3343:     16:       3344: last,shared,eof
  /mnt/sde/snap/foobar: 2 extents found

Reviewed-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/send.c

index 27e92594a81b0478a9140c1710a3cb5db50fc4fa..cbf4909f5cd9cb52f41a0c62e745bfa6610f59fe 100644 (file)
@@ -1248,12 +1248,20 @@ static int __iterate_backrefs(u64 ino, u64 offset, u64 root, void *ctx_)
         */
        if (found->root == bctx->sctx->send_root) {
                /*
-                * TODO for the moment we don't accept clones from the inode
-                * that is currently send. We may change this when
-                * BTRFS_IOC_CLONE_RANGE supports cloning from and to the same
-                * file.
+                * If the source inode was not yet processed we can't issue a
+                * clone operation, as the source extent does not exist yet at
+                * the destination of the stream.
                 */
-               if (ino >= bctx->cur_objectid)
+               if (ino > bctx->cur_objectid)
+                       return 0;
+               /*
+                * We clone from the inode currently being sent as long as the
+                * source extent is already processed, otherwise we could try
+                * to clone from an extent that does not exist yet at the
+                * destination of the stream.
+                */
+               if (ino == bctx->cur_objectid &&
+                   offset >= bctx->sctx->cur_inode_next_write_offset)
                        return 0;
        }