diff options
author | Filipe Manana <fdmanana@gmail.com> | 2014-03-27 16:14:01 -0400 |
---|---|---|
committer | Chris Mason <clm@fb.com> | 2014-06-09 20:20:40 -0400 |
commit | f959492fc15b60d874a9cbf55ae4760f2ef261ed (patch) | |
tree | f3b5429a32feef8110f8e8c74b1529f2127e010b /fs/btrfs/send.c | |
parent | a10c40766c30a002affc0c47dd515d048c3959b4 (diff) |
Btrfs: send, fix more issues related to directory renames
This is a continuation of the previous changes titled:
Btrfs: fix incremental send's decision to delay a dir move/rename
Btrfs: part 2, fix incremental send's decision to delay a dir move/rename
There's a few more cases where a directory rename/move must be delayed which was
previously overlooked. If our immediate ancestor has a lower inode number than
ours and it doesn't have a delayed rename/move operation associated to it, it
doesn't mean there isn't any non-direct ancestor of our current inode that needs
to be renamed/moved before our current inode (i.e. with a higher inode number
than ours).
So we can't stop the search if our immediate ancestor has a lower inode number than
ours, we need to navigate the directory hierarchy upwards until we hit the root or:
1) find an ancestor with an higher inode number that was renamed/moved in the send
root too (or already has a pending rename/move registered);
2) find an ancestor that is a new directory (higher inode number than ours and
exists only in the send root).
Reproducer for case 1)
$ mkfs.btrfs -f /dev/sdd
$ mount /dev/sdd /mnt
$ mkdir -p /mnt/a/b
$ mkdir -p /mnt/a/c/d
$ mkdir /mnt/a/b/e
$ mkdir /mnt/a/c/d/f
$ mv /mnt/a/b /mnt/a/c/d/2b
$ mkdir /mnt/a/x
$ mkdir /mnt/a/y
$ btrfs subvolume snapshot -r /mnt /mnt/snap1
$ btrfs send /mnt/snap1 -f /tmp/base.send
$ mv /mnt/a/x /mnt/a/y
$ mv /mnt/a/c/d/2b/e /mnt/a/c/d/2b/2e
$ mv /mnt/a/c/d /mnt/a/h/2d
$ mv /mnt/a/c /mnt/a/h/2d/2b/2c
$ btrfs subvolume snapshot -r /mnt /mnt/snap2
$ btrfs send -p /mnt/snap1 /mnt/snap2 -f /tmp/incremental.send
Simple reproducer for case 2)
$ mkfs.btrfs -f /dev/sdd
$ mount /dev/sdd /mnt
$ mkdir -p /mnt/a/b
$ mkdir /mnt/a/c
$ mv /mnt/a/b /mnt/a/c/b2
$ mkdir /mnt/a/e
$ btrfs subvolume snapshot -r /mnt /mnt/snap1
$ btrfs send /mnt/snap1 -f /tmp/base.send
$ mv /mnt/a/c/b2 /mnt/a/e/b3
$ mkdir /mnt/a/e/b3/f
$ mkdir /mnt/a/h
$ mv /mnt/a/c /mnt/a/e/b3/f/c2
$ mv /mnt/a/e /mnt/a/h/e2
$ btrfs subvolume snapshot -r /mnt /mnt/snap2
$ btrfs send -p /mnt/snap1 /mnt/snap2 -f /tmp/incremental.send
Another simple reproducer for case 2)
$ mkfs.btrfs -f /dev/sdd
$ mount /dev/sdd /mnt
$ mkdir -p /mnt/a/b
$ mkdir /mnt/a/c
$ mkdir /mnt/a/b/d
$ mkdir /mnt/a/c/e
$ btrfs subvolume snapshot -r /mnt /mnt/snap1
$ btrfs send /mnt/snap1 -f /tmp/base.send
$ mkdir /mnt/a/b/d/f
$ mkdir /mnt/a/b/g
$ mv /mnt/a/c/e /mnt/a/b/g/e2
$ mv /mnt/a/c /mnt/a/b/d/f/c2
$ mv /mnt/a/b/d/f /mnt/a/b/g/e2/f2
$ btrfs subvolume snapshot -r /mnt /mnt/snap2
$ btrfs send -p /mnt/snap1 /mnt/snap2 -f /tmp/incremental.send
More complex reproducer for case 2)
$ mkfs.btrfs -f /dev/sdd
$ mount /dev/sdd /mnt
$ mkdir -p /mnt/a/b
$ mkdir -p /mnt/a/c/d
$ mkdir /mnt/a/b/e
$ mkdir /mnt/a/c/d/f
$ mv /mnt/a/b /mnt/a/c/d/2b
$ mkdir /mnt/a/x
$ mkdir /mnt/a/y
$ btrfs subvolume snapshot -r /mnt /mnt/snap1
$ btrfs send /mnt/snap1 -f /tmp/base.send
$ mv /mnt/a/x /mnt/a/y
$ mv /mnt/a/c/d/2b/e /mnt/a/c/d/2b/2e
$ mv /mnt/a/c/d /mnt/a/h/2d
$ mv /mnt/a/c /mnt/a/h/2d/2b/2c
$ btrfs subvolume snapshot -r /mnt /mnt/snap2
$ btrfs send -p /mnt/snap1 /mnt/snap2 -f /tmp/incremental.send
For both cases the incremental send would enter an infinite loop when building
path strings.
While solving these cases, this change also re-implements the code to detect
when directory moves/renames should be delayed. Instead of dealing with several
specific cases separately, it's now more generic handling all cases with a simple
detection algorithm and if when applying a delayed move/rename there's a path loop
detected, it further delays the move/rename registering a new ancestor inode as
the dependency inode (so our rename happens after that ancestor is renamed).
Tests for these cases is being added to xfstests too.
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 | 190 |
1 files changed, 96 insertions, 94 deletions
diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index fb6aeed9c223..3f14b31c0c96 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c | |||
@@ -2940,7 +2940,9 @@ static void free_waiting_dir_move(struct send_ctx *sctx, | |||
2940 | static int add_pending_dir_move(struct send_ctx *sctx, | 2940 | static int add_pending_dir_move(struct send_ctx *sctx, |
2941 | u64 ino, | 2941 | u64 ino, |
2942 | u64 ino_gen, | 2942 | u64 ino_gen, |
2943 | u64 parent_ino) | 2943 | u64 parent_ino, |
2944 | struct list_head *new_refs, | ||
2945 | struct list_head *deleted_refs) | ||
2944 | { | 2946 | { |
2945 | struct rb_node **p = &sctx->pending_dir_moves.rb_node; | 2947 | struct rb_node **p = &sctx->pending_dir_moves.rb_node; |
2946 | struct rb_node *parent = NULL; | 2948 | struct rb_node *parent = NULL; |
@@ -2972,12 +2974,12 @@ static int add_pending_dir_move(struct send_ctx *sctx, | |||
2972 | } | 2974 | } |
2973 | } | 2975 | } |
2974 | 2976 | ||
2975 | list_for_each_entry(cur, &sctx->deleted_refs, list) { | 2977 | list_for_each_entry(cur, deleted_refs, list) { |
2976 | ret = dup_ref(cur, &pm->update_refs); | 2978 | ret = dup_ref(cur, &pm->update_refs); |
2977 | if (ret < 0) | 2979 | if (ret < 0) |
2978 | goto out; | 2980 | goto out; |
2979 | } | 2981 | } |
2980 | list_for_each_entry(cur, &sctx->new_refs, list) { | 2982 | list_for_each_entry(cur, new_refs, list) { |
2981 | ret = dup_ref(cur, &pm->update_refs); | 2983 | ret = dup_ref(cur, &pm->update_refs); |
2982 | if (ret < 0) | 2984 | if (ret < 0) |
2983 | goto out; | 2985 | goto out; |
@@ -3020,6 +3022,48 @@ static struct pending_dir_move *get_pending_dir_moves(struct send_ctx *sctx, | |||
3020 | return NULL; | 3022 | return NULL; |
3021 | } | 3023 | } |
3022 | 3024 | ||
3025 | static int path_loop(struct send_ctx *sctx, struct fs_path *name, | ||
3026 | u64 ino, u64 gen, u64 *ancestor_ino) | ||
3027 | { | ||
3028 | int ret = 0; | ||
3029 | u64 parent_inode = 0; | ||
3030 | u64 parent_gen = 0; | ||
3031 | u64 start_ino = ino; | ||
3032 | |||
3033 | *ancestor_ino = 0; | ||
3034 | while (ino != BTRFS_FIRST_FREE_OBJECTID) { | ||
3035 | fs_path_reset(name); | ||
3036 | |||
3037 | if (is_waiting_for_rm(sctx, ino)) | ||
3038 | break; | ||
3039 | if (is_waiting_for_move(sctx, ino)) { | ||
3040 | if (*ancestor_ino == 0) | ||
3041 | *ancestor_ino = ino; | ||
3042 | ret = get_first_ref(sctx->parent_root, ino, | ||
3043 | &parent_inode, &parent_gen, name); | ||
3044 | } else { | ||
3045 | ret = __get_cur_name_and_parent(sctx, ino, gen, | ||
3046 | &parent_inode, | ||
3047 | &parent_gen, name); | ||
3048 | if (ret > 0) { | ||
3049 | ret = 0; | ||
3050 | break; | ||
3051 | } | ||
3052 | } | ||
3053 | if (ret < 0) | ||
3054 | break; | ||
3055 | if (parent_inode == start_ino) { | ||
3056 | ret = 1; | ||
3057 | if (*ancestor_ino == 0) | ||
3058 | *ancestor_ino = ino; | ||
3059 | break; | ||
3060 | } | ||
3061 | ino = parent_inode; | ||
3062 | gen = parent_gen; | ||
3063 | } | ||
3064 | return ret; | ||
3065 | } | ||
3066 | |||
3023 | static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm) | 3067 | static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm) |
3024 | { | 3068 | { |
3025 | struct fs_path *from_path = NULL; | 3069 | struct fs_path *from_path = NULL; |
@@ -3031,6 +3075,7 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm) | |||
3031 | struct waiting_dir_move *dm = NULL; | 3075 | struct waiting_dir_move *dm = NULL; |
3032 | u64 rmdir_ino = 0; | 3076 | u64 rmdir_ino = 0; |
3033 | int ret; | 3077 | int ret; |
3078 | u64 ancestor = 0; | ||
3034 | 3079 | ||
3035 | name = fs_path_alloc(); | 3080 | name = fs_path_alloc(); |
3036 | from_path = fs_path_alloc(); | 3081 | from_path = fs_path_alloc(); |
@@ -3057,11 +3102,25 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm) | |||
3057 | if (ret < 0) | 3102 | if (ret < 0) |
3058 | goto out; | 3103 | goto out; |
3059 | 3104 | ||
3105 | sctx->send_progress = sctx->cur_ino + 1; | ||
3106 | ret = path_loop(sctx, name, pm->ino, pm->gen, &ancestor); | ||
3107 | if (ret) { | ||
3108 | LIST_HEAD(deleted_refs); | ||
3109 | ASSERT(ancestor > BTRFS_FIRST_FREE_OBJECTID); | ||
3110 | ret = add_pending_dir_move(sctx, pm->ino, pm->gen, ancestor, | ||
3111 | &pm->update_refs, &deleted_refs); | ||
3112 | if (ret < 0) | ||
3113 | goto out; | ||
3114 | if (rmdir_ino) { | ||
3115 | dm = get_waiting_dir_move(sctx, pm->ino); | ||
3116 | ASSERT(dm); | ||
3117 | dm->rmdir_ino = rmdir_ino; | ||
3118 | } | ||
3119 | goto out; | ||
3120 | } | ||
3060 | fs_path_reset(name); | 3121 | fs_path_reset(name); |
3061 | to_path = name; | 3122 | to_path = name; |
3062 | name = NULL; | 3123 | name = NULL; |
3063 | |||
3064 | sctx->send_progress = sctx->cur_ino + 1; | ||
3065 | ret = get_cur_path(sctx, pm->ino, pm->gen, to_path); | 3124 | ret = get_cur_path(sctx, pm->ino, pm->gen, to_path); |
3066 | if (ret < 0) | 3125 | if (ret < 0) |
3067 | goto out; | 3126 | goto out; |
@@ -3185,127 +3244,74 @@ out: | |||
3185 | static int wait_for_parent_move(struct send_ctx *sctx, | 3244 | static int wait_for_parent_move(struct send_ctx *sctx, |
3186 | struct recorded_ref *parent_ref) | 3245 | struct recorded_ref *parent_ref) |
3187 | { | 3246 | { |
3188 | int ret; | 3247 | int ret = 0; |
3189 | u64 ino = parent_ref->dir; | 3248 | u64 ino = parent_ref->dir; |
3190 | u64 parent_ino_before, parent_ino_after; | 3249 | u64 parent_ino_before, parent_ino_after; |
3191 | u64 old_gen; | ||
3192 | struct fs_path *path_before = NULL; | 3250 | struct fs_path *path_before = NULL; |
3193 | struct fs_path *path_after = NULL; | 3251 | struct fs_path *path_after = NULL; |
3194 | int len1, len2; | 3252 | int len1, len2; |
3195 | int register_upper_dirs; | ||
3196 | u64 gen; | ||
3197 | |||
3198 | if (is_waiting_for_move(sctx, ino)) | ||
3199 | return 1; | ||
3200 | |||
3201 | if (parent_ref->dir <= sctx->cur_ino) | ||
3202 | return 0; | ||
3203 | |||
3204 | ret = get_inode_info(sctx->parent_root, ino, NULL, &old_gen, | ||
3205 | NULL, NULL, NULL, NULL); | ||
3206 | if (ret == -ENOENT) | ||
3207 | return 0; | ||
3208 | else if (ret < 0) | ||
3209 | return ret; | ||
3210 | |||
3211 | if (parent_ref->dir_gen != old_gen) | ||
3212 | return 0; | ||
3213 | |||
3214 | path_before = fs_path_alloc(); | ||
3215 | if (!path_before) | ||
3216 | return -ENOMEM; | ||
3217 | |||
3218 | ret = get_first_ref(sctx->parent_root, ino, &parent_ino_before, | ||
3219 | NULL, path_before); | ||
3220 | if (ret == -ENOENT) { | ||
3221 | ret = 0; | ||
3222 | goto out; | ||
3223 | } else if (ret < 0) { | ||
3224 | goto out; | ||
3225 | } | ||
3226 | 3253 | ||
3227 | path_after = fs_path_alloc(); | 3254 | path_after = fs_path_alloc(); |
3228 | if (!path_after) { | 3255 | path_before = fs_path_alloc(); |
3256 | if (!path_after || !path_before) { | ||
3229 | ret = -ENOMEM; | 3257 | ret = -ENOMEM; |
3230 | goto out; | 3258 | goto out; |
3231 | } | 3259 | } |
3232 | 3260 | ||
3233 | ret = get_first_ref(sctx->send_root, ino, &parent_ino_after, | ||
3234 | &gen, path_after); | ||
3235 | if (ret == -ENOENT) { | ||
3236 | ret = 0; | ||
3237 | goto out; | ||
3238 | } else if (ret < 0) { | ||
3239 | goto out; | ||
3240 | } | ||
3241 | |||
3242 | len1 = fs_path_len(path_before); | ||
3243 | len2 = fs_path_len(path_after); | ||
3244 | if (parent_ino_before != parent_ino_after || len1 != len2 || | ||
3245 | memcmp(path_before->start, path_after->start, len1)) { | ||
3246 | ret = 1; | ||
3247 | goto out; | ||
3248 | } | ||
3249 | ret = 0; | ||
3250 | |||
3251 | /* | 3261 | /* |
3252 | * Ok, our new most direct ancestor has a higher inode number but | 3262 | * Our current directory inode may not yet be renamed/moved because some |
3253 | * wasn't moved/renamed. So maybe some of the new ancestors higher in | 3263 | * ancestor (immediate or not) has to be renamed/moved first. So find if |
3254 | * the hierarchy have an higher inode number too *and* were renamed | 3264 | * such ancestor exists and make sure our own rename/move happens after |
3255 | * or moved - in this case we need to wait for the ancestor's rename | 3265 | * that ancestor is processed. |
3256 | * or move operation before we can do the move/rename for the current | ||
3257 | * inode. | ||
3258 | */ | 3266 | */ |
3259 | register_upper_dirs = 0; | 3267 | while (ino > BTRFS_FIRST_FREE_OBJECTID) { |
3260 | ino = parent_ino_after; | 3268 | if (is_waiting_for_move(sctx, ino)) { |
3261 | again: | 3269 | ret = 1; |
3262 | while ((ret == 0 || register_upper_dirs) && ino > sctx->cur_ino) { | 3270 | break; |
3263 | u64 parent_gen; | 3271 | } |
3264 | 3272 | ||
3265 | fs_path_reset(path_before); | 3273 | fs_path_reset(path_before); |
3266 | fs_path_reset(path_after); | 3274 | fs_path_reset(path_after); |
3267 | 3275 | ||
3268 | ret = get_first_ref(sctx->send_root, ino, &parent_ino_after, | 3276 | ret = get_first_ref(sctx->send_root, ino, &parent_ino_after, |
3269 | &parent_gen, path_after); | 3277 | NULL, path_after); |
3270 | if (ret < 0) | 3278 | if (ret < 0) |
3271 | goto out; | 3279 | goto out; |
3272 | ret = get_first_ref(sctx->parent_root, ino, &parent_ino_before, | 3280 | ret = get_first_ref(sctx->parent_root, ino, &parent_ino_before, |
3273 | NULL, path_before); | 3281 | NULL, path_before); |
3274 | if (ret == -ENOENT) { | 3282 | if (ret < 0 && ret != -ENOENT) { |
3275 | ret = 0; | ||
3276 | break; | ||
3277 | } else if (ret < 0) { | ||
3278 | goto out; | 3283 | goto out; |
3284 | } else if (ret == -ENOENT) { | ||
3285 | ret = 1; | ||
3286 | break; | ||
3279 | } | 3287 | } |
3280 | 3288 | ||
3281 | len1 = fs_path_len(path_before); | 3289 | len1 = fs_path_len(path_before); |
3282 | len2 = fs_path_len(path_after); | 3290 | len2 = fs_path_len(path_after); |
3283 | if (parent_ino_before != parent_ino_after || len1 != len2 || | 3291 | if (ino > sctx->cur_ino && |
3284 | memcmp(path_before->start, path_after->start, len1)) { | 3292 | (parent_ino_before != parent_ino_after || len1 != len2 || |
3293 | memcmp(path_before->start, path_after->start, len1))) { | ||
3285 | ret = 1; | 3294 | ret = 1; |
3286 | if (register_upper_dirs) { | 3295 | break; |
3287 | break; | ||
3288 | } else { | ||
3289 | register_upper_dirs = 1; | ||
3290 | ino = parent_ref->dir; | ||
3291 | gen = parent_ref->dir_gen; | ||
3292 | goto again; | ||
3293 | } | ||
3294 | } else if (register_upper_dirs) { | ||
3295 | ret = add_pending_dir_move(sctx, ino, gen, | ||
3296 | parent_ino_after); | ||
3297 | if (ret < 0 && ret != -EEXIST) | ||
3298 | goto out; | ||
3299 | } | 3296 | } |
3300 | |||
3301 | ino = parent_ino_after; | 3297 | ino = parent_ino_after; |
3302 | gen = parent_gen; | ||
3303 | } | 3298 | } |
3304 | 3299 | ||
3305 | out: | 3300 | out: |
3306 | fs_path_free(path_before); | 3301 | fs_path_free(path_before); |
3307 | fs_path_free(path_after); | 3302 | fs_path_free(path_after); |
3308 | 3303 | ||
3304 | if (ret == 1) { | ||
3305 | ret = add_pending_dir_move(sctx, | ||
3306 | sctx->cur_ino, | ||
3307 | sctx->cur_inode_gen, | ||
3308 | ino, | ||
3309 | &sctx->new_refs, | ||
3310 | &sctx->deleted_refs); | ||
3311 | if (!ret) | ||
3312 | ret = 1; | ||
3313 | } | ||
3314 | |||
3309 | return ret; | 3315 | return ret; |
3310 | } | 3316 | } |
3311 | 3317 | ||
@@ -3466,10 +3472,6 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino); | |||
3466 | if (ret < 0) | 3472 | if (ret < 0) |
3467 | goto out; | 3473 | goto out; |
3468 | if (ret) { | 3474 | if (ret) { |
3469 | ret = add_pending_dir_move(sctx, | ||
3470 | sctx->cur_ino, | ||
3471 | sctx->cur_inode_gen, | ||
3472 | cur->dir); | ||
3473 | *pending_move = 1; | 3475 | *pending_move = 1; |
3474 | } else { | 3476 | } else { |
3475 | ret = send_rename(sctx, valid_path, | 3477 | ret = send_rename(sctx, valid_path, |