aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorJosef Bacik <jbacik@fusionio.com>2013-08-16 16:52:55 -0400
committerChris Mason <chris.mason@fusionio.com>2013-09-01 08:15:44 -0400
commitba5e8f2e2d3074bf151dd222dae9bb400e621b82 (patch)
tree626f84ad5ec8836f08e6f712e72cc65cb7a8a8fc /fs
parentdc11dd5d707a4157882f281c96055d6894d10c8c (diff)
Btrfs: fix send issues related to inode number reuse
If you are sending a snapshot and specifying a parent snapshot we will walk the trees and figure out where they differ and send the differences only. The way we check for differences are if the leaves aren't the same and if the keys are not the same within the leaves. So if neither leaf is the same (ie the leaf has been cow'ed from the parent snapshot) we walk each item in the send root and check it against the parent root. If the items match exactly then we don't do anything. This doesn't quite work for inode refs, since they will just have the name and the parent objectid. If you move the file from a directory and then remove that directory and re-create a directory with the same inode number as the old directory and then move that file back into that directory we will assume that nothing changed and you will get errors when you try to receive. In order to fix this we need to do extra checking to see if the inode ref really is the same or not. So do this by passing down BTRFS_COMPARE_TREE_SAME if the items match. Then if the key type is an inode ref we can do some extra checking, otherwise we just keep processing. The extra checking is to look up the generation of the directory in the parent volume and compare it to the generation of the send volume. If they match then they are the same directory and we are good to go. If they don't we have to add them to the changed refs list. This means we have to track the generation of the ref we're trying to lookup when we iterate all the refs for a particular inode. So in the case of looking for new refs we have to get the generation from the parent volume, and in the case of looking for deleted refs we have to get the generation from the send volume to compare with. There was also the issue of using a ulist to keep track of the directories we needed to check. Because we can get a deleted ref and a new ref for the same inode number the ulist won't work since it indexes based on the value. So instead just dup any directory ref we find and add it to a local list, and then process that list as normal and do away with using a ulist for this altogether. Before we would fail all of the tests in the far-progs that related to moving directories (test group 32). With this patch we now pass these tests, and all of the tests in the far-progs send testing suite. Thanks, Signed-off-by: Josef Bacik <jbacik@fusionio.com> Signed-off-by: Chris Mason <chris.mason@fusionio.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/btrfs/ctree.c21
-rw-r--r--fs/btrfs/ctree.h1
-rw-r--r--fs/btrfs/send.c162
3 files changed, 144 insertions, 40 deletions
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
index 5f7a97556583..0708ebed2df7 100644
--- a/fs/btrfs/ctree.c
+++ b/fs/btrfs/ctree.c
@@ -5297,19 +5297,20 @@ int btrfs_compare_trees(struct btrfs_root *left_root,
5297 goto out; 5297 goto out;
5298 advance_right = ADVANCE; 5298 advance_right = ADVANCE;
5299 } else { 5299 } else {
5300 enum btrfs_compare_tree_result cmp;
5301
5300 WARN_ON(!extent_buffer_uptodate(left_path->nodes[0])); 5302 WARN_ON(!extent_buffer_uptodate(left_path->nodes[0]));
5301 ret = tree_compare_item(left_root, left_path, 5303 ret = tree_compare_item(left_root, left_path,
5302 right_path, tmp_buf); 5304 right_path, tmp_buf);
5303 if (ret) { 5305 if (ret)
5304 WARN_ON(!extent_buffer_uptodate(left_path->nodes[0])); 5306 cmp = BTRFS_COMPARE_TREE_CHANGED;
5305 ret = changed_cb(left_root, right_root, 5307 else
5306 left_path, right_path, 5308 cmp = BTRFS_COMPARE_TREE_SAME;
5307 &left_key, 5309 ret = changed_cb(left_root, right_root,
5308 BTRFS_COMPARE_TREE_CHANGED, 5310 left_path, right_path,
5309 ctx); 5311 &left_key, cmp, ctx);
5310 if (ret < 0) 5312 if (ret < 0)
5311 goto out; 5313 goto out;
5312 }
5313 advance_left = ADVANCE; 5314 advance_left = ADVANCE;
5314 advance_right = ADVANCE; 5315 advance_right = ADVANCE;
5315 } 5316 }
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 7c93d9f13812..9766e9f04024 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -3301,6 +3301,7 @@ enum btrfs_compare_tree_result {
3301 BTRFS_COMPARE_TREE_NEW, 3301 BTRFS_COMPARE_TREE_NEW,
3302 BTRFS_COMPARE_TREE_DELETED, 3302 BTRFS_COMPARE_TREE_DELETED,
3303 BTRFS_COMPARE_TREE_CHANGED, 3303 BTRFS_COMPARE_TREE_CHANGED,
3304 BTRFS_COMPARE_TREE_SAME,
3304}; 3305};
3305typedef int (*btrfs_changed_cb_t)(struct btrfs_root *left_root, 3306typedef int (*btrfs_changed_cb_t)(struct btrfs_root *left_root,
3306 struct btrfs_root *right_root, 3307 struct btrfs_root *right_root,
diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
index f8f8b1f3bb2b..fc03a5755a6b 100644
--- a/fs/btrfs/send.c
+++ b/fs/btrfs/send.c
@@ -2630,6 +2630,22 @@ static int record_ref(struct list_head *head, u64 dir,
2630 return 0; 2630 return 0;
2631} 2631}
2632 2632
2633static int dup_ref(struct recorded_ref *ref, struct list_head *list)
2634{
2635 struct recorded_ref *new;
2636
2637 new = kmalloc(sizeof(*ref), GFP_NOFS);
2638 if (!new)
2639 return -ENOMEM;
2640
2641 new->dir = ref->dir;
2642 new->dir_gen = ref->dir_gen;
2643 new->full_path = NULL;
2644 INIT_LIST_HEAD(&new->list);
2645 list_add_tail(&new->list, list);
2646 return 0;
2647}
2648
2633static void __free_recorded_refs(struct list_head *head) 2649static void __free_recorded_refs(struct list_head *head)
2634{ 2650{
2635 struct recorded_ref *cur; 2651 struct recorded_ref *cur;
@@ -2744,9 +2760,7 @@ static int process_recorded_refs(struct send_ctx *sctx)
2744 int ret = 0; 2760 int ret = 0;
2745 struct recorded_ref *cur; 2761 struct recorded_ref *cur;
2746 struct recorded_ref *cur2; 2762 struct recorded_ref *cur2;
2747 struct ulist *check_dirs = NULL; 2763 struct list_head check_dirs;
2748 struct ulist_iterator uit;
2749 struct ulist_node *un;
2750 struct fs_path *valid_path = NULL; 2764 struct fs_path *valid_path = NULL;
2751 u64 ow_inode = 0; 2765 u64 ow_inode = 0;
2752 u64 ow_gen; 2766 u64 ow_gen;
@@ -2760,6 +2774,7 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
2760 * which is always '..' 2774 * which is always '..'
2761 */ 2775 */
2762 BUG_ON(sctx->cur_ino <= BTRFS_FIRST_FREE_OBJECTID); 2776 BUG_ON(sctx->cur_ino <= BTRFS_FIRST_FREE_OBJECTID);
2777 INIT_LIST_HEAD(&check_dirs);
2763 2778
2764 valid_path = fs_path_alloc(); 2779 valid_path = fs_path_alloc();
2765 if (!valid_path) { 2780 if (!valid_path) {
@@ -2767,12 +2782,6 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
2767 goto out; 2782 goto out;
2768 } 2783 }
2769 2784
2770 check_dirs = ulist_alloc(GFP_NOFS);
2771 if (!check_dirs) {
2772 ret = -ENOMEM;
2773 goto out;
2774 }
2775
2776 /* 2785 /*
2777 * First, check if the first ref of the current inode was overwritten 2786 * First, check if the first ref of the current inode was overwritten
2778 * before. If yes, we know that the current inode was already orphanized 2787 * before. If yes, we know that the current inode was already orphanized
@@ -2909,8 +2918,7 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
2909 goto out; 2918 goto out;
2910 } 2919 }
2911 } 2920 }
2912 ret = ulist_add(check_dirs, cur->dir, cur->dir_gen, 2921 ret = dup_ref(cur, &check_dirs);
2913 GFP_NOFS);
2914 if (ret < 0) 2922 if (ret < 0)
2915 goto out; 2923 goto out;
2916 } 2924 }
@@ -2938,8 +2946,7 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
2938 } 2946 }
2939 2947
2940 list_for_each_entry(cur, &sctx->deleted_refs, list) { 2948 list_for_each_entry(cur, &sctx->deleted_refs, list) {
2941 ret = ulist_add(check_dirs, cur->dir, cur->dir_gen, 2949 ret = dup_ref(cur, &check_dirs);
2942 GFP_NOFS);
2943 if (ret < 0) 2950 if (ret < 0)
2944 goto out; 2951 goto out;
2945 } 2952 }
@@ -2950,8 +2957,7 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
2950 */ 2957 */
2951 cur = list_entry(sctx->deleted_refs.next, struct recorded_ref, 2958 cur = list_entry(sctx->deleted_refs.next, struct recorded_ref,
2952 list); 2959 list);
2953 ret = ulist_add(check_dirs, cur->dir, cur->dir_gen, 2960 ret = dup_ref(cur, &check_dirs);
2954 GFP_NOFS);
2955 if (ret < 0) 2961 if (ret < 0)
2956 goto out; 2962 goto out;
2957 } else if (!S_ISDIR(sctx->cur_inode_mode)) { 2963 } else if (!S_ISDIR(sctx->cur_inode_mode)) {
@@ -2971,12 +2977,10 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
2971 if (ret < 0) 2977 if (ret < 0)
2972 goto out; 2978 goto out;
2973 } 2979 }
2974 ret = ulist_add(check_dirs, cur->dir, cur->dir_gen, 2980 ret = dup_ref(cur, &check_dirs);
2975 GFP_NOFS);
2976 if (ret < 0) 2981 if (ret < 0)
2977 goto out; 2982 goto out;
2978 } 2983 }
2979
2980 /* 2984 /*
2981 * If the inode is still orphan, unlink the orphan. This may 2985 * If the inode is still orphan, unlink the orphan. This may
2982 * happen when a previous inode did overwrite the first ref 2986 * happen when a previous inode did overwrite the first ref
@@ -2998,33 +3002,32 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
2998 * deletion and if it's finally possible to perform the rmdir now. 3002 * deletion and if it's finally possible to perform the rmdir now.
2999 * We also update the inode stats of the parent dirs here. 3003 * We also update the inode stats of the parent dirs here.
3000 */ 3004 */
3001 ULIST_ITER_INIT(&uit); 3005 list_for_each_entry(cur, &check_dirs, list) {
3002 while ((un = ulist_next(check_dirs, &uit))) {
3003 /* 3006 /*
3004 * In case we had refs into dirs that were not processed yet, 3007 * In case we had refs into dirs that were not processed yet,
3005 * we don't need to do the utime and rmdir logic for these dirs. 3008 * we don't need to do the utime and rmdir logic for these dirs.
3006 * The dir will be processed later. 3009 * The dir will be processed later.
3007 */ 3010 */
3008 if (un->val > sctx->cur_ino) 3011 if (cur->dir > sctx->cur_ino)
3009 continue; 3012 continue;
3010 3013
3011 ret = get_cur_inode_state(sctx, un->val, un->aux); 3014 ret = get_cur_inode_state(sctx, cur->dir, cur->dir_gen);
3012 if (ret < 0) 3015 if (ret < 0)
3013 goto out; 3016 goto out;
3014 3017
3015 if (ret == inode_state_did_create || 3018 if (ret == inode_state_did_create ||
3016 ret == inode_state_no_change) { 3019 ret == inode_state_no_change) {
3017 /* TODO delayed utimes */ 3020 /* TODO delayed utimes */
3018 ret = send_utimes(sctx, un->val, un->aux); 3021 ret = send_utimes(sctx, cur->dir, cur->dir_gen);
3019 if (ret < 0) 3022 if (ret < 0)
3020 goto out; 3023 goto out;
3021 } else if (ret == inode_state_did_delete) { 3024 } else if (ret == inode_state_did_delete) {
3022 ret = can_rmdir(sctx, un->val, sctx->cur_ino); 3025 ret = can_rmdir(sctx, cur->dir, sctx->cur_ino);
3023 if (ret < 0) 3026 if (ret < 0)
3024 goto out; 3027 goto out;
3025 if (ret) { 3028 if (ret) {
3026 ret = get_cur_path(sctx, un->val, un->aux, 3029 ret = get_cur_path(sctx, cur->dir,
3027 valid_path); 3030 cur->dir_gen, valid_path);
3028 if (ret < 0) 3031 if (ret < 0)
3029 goto out; 3032 goto out;
3030 ret = send_rmdir(sctx, valid_path); 3033 ret = send_rmdir(sctx, valid_path);
@@ -3037,8 +3040,8 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
3037 ret = 0; 3040 ret = 0;
3038 3041
3039out: 3042out:
3043 __free_recorded_refs(&check_dirs);
3040 free_recorded_refs(sctx); 3044 free_recorded_refs(sctx);
3041 ulist_free(check_dirs);
3042 fs_path_free(valid_path); 3045 fs_path_free(valid_path);
3043 return ret; 3046 return ret;
3044} 3047}
@@ -3139,6 +3142,8 @@ out:
3139 3142
3140struct find_ref_ctx { 3143struct find_ref_ctx {
3141 u64 dir; 3144 u64 dir;
3145 u64 dir_gen;
3146 struct btrfs_root *root;
3142 struct fs_path *name; 3147 struct fs_path *name;
3143 int found_idx; 3148 int found_idx;
3144}; 3149};
@@ -3148,9 +3153,21 @@ static int __find_iref(int num, u64 dir, int index,
3148 void *ctx_) 3153 void *ctx_)
3149{ 3154{
3150 struct find_ref_ctx *ctx = ctx_; 3155 struct find_ref_ctx *ctx = ctx_;
3156 u64 dir_gen;
3157 int ret;
3151 3158
3152 if (dir == ctx->dir && fs_path_len(name) == fs_path_len(ctx->name) && 3159 if (dir == ctx->dir && fs_path_len(name) == fs_path_len(ctx->name) &&
3153 strncmp(name->start, ctx->name->start, fs_path_len(name)) == 0) { 3160 strncmp(name->start, ctx->name->start, fs_path_len(name)) == 0) {
3161 /*
3162 * To avoid doing extra lookups we'll only do this if everything
3163 * else matches.
3164 */
3165 ret = get_inode_info(ctx->root, dir, NULL, &dir_gen, NULL,
3166 NULL, NULL, NULL);
3167 if (ret)
3168 return ret;
3169 if (dir_gen != ctx->dir_gen)
3170 return 0;
3154 ctx->found_idx = num; 3171 ctx->found_idx = num;
3155 return 1; 3172 return 1;
3156 } 3173 }
@@ -3160,14 +3177,16 @@ static int __find_iref(int num, u64 dir, int index,
3160static int find_iref(struct btrfs_root *root, 3177static int find_iref(struct btrfs_root *root,
3161 struct btrfs_path *path, 3178 struct btrfs_path *path,
3162 struct btrfs_key *key, 3179 struct btrfs_key *key,
3163 u64 dir, struct fs_path *name) 3180 u64 dir, u64 dir_gen, struct fs_path *name)
3164{ 3181{
3165 int ret; 3182 int ret;
3166 struct find_ref_ctx ctx; 3183 struct find_ref_ctx ctx;
3167 3184
3168 ctx.dir = dir; 3185 ctx.dir = dir;
3169 ctx.name = name; 3186 ctx.name = name;
3187 ctx.dir_gen = dir_gen;
3170 ctx.found_idx = -1; 3188 ctx.found_idx = -1;
3189 ctx.root = root;
3171 3190
3172 ret = iterate_inode_ref(root, path, key, 0, __find_iref, &ctx); 3191 ret = iterate_inode_ref(root, path, key, 0, __find_iref, &ctx);
3173 if (ret < 0) 3192 if (ret < 0)
@@ -3183,11 +3202,17 @@ static int __record_changed_new_ref(int num, u64 dir, int index,
3183 struct fs_path *name, 3202 struct fs_path *name,
3184 void *ctx) 3203 void *ctx)
3185{ 3204{
3205 u64 dir_gen;
3186 int ret; 3206 int ret;
3187 struct send_ctx *sctx = ctx; 3207 struct send_ctx *sctx = ctx;
3188 3208
3209 ret = get_inode_info(sctx->send_root, dir, NULL, &dir_gen, NULL,
3210 NULL, NULL, NULL);
3211 if (ret)
3212 return ret;
3213
3189 ret = find_iref(sctx->parent_root, sctx->right_path, 3214 ret = find_iref(sctx->parent_root, sctx->right_path,
3190 sctx->cmp_key, dir, name); 3215 sctx->cmp_key, dir, dir_gen, name);
3191 if (ret == -ENOENT) 3216 if (ret == -ENOENT)
3192 ret = __record_new_ref(num, dir, index, name, sctx); 3217 ret = __record_new_ref(num, dir, index, name, sctx);
3193 else if (ret > 0) 3218 else if (ret > 0)
@@ -3200,11 +3225,17 @@ static int __record_changed_deleted_ref(int num, u64 dir, int index,
3200 struct fs_path *name, 3225 struct fs_path *name,
3201 void *ctx) 3226 void *ctx)
3202{ 3227{
3228 u64 dir_gen;
3203 int ret; 3229 int ret;
3204 struct send_ctx *sctx = ctx; 3230 struct send_ctx *sctx = ctx;
3205 3231
3232 ret = get_inode_info(sctx->parent_root, dir, NULL, &dir_gen, NULL,
3233 NULL, NULL, NULL);
3234 if (ret)
3235 return ret;
3236
3206 ret = find_iref(sctx->send_root, sctx->left_path, sctx->cmp_key, 3237 ret = find_iref(sctx->send_root, sctx->left_path, sctx->cmp_key,
3207 dir, name); 3238 dir, dir_gen, name);
3208 if (ret == -ENOENT) 3239 if (ret == -ENOENT)
3209 ret = __record_deleted_ref(num, dir, index, name, sctx); 3240 ret = __record_deleted_ref(num, dir, index, name, sctx);
3210 else if (ret > 0) 3241 else if (ret > 0)
@@ -4381,6 +4412,64 @@ static int changed_extent(struct send_ctx *sctx,
4381 return ret; 4412 return ret;
4382} 4413}
4383 4414
4415static int dir_changed(struct send_ctx *sctx, u64 dir)
4416{
4417 u64 orig_gen, new_gen;
4418 int ret;
4419
4420 ret = get_inode_info(sctx->send_root, dir, NULL, &new_gen, NULL, NULL,
4421 NULL, NULL);
4422 if (ret)
4423 return ret;
4424
4425 ret = get_inode_info(sctx->parent_root, dir, NULL, &orig_gen, NULL,
4426 NULL, NULL, NULL);
4427 if (ret)
4428 return ret;
4429
4430 return (orig_gen != new_gen) ? 1 : 0;
4431}
4432
4433static int compare_refs(struct send_ctx *sctx, struct btrfs_path *path,
4434 struct btrfs_key *key)
4435{
4436 struct btrfs_inode_extref *extref;
4437 struct extent_buffer *leaf;
4438 u64 dirid = 0, last_dirid = 0;
4439 unsigned long ptr;
4440 u32 item_size;
4441 u32 cur_offset = 0;
4442 int ref_name_len;
4443 int ret = 0;
4444
4445 /* Easy case, just check this one dirid */
4446 if (key->type == BTRFS_INODE_REF_KEY) {
4447 dirid = key->offset;
4448
4449 ret = dir_changed(sctx, dirid);
4450 goto out;
4451 }
4452
4453 leaf = path->nodes[0];
4454 item_size = btrfs_item_size_nr(leaf, path->slots[0]);
4455 ptr = btrfs_item_ptr_offset(leaf, path->slots[0]);
4456 while (cur_offset < item_size) {
4457 extref = (struct btrfs_inode_extref *)(ptr +
4458 cur_offset);
4459 dirid = btrfs_inode_extref_parent(leaf, extref);
4460 ref_name_len = btrfs_inode_extref_name_len(leaf, extref);
4461 cur_offset += ref_name_len + sizeof(*extref);
4462 if (dirid == last_dirid)
4463 continue;
4464 ret = dir_changed(sctx, dirid);
4465 if (ret)
4466 break;
4467 last_dirid = dirid;
4468 }
4469out:
4470 return ret;
4471}
4472
4384/* 4473/*
4385 * Updates compare related fields in sctx and simply forwards to the actual 4474 * Updates compare related fields in sctx and simply forwards to the actual
4386 * changed_xxx functions. 4475 * changed_xxx functions.
@@ -4396,6 +4485,19 @@ static int changed_cb(struct btrfs_root *left_root,
4396 int ret = 0; 4485 int ret = 0;
4397 struct send_ctx *sctx = ctx; 4486 struct send_ctx *sctx = ctx;
4398 4487
4488 if (result == BTRFS_COMPARE_TREE_SAME) {
4489 if (key->type != BTRFS_INODE_REF_KEY &&
4490 key->type != BTRFS_INODE_EXTREF_KEY)
4491 return 0;
4492 ret = compare_refs(sctx, left_path, key);
4493 if (!ret)
4494 return 0;
4495 if (ret < 0)
4496 return ret;
4497 result = BTRFS_COMPARE_TREE_CHANGED;
4498 ret = 0;
4499 }
4500
4399 sctx->left_path = left_path; 4501 sctx->left_path = left_path;
4400 sctx->right_path = right_path; 4502 sctx->right_path = right_path;
4401 sctx->cmp_key = key; 4503 sctx->cmp_key = key;