Btrfs: fix incremental send's decision to delay a dir move/rename
authorFilipe Manana <fdmanana@gmail.com>
Sun, 16 Mar 2014 20:37:26 +0000 (20:37 +0000)
committerChris Mason <clm@fb.com>
Fri, 21 Mar 2014 22:24:27 +0000 (15:24 -0700)
It's possible to change the parent/child relationship between directories
in such a way that if a child directory has a higher inode number than
its parent, it doesn't necessarily means the child rename/move operation
can be performed immediately. The parent migth have its own rename/move
operation delayed, therefore in this case the child needs to have its
rename/move operation delayed too, and be performed after its new parent's
rename/move.

Steps to reproduce the issue:

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

      $ mkdir /mnt/A
      $ mkdir /mnt/B
      $ mkdir /mnt/C
      $ mv /mnt/C /mnt/A
      $ mv /mnt/B /mnt/A/C
      $ mkdir /mnt/A/C/D

      $ btrfs subvolume snapshot -r /mnt /mnt/snap1
      $ btrfs send /mnt/snap1 -f /tmp/base.send

      $ mv /mnt/A/C/D /mnt/A/D2
      $ mv /mnt/A/C/B /mnt/A/D2/B2
      $ mv /mnt/A/C /mnt/A/D2/B2/C2

      $ btrfs subvolume snapshot -r /mnt /mnt/snap2
      $ btrfs send -p /mnt/snap1 /mnt/snap2 -f /tmp/incremental.send

The incremental send caused the kernel code to enter an infinite loop when
building the path string for directory C after its references are processed.

The necessary conditions here are that C has an inode number higher than both
A and B, and B as an higher inode number higher than A, and D has the highest
inode number, that is:
    inode_number(A) < inode_number(B) < inode_number(C) < inode_number(D)

The same issue could happen if after the first snapshot there's any number
of intermediary parent directories between A2 and B2, and between B2 and C2.

A test case for xfstests follows, covering this simple case and more advanced
ones, with files and hard links created inside the directories.

Signed-off-by: Filipe David Borba Manana <fdmanana@gmail.com>
Signed-off-by: Chris Mason <clm@fb.com>
fs/btrfs/send.c

index 92d4ae8..f164724 100644 (file)
@@ -3184,12 +3184,12 @@ static int wait_for_parent_move(struct send_ctx *sctx,
        struct fs_path *path_after = NULL;
        int len1, len2;
 
-       if (parent_ref->dir <= sctx->cur_ino)
-               return 0;
-
        if (is_waiting_for_move(sctx, ino))
                return 1;
 
+       if (parent_ref->dir <= sctx->cur_ino)
+               return 0;
+
        ret = get_inode_info(sctx->parent_root, ino, NULL, &old_gen,
                             NULL, NULL, NULL, NULL);
        if (ret == -ENOENT)