diff options
author | Filipe Manana <fdmanana@gmail.com> | 2014-03-19 10:20:54 -0400 |
---|---|---|
committer | Chris Mason <clm@fb.com> | 2014-03-21 18:25:48 -0400 |
commit | bfa7e1f8be4bd7118e485a42cc8889530d415d05 (patch) | |
tree | 907649fa41b515befbfcae14feeb0e6264a1fcce /fs/btrfs/send.c | |
parent | 7b119a8b8998f17abd6caf928dee5bf203eef8c5 (diff) |
Btrfs: part 2, fix incremental send's decision to delay a dir move/rename
For an incremental send, fix the process of determining whether the directory
inode we're currently processing needs to have its move/rename operation delayed.
We were ignoring the fact that if the inode's new immediate ancestor has a higher
inode number than ours but wasn't renamed/moved, we might still need to delay our
move/rename, because some other ancestor directory higher in the hierarchy might
have an inode number higher than ours *and* was renamed/moved too - in this case
we have to wait for rename/move of that ancestor to happen before our current
directory's rename/move operation.
Simple steps to reproduce this issue:
$ mkfs.btrfs -f /dev/sdd
$ mount /dev/sdd /mnt
$ mkdir -p /mnt/a/x1/x2
$ mkdir /mnt/a/Z
$ mkdir -p /mnt/a/x1/x2/x3/x4/x5
$ btrfs subvolume snapshot -r /mnt /mnt/snap1
$ btrfs send /mnt/snap1 -f /tmp/base.send
$ mv /mnt/a/x1/x2/x3 /mnt/a/Z/X33
$ mv /mnt/a/x1/x2 /mnt/a/Z/X33/x4/x5/X22
$ 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 Z after its references are processed.
A more complex scenario:
$ mkfs.btrfs -f /dev/sdd
$ mount /dev/sdd /mnt
$ mkdir -p /mnt/a/b/c/d
$ mkdir /mnt/a/b/c/d/e
$ mkdir /mnt/a/b/c/d/f
$ mv /mnt/a/b/c/d/e /mnt/a/b/c/d/f/E2
$ mkdir /mmt/a/b/c/g
$ mv /mnt/a/b/c/d /mnt/a/b/D2
$ btrfs subvolume snapshot -r /mnt /mnt/snap1
$ btrfs send /mnt/snap1 -f /tmp/base.send
$ mkdir /mnt/a/o
$ mv /mnt/a/b/c/g /mnt/a/b/D2/f/G2
$ mv /mnt/a/b/D2 /mnt/a/b/dd
$ mv /mnt/a/b/c /mnt/a/C2
$ mv /mnt/a/b/dd/f /mnt/a/o/FF
$ mv /mnt/a/b /mnt/a/o/FF/E2/BB
$ btrfs subvolume snapshot -r /mnt /mnt/snap2
$ btrfs send -p /mnt/snap1 /mnt/snap2 -f /tmp/incremental.send
A test case for xfstests follows.
Signed-off-by: Filipe David Borba Manana <fdmanana@gmail.com>
Signed-off-by: Chris Mason <clm@fb.com>
Diffstat (limited to 'fs/btrfs/send.c')
-rw-r--r-- | fs/btrfs/send.c | 71 |
1 files changed, 66 insertions, 5 deletions
diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index f16472409eec..143fed3f4586 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c | |||
@@ -2916,7 +2916,10 @@ static void free_waiting_dir_move(struct send_ctx *sctx, | |||
2916 | kfree(dm); | 2916 | kfree(dm); |
2917 | } | 2917 | } |
2918 | 2918 | ||
2919 | static int add_pending_dir_move(struct send_ctx *sctx, u64 parent_ino) | 2919 | static int add_pending_dir_move(struct send_ctx *sctx, |
2920 | u64 ino, | ||
2921 | u64 ino_gen, | ||
2922 | u64 parent_ino) | ||
2920 | { | 2923 | { |
2921 | struct rb_node **p = &sctx->pending_dir_moves.rb_node; | 2924 | struct rb_node **p = &sctx->pending_dir_moves.rb_node; |
2922 | struct rb_node *parent = NULL; | 2925 | struct rb_node *parent = NULL; |
@@ -2929,8 +2932,8 @@ static int add_pending_dir_move(struct send_ctx *sctx, u64 parent_ino) | |||
2929 | if (!pm) | 2932 | if (!pm) |
2930 | return -ENOMEM; | 2933 | return -ENOMEM; |
2931 | pm->parent_ino = parent_ino; | 2934 | pm->parent_ino = parent_ino; |
2932 | pm->ino = sctx->cur_ino; | 2935 | pm->ino = ino; |
2933 | pm->gen = sctx->cur_inode_gen; | 2936 | pm->gen = ino_gen; |
2934 | INIT_LIST_HEAD(&pm->list); | 2937 | INIT_LIST_HEAD(&pm->list); |
2935 | INIT_LIST_HEAD(&pm->update_refs); | 2938 | INIT_LIST_HEAD(&pm->update_refs); |
2936 | RB_CLEAR_NODE(&pm->node); | 2939 | RB_CLEAR_NODE(&pm->node); |
@@ -3183,6 +3186,8 @@ static int wait_for_parent_move(struct send_ctx *sctx, | |||
3183 | struct fs_path *path_before = NULL; | 3186 | struct fs_path *path_before = NULL; |
3184 | struct fs_path *path_after = NULL; | 3187 | struct fs_path *path_after = NULL; |
3185 | int len1, len2; | 3188 | int len1, len2; |
3189 | int register_upper_dirs; | ||
3190 | u64 gen; | ||
3186 | 3191 | ||
3187 | if (is_waiting_for_move(sctx, ino)) | 3192 | if (is_waiting_for_move(sctx, ino)) |
3188 | return 1; | 3193 | return 1; |
@@ -3220,7 +3225,7 @@ static int wait_for_parent_move(struct send_ctx *sctx, | |||
3220 | } | 3225 | } |
3221 | 3226 | ||
3222 | ret = get_first_ref(sctx->send_root, ino, &parent_ino_after, | 3227 | ret = get_first_ref(sctx->send_root, ino, &parent_ino_after, |
3223 | NULL, path_after); | 3228 | &gen, path_after); |
3224 | if (ret == -ENOENT) { | 3229 | if (ret == -ENOENT) { |
3225 | ret = 0; | 3230 | ret = 0; |
3226 | goto out; | 3231 | goto out; |
@@ -3237,6 +3242,60 @@ static int wait_for_parent_move(struct send_ctx *sctx, | |||
3237 | } | 3242 | } |
3238 | ret = 0; | 3243 | ret = 0; |
3239 | 3244 | ||
3245 | /* | ||
3246 | * Ok, our new most direct ancestor has a higher inode number but | ||
3247 | * wasn't moved/renamed. So maybe some of the new ancestors higher in | ||
3248 | * the hierarchy have an higher inode number too *and* were renamed | ||
3249 | * or moved - in this case we need to wait for the ancestor's rename | ||
3250 | * or move operation before we can do the move/rename for the current | ||
3251 | * inode. | ||
3252 | */ | ||
3253 | register_upper_dirs = 0; | ||
3254 | ino = parent_ino_after; | ||
3255 | again: | ||
3256 | while ((ret == 0 || register_upper_dirs) && ino > sctx->cur_ino) { | ||
3257 | u64 parent_gen; | ||
3258 | |||
3259 | fs_path_reset(path_before); | ||
3260 | fs_path_reset(path_after); | ||
3261 | |||
3262 | ret = get_first_ref(sctx->send_root, ino, &parent_ino_after, | ||
3263 | &parent_gen, path_after); | ||
3264 | if (ret < 0) | ||
3265 | goto out; | ||
3266 | ret = get_first_ref(sctx->parent_root, ino, &parent_ino_before, | ||
3267 | NULL, path_before); | ||
3268 | if (ret == -ENOENT) { | ||
3269 | ret = 0; | ||
3270 | break; | ||
3271 | } else if (ret < 0) { | ||
3272 | goto out; | ||
3273 | } | ||
3274 | |||
3275 | len1 = fs_path_len(path_before); | ||
3276 | len2 = fs_path_len(path_after); | ||
3277 | if (parent_ino_before != parent_ino_after || len1 != len2 || | ||
3278 | memcmp(path_before->start, path_after->start, len1)) { | ||
3279 | ret = 1; | ||
3280 | if (register_upper_dirs) { | ||
3281 | break; | ||
3282 | } else { | ||
3283 | register_upper_dirs = 1; | ||
3284 | ino = parent_ref->dir; | ||
3285 | gen = parent_ref->dir_gen; | ||
3286 | goto again; | ||
3287 | } | ||
3288 | } else if (register_upper_dirs) { | ||
3289 | ret = add_pending_dir_move(sctx, ino, gen, | ||
3290 | parent_ino_after); | ||
3291 | if (ret < 0 && ret != -EEXIST) | ||
3292 | goto out; | ||
3293 | } | ||
3294 | |||
3295 | ino = parent_ino_after; | ||
3296 | gen = parent_gen; | ||
3297 | } | ||
3298 | |||
3240 | out: | 3299 | out: |
3241 | fs_path_free(path_before); | 3300 | fs_path_free(path_before); |
3242 | fs_path_free(path_after); | 3301 | fs_path_free(path_after); |
@@ -3402,7 +3461,9 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino); | |||
3402 | goto out; | 3461 | goto out; |
3403 | if (ret) { | 3462 | if (ret) { |
3404 | ret = add_pending_dir_move(sctx, | 3463 | ret = add_pending_dir_move(sctx, |
3405 | cur->dir); | 3464 | sctx->cur_ino, |
3465 | sctx->cur_inode_gen, | ||
3466 | cur->dir); | ||
3406 | *pending_move = 1; | 3467 | *pending_move = 1; |
3407 | } else { | 3468 | } else { |
3408 | ret = send_rename(sctx, valid_path, | 3469 | ret = send_rename(sctx, valid_path, |