@@ -2778,6 +2778,20 @@ struct recorded_ref {
int name_len;
};
+static void set_ref_path(struct recorded_ref *ref,
+ struct fs_path *path)
+{
+ ref->full_path = path;
+ ref->name = (char *)kbasename(ref->full_path->start);
+ ref->name_len = ref->full_path->end - ref->name;
+ ref->dir_path = ref->full_path->start;
+ if (ref->name == ref->full_path->start)
+ ref->dir_path_len = 0;
+ else
+ ref->dir_path_len = ref->full_path->end -
+ ref->full_path->start - 1 - ref->name_len;
+}
+
/*
* We need to process new refs before deleted refs, but compare_tree gives us
* everything mixed. So we first record all refs and later process them.
@@ -2794,17 +2808,7 @@ static int __record_ref(struct list_head *head, u64 dir,
ref->dir = dir;
ref->dir_gen = dir_gen;
- ref->full_path = path;
-
- ref->name = (char *)kbasename(ref->full_path->start);
- ref->name_len = ref->full_path->end - ref->name;
- ref->dir_path = ref->full_path->start;
- if (ref->name == ref->full_path->start)
- ref->dir_path_len = 0;
- else
- ref->dir_path_len = ref->full_path->end -
- ref->full_path->start - 1 - ref->name_len;
-
+ set_ref_path(ref, path);
list_add_tail(&ref->list, head);
return 0;
}
@@ -3699,6 +3703,7 @@ static int process_recorded_refs(struct send_ctx *sctx, int *pending_move)
int is_orphan = 0;
u64 last_dir_ino_rm = 0;
bool can_rename = true;
+ bool orphanized_ancestor = false;
btrfs_debug(fs_info, "process_recorded_refs %llu", sctx->cur_ino);
@@ -3854,6 +3859,7 @@ static int process_recorded_refs(struct send_ctx *sctx, int *pending_move)
ow_inode, ow_gen,
sctx->cur_ino, NULL);
if (ret > 0) {
+ orphanized_ancestor = true;
fs_path_reset(valid_path);
ret = get_cur_path(sctx, sctx->cur_ino,
sctx->cur_inode_gen,
@@ -3979,6 +3985,43 @@ static int process_recorded_refs(struct send_ctx *sctx, int *pending_move)
if (ret < 0)
goto out;
if (!ret) {
+ /*
+ * If we orphanized any ancestor before, we need
+ * to recompute the full path for deleted names,
+ * since any such path was computed before we
+ * processed any references and orphanized any
+ * ancestor inode.
+ */
+ if (orphanized_ancestor) {
+ struct fs_path *new_path;
+
+ /*
+ * Our reference's name member points to
+ * its full_path member string, so we
+ * use here a new path.
+ */
+ new_path = fs_path_alloc();
+ if (!new_path) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ ret = get_cur_path(sctx, cur->dir,
+ cur->dir_gen,
+ new_path);
+ if (ret < 0) {
+ fs_path_free(new_path);
+ goto out;
+ }
+ ret = fs_path_add(new_path,
+ cur->name,
+ cur->name_len);
+ if (ret < 0) {
+ fs_path_free(new_path);
+ goto out;
+ }
+ fs_path_free(cur->full_path);
+ set_ref_path(cur, new_path);
+ }
ret = send_unlink(sctx, cur->full_path);
if (ret < 0)
goto out;