aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs/send.c
diff options
context:
space:
mode:
authorFilipe Manana <fdmanana@gmail.com>2014-03-27 16:14:01 -0400
committerChris Mason <clm@fb.com>2014-06-09 20:20:40 -0400
commitf959492fc15b60d874a9cbf55ae4760f2ef261ed (patch)
treef3b5429a32feef8110f8e8c74b1529f2127e010b /fs/btrfs/send.c
parenta10c40766c30a002affc0c47dd515d048c3959b4 (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.c190
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,
2940static int add_pending_dir_move(struct send_ctx *sctx, 2940static 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
3025static 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
3023static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm) 3067static 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:
3185static int wait_for_parent_move(struct send_ctx *sctx, 3244static 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)) {
3261again: 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
3305out: 3300out:
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,