|
@@ -231,7 +231,6 @@ struct pending_dir_move {
|
|
|
u64 parent_ino;
|
|
|
u64 ino;
|
|
|
u64 gen;
|
|
|
- bool is_orphan;
|
|
|
struct list_head update_refs;
|
|
|
};
|
|
|
|
|
@@ -1861,7 +1860,8 @@ static int will_overwrite_ref(struct send_ctx *sctx, u64 dir, u64 dir_gen,
|
|
|
* was already unlinked/moved, so we can safely assume that we will not
|
|
|
* overwrite anything at this point in time.
|
|
|
*/
|
|
|
- if (other_inode > sctx->send_progress) {
|
|
|
+ if (other_inode > sctx->send_progress ||
|
|
|
+ is_waiting_for_move(sctx, other_inode)) {
|
|
|
ret = get_inode_info(sctx->parent_root, other_inode, NULL,
|
|
|
who_gen, NULL, NULL, NULL, NULL);
|
|
|
if (ret < 0)
|
|
@@ -3047,7 +3047,6 @@ static int add_pending_dir_move(struct send_ctx *sctx,
|
|
|
pm->parent_ino = parent_ino;
|
|
|
pm->ino = ino;
|
|
|
pm->gen = ino_gen;
|
|
|
- pm->is_orphan = is_orphan;
|
|
|
INIT_LIST_HEAD(&pm->list);
|
|
|
INIT_LIST_HEAD(&pm->update_refs);
|
|
|
RB_CLEAR_NODE(&pm->node);
|
|
@@ -3113,6 +3112,48 @@ static struct pending_dir_move *get_pending_dir_moves(struct send_ctx *sctx,
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
+static int path_loop(struct send_ctx *sctx, struct fs_path *name,
|
|
|
+ u64 ino, u64 gen, u64 *ancestor_ino)
|
|
|
+{
|
|
|
+ int ret = 0;
|
|
|
+ u64 parent_inode = 0;
|
|
|
+ u64 parent_gen = 0;
|
|
|
+ u64 start_ino = ino;
|
|
|
+
|
|
|
+ *ancestor_ino = 0;
|
|
|
+ while (ino != BTRFS_FIRST_FREE_OBJECTID) {
|
|
|
+ fs_path_reset(name);
|
|
|
+
|
|
|
+ if (is_waiting_for_rm(sctx, ino))
|
|
|
+ break;
|
|
|
+ if (is_waiting_for_move(sctx, ino)) {
|
|
|
+ if (*ancestor_ino == 0)
|
|
|
+ *ancestor_ino = ino;
|
|
|
+ ret = get_first_ref(sctx->parent_root, ino,
|
|
|
+ &parent_inode, &parent_gen, name);
|
|
|
+ } else {
|
|
|
+ ret = __get_cur_name_and_parent(sctx, ino, gen,
|
|
|
+ &parent_inode,
|
|
|
+ &parent_gen, name);
|
|
|
+ if (ret > 0) {
|
|
|
+ ret = 0;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (ret < 0)
|
|
|
+ break;
|
|
|
+ if (parent_inode == start_ino) {
|
|
|
+ ret = 1;
|
|
|
+ if (*ancestor_ino == 0)
|
|
|
+ *ancestor_ino = ino;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ ino = parent_inode;
|
|
|
+ gen = parent_gen;
|
|
|
+ }
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm)
|
|
|
{
|
|
|
struct fs_path *from_path = NULL;
|
|
@@ -3123,6 +3164,8 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm)
|
|
|
u64 parent_ino, parent_gen;
|
|
|
struct waiting_dir_move *dm = NULL;
|
|
|
u64 rmdir_ino = 0;
|
|
|
+ u64 ancestor;
|
|
|
+ bool is_orphan;
|
|
|
int ret;
|
|
|
|
|
|
name = fs_path_alloc();
|
|
@@ -3135,9 +3178,10 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm)
|
|
|
dm = get_waiting_dir_move(sctx, pm->ino);
|
|
|
ASSERT(dm);
|
|
|
rmdir_ino = dm->rmdir_ino;
|
|
|
+ is_orphan = dm->orphanized;
|
|
|
free_waiting_dir_move(sctx, dm);
|
|
|
|
|
|
- if (pm->is_orphan) {
|
|
|
+ if (is_orphan) {
|
|
|
ret = gen_unique_name(sctx, pm->ino,
|
|
|
pm->gen, from_path);
|
|
|
} else {
|
|
@@ -3155,6 +3199,22 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm)
|
|
|
goto out;
|
|
|
|
|
|
sctx->send_progress = sctx->cur_ino + 1;
|
|
|
+ ret = path_loop(sctx, name, pm->ino, pm->gen, &ancestor);
|
|
|
+ if (ret) {
|
|
|
+ LIST_HEAD(deleted_refs);
|
|
|
+ ASSERT(ancestor > BTRFS_FIRST_FREE_OBJECTID);
|
|
|
+ ret = add_pending_dir_move(sctx, pm->ino, pm->gen, ancestor,
|
|
|
+ &pm->update_refs, &deleted_refs,
|
|
|
+ is_orphan);
|
|
|
+ if (ret < 0)
|
|
|
+ goto out;
|
|
|
+ if (rmdir_ino) {
|
|
|
+ dm = get_waiting_dir_move(sctx, pm->ino);
|
|
|
+ ASSERT(dm);
|
|
|
+ dm->rmdir_ino = rmdir_ino;
|
|
|
+ }
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
fs_path_reset(name);
|
|
|
to_path = name;
|
|
|
name = NULL;
|
|
@@ -3325,6 +3385,7 @@ static int wait_for_dest_dir_move(struct send_ctx *sctx,
|
|
|
u64 left_gen;
|
|
|
u64 right_gen;
|
|
|
int ret = 0;
|
|
|
+ struct waiting_dir_move *wdm;
|
|
|
|
|
|
if (RB_EMPTY_ROOT(&sctx->waiting_dir_moves))
|
|
|
return 0;
|
|
@@ -3383,7 +3444,8 @@ static int wait_for_dest_dir_move(struct send_ctx *sctx,
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
|
- if (is_waiting_for_move(sctx, di_key.objectid)) {
|
|
|
+ wdm = get_waiting_dir_move(sctx, di_key.objectid);
|
|
|
+ if (wdm && !wdm->orphanized) {
|
|
|
ret = add_pending_dir_move(sctx,
|
|
|
sctx->cur_ino,
|
|
|
sctx->cur_inode_gen,
|
|
@@ -3643,11 +3705,26 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
|
|
|
goto out;
|
|
|
if (ret) {
|
|
|
struct name_cache_entry *nce;
|
|
|
+ struct waiting_dir_move *wdm;
|
|
|
|
|
|
ret = orphanize_inode(sctx, ow_inode, ow_gen,
|
|
|
cur->full_path);
|
|
|
if (ret < 0)
|
|
|
goto out;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If ow_inode has its rename operation delayed
|
|
|
+ * make sure that its orphanized name is used in
|
|
|
+ * the source path when performing its rename
|
|
|
+ * operation.
|
|
|
+ */
|
|
|
+ if (is_waiting_for_move(sctx, ow_inode)) {
|
|
|
+ wdm = get_waiting_dir_move(sctx,
|
|
|
+ ow_inode);
|
|
|
+ ASSERT(wdm);
|
|
|
+ wdm->orphanized = true;
|
|
|
+ }
|
|
|
+
|
|
|
/*
|
|
|
* Make sure we clear our orphanized inode's
|
|
|
* name from the name cache. This is because the
|
|
@@ -3663,6 +3740,19 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
|
|
|
name_cache_delete(sctx, nce);
|
|
|
kfree(nce);
|
|
|
}
|
|
|
+
|
|
|
+ /*
|
|
|
+ * ow_inode might currently be an ancestor of
|
|
|
+ * cur_ino, therefore compute valid_path (the
|
|
|
+ * current path of cur_ino) again because it
|
|
|
+ * might contain the pre-orphanization name of
|
|
|
+ * ow_inode, which is no longer valid.
|
|
|
+ */
|
|
|
+ fs_path_reset(valid_path);
|
|
|
+ ret = get_cur_path(sctx, sctx->cur_ino,
|
|
|
+ sctx->cur_inode_gen, valid_path);
|
|
|
+ if (ret < 0)
|
|
|
+ goto out;
|
|
|
} else {
|
|
|
ret = send_unlink(sctx, cur->full_path);
|
|
|
if (ret < 0)
|