diff options
author | Filipe Manana <fdmanana@gmail.com> | 2014-03-16 20:37:26 +0000 |
---|---|---|
committer | Chris Mason <clm@fb.com> | 2014-03-21 15:24:27 -0700 |
commit | 7b119a8b8998f17abd6caf928dee5bf203eef8c5 (patch) | |
tree | 5b388977b019040550d3d67a6f204a5b31c0a1b9 /fs/btrfs | |
parent | 425b5dafc8738d3d6d6b05827f40bd32bf04a20b (diff) | |
download | linux-stable-7b119a8b8998f17abd6caf928dee5bf203eef8c5.tar.gz linux-stable-7b119a8b8998f17abd6caf928dee5bf203eef8c5.tar.bz2 linux-stable-7b119a8b8998f17abd6caf928dee5bf203eef8c5.zip |
Btrfs: fix incremental send's decision to delay a dir move/rename
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>
Diffstat (limited to 'fs/btrfs')
-rw-r--r-- | fs/btrfs/send.c | 6 |
1 files changed, 3 insertions, 3 deletions
diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index 92d4ae8a8ae6..f16472409eec 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c @@ -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) |