Btrfs: incremental send, fix invalid path after dir rename
[platform/kernel/linux-rpi.git] / fs / btrfs / send.c
index 3ddd2bb..cb9502a 100644 (file)
@@ -2857,19 +2857,48 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm)
 {
        struct fs_path *from_path = NULL;
        struct fs_path *to_path = NULL;
+       struct fs_path *name = NULL;
        u64 orig_progress = sctx->send_progress;
        struct recorded_ref *cur;
+       u64 parent_ino, parent_gen;
        int ret;
 
+       name = fs_path_alloc();
        from_path = fs_path_alloc();
-       if (!from_path)
-               return -ENOMEM;
+       if (!name || !from_path) {
+               ret = -ENOMEM;
+               goto out;
+       }
 
-       sctx->send_progress = pm->ino;
-       ret = get_cur_path(sctx, pm->ino, pm->gen, from_path);
+       ret = del_waiting_dir_move(sctx, pm->ino);
+       ASSERT(ret == 0);
+
+       ret = get_first_ref(sctx->parent_root, pm->ino,
+                           &parent_ino, &parent_gen, name);
        if (ret < 0)
                goto out;
 
+       if (parent_ino == sctx->cur_ino) {
+               /* child only renamed, not moved */
+               ASSERT(parent_gen == sctx->cur_inode_gen);
+               ret = get_cur_path(sctx, sctx->cur_ino, sctx->cur_inode_gen,
+                                  from_path);
+               if (ret < 0)
+                       goto out;
+               ret = fs_path_add_path(from_path, name);
+               if (ret < 0)
+                       goto out;
+       } else {
+               /* child moved and maybe renamed too */
+               sctx->send_progress = pm->ino;
+               ret = get_cur_path(sctx, pm->ino, pm->gen, from_path);
+               if (ret < 0)
+                       goto out;
+       }
+
+       fs_path_free(name);
+       name = NULL;
+
        to_path = fs_path_alloc();
        if (!to_path) {
                ret = -ENOMEM;
@@ -2877,9 +2906,6 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm)
        }
 
        sctx->send_progress = sctx->cur_ino + 1;
-       ret = del_waiting_dir_move(sctx, pm->ino);
-       ASSERT(ret == 0);
-
        ret = get_cur_path(sctx, pm->ino, pm->gen, to_path);
        if (ret < 0)
                goto out;
@@ -2903,6 +2929,7 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm)
        }
 
 out:
+       fs_path_free(name);
        fs_path_free(from_path);
        fs_path_free(to_path);
        sctx->send_progress = orig_progress;