aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs/send.c
diff options
context:
space:
mode:
authorFilipe Manana <fdmanana@gmail.com>2014-02-16 08:43:11 -0500
committerJosef Bacik <jbacik@fb.com>2014-03-10 15:16:48 -0400
commit2b863a135f22f242ba4fc669f3a6b2f6c826832c (patch)
treef03db49dfa53f7d2988c9d635715cc2481efacdd /fs/btrfs/send.c
parent12870f1c9b2de7d475d22e73fd7db1b418599725 (diff)
Btrfs: incremental send, fix invalid path after dir rename
This fixes yet one more case not caught by the commit titled: Btrfs: fix infinite path build loops in incremental send In this case, even before the initial full send, we have a directory which is a child of a directory with a higher inode number. Then we perform the initial send, and after we rename both the child and the parent, without moving them around. After doing these 2 renames, an incremental send sent a rename instruction for the child directory which contained an invalid "from" path (referenced the parent's old name, not the new one), which made the btrfs receive command fail. Steps to reproduce: $ mkfs.btrfs -f /dev/sdb3 $ mount /dev/sdb3 /mnt/btrfs $ mkdir -p /mnt/btrfs/a/b $ mkdir /mnt/btrfs/d $ mkdir /mnt/btrfs/a/b/c $ mv /mnt/btrfs/d /mnt/btrfs/a/b/c $ btrfs subvolume snapshot -r /mnt/btrfs /mnt/btrfs/snap1 $ btrfs send /mnt/btrfs/snap1 -f /tmp/base.send $ mv /mnt/btrfs/a/b/c /mnt/btrfs/a/b/x $ mv /mnt/btrfs/a/b/x/d /mnt/btrfs/a/b/x/y $ btrfs subvolume snapshot -r /mnt/btrfs /mnt/btrfs/snap2 $ btrfs send -p /mnt/btrfs/snap1 /mnt/btrfs/snap2 -f /tmp/incremental.send $ umout /mnt/btrfs $ mkfs.btrfs -f /dev/sdb3 $ mount /dev/sdb3 /mnt/btrfs $ btrfs receive /mnt/btrfs -f /tmp/base.send $ btrfs receive /mnt/btrfs -f /tmp/incremental.send The second btrfs receive command failed with: "ERROR: rename a/b/c/d -> a/b/x/y failed. No such file or directory" A test case for xfstests follows. Signed-off-by: Filipe David Borba Manana <fdmanana@gmail.com> Signed-off-by: Josef Bacik <jbacik@fb.com>
Diffstat (limited to 'fs/btrfs/send.c')
-rw-r--r--fs/btrfs/send.c41
1 files changed, 34 insertions, 7 deletions
diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
index 3ddd2bb75083..cb9502adccdc 100644
--- a/fs/btrfs/send.c
+++ b/fs/btrfs/send.c
@@ -2857,19 +2857,48 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm)
2857{ 2857{
2858 struct fs_path *from_path = NULL; 2858 struct fs_path *from_path = NULL;
2859 struct fs_path *to_path = NULL; 2859 struct fs_path *to_path = NULL;
2860 struct fs_path *name = NULL;
2860 u64 orig_progress = sctx->send_progress; 2861 u64 orig_progress = sctx->send_progress;
2861 struct recorded_ref *cur; 2862 struct recorded_ref *cur;
2863 u64 parent_ino, parent_gen;
2862 int ret; 2864 int ret;
2863 2865
2866 name = fs_path_alloc();
2864 from_path = fs_path_alloc(); 2867 from_path = fs_path_alloc();
2865 if (!from_path) 2868 if (!name || !from_path) {
2866 return -ENOMEM; 2869 ret = -ENOMEM;
2870 goto out;
2871 }
2867 2872
2868 sctx->send_progress = pm->ino; 2873 ret = del_waiting_dir_move(sctx, pm->ino);
2869 ret = get_cur_path(sctx, pm->ino, pm->gen, from_path); 2874 ASSERT(ret == 0);
2875
2876 ret = get_first_ref(sctx->parent_root, pm->ino,
2877 &parent_ino, &parent_gen, name);
2870 if (ret < 0) 2878 if (ret < 0)
2871 goto out; 2879 goto out;
2872 2880
2881 if (parent_ino == sctx->cur_ino) {
2882 /* child only renamed, not moved */
2883 ASSERT(parent_gen == sctx->cur_inode_gen);
2884 ret = get_cur_path(sctx, sctx->cur_ino, sctx->cur_inode_gen,
2885 from_path);
2886 if (ret < 0)
2887 goto out;
2888 ret = fs_path_add_path(from_path, name);
2889 if (ret < 0)
2890 goto out;
2891 } else {
2892 /* child moved and maybe renamed too */
2893 sctx->send_progress = pm->ino;
2894 ret = get_cur_path(sctx, pm->ino, pm->gen, from_path);
2895 if (ret < 0)
2896 goto out;
2897 }
2898
2899 fs_path_free(name);
2900 name = NULL;
2901
2873 to_path = fs_path_alloc(); 2902 to_path = fs_path_alloc();
2874 if (!to_path) { 2903 if (!to_path) {
2875 ret = -ENOMEM; 2904 ret = -ENOMEM;
@@ -2877,9 +2906,6 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm)
2877 } 2906 }
2878 2907
2879 sctx->send_progress = sctx->cur_ino + 1; 2908 sctx->send_progress = sctx->cur_ino + 1;
2880 ret = del_waiting_dir_move(sctx, pm->ino);
2881 ASSERT(ret == 0);
2882
2883 ret = get_cur_path(sctx, pm->ino, pm->gen, to_path); 2909 ret = get_cur_path(sctx, pm->ino, pm->gen, to_path);
2884 if (ret < 0) 2910 if (ret < 0)
2885 goto out; 2911 goto out;
@@ -2903,6 +2929,7 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm)
2903 } 2929 }
2904 2930
2905out: 2931out:
2932 fs_path_free(name);
2906 fs_path_free(from_path); 2933 fs_path_free(from_path);
2907 fs_path_free(to_path); 2934 fs_path_free(to_path);
2908 sctx->send_progress = orig_progress; 2935 sctx->send_progress = orig_progress;