Loading fs/btrfs/send.c +83 −21 Original line number Original line Diff line number Diff line Loading @@ -243,6 +243,7 @@ struct waiting_dir_move { * after this directory is moved, we can try to rmdir the ino rmdir_ino. * after this directory is moved, we can try to rmdir the ino rmdir_ino. */ */ u64 rmdir_ino; u64 rmdir_ino; bool orphanized; }; }; struct orphan_dir_info { struct orphan_dir_info { Loading Loading @@ -1916,8 +1917,13 @@ static int did_overwrite_ref(struct send_ctx *sctx, goto out; goto out; } } /* we know that it is or will be overwritten. check this now */ /* if (ow_inode < sctx->send_progress) * We know that it is or will be overwritten. Check this now. * The current inode being processed might have been the one that caused * inode 'ino' to be orphanized, therefore ow_inode can actually be the * same as sctx->send_progress. */ if (ow_inode <= sctx->send_progress) ret = 1; ret = 1; else else ret = 0; ret = 0; Loading Loading @@ -2239,6 +2245,8 @@ static int get_cur_path(struct send_ctx *sctx, u64 ino, u64 gen, fs_path_reset(dest); fs_path_reset(dest); while (!stop && ino != BTRFS_FIRST_FREE_OBJECTID) { while (!stop && ino != BTRFS_FIRST_FREE_OBJECTID) { struct waiting_dir_move *wdm; fs_path_reset(name); fs_path_reset(name); if (is_waiting_for_rm(sctx, ino)) { if (is_waiting_for_rm(sctx, ino)) { Loading @@ -2249,7 +2257,11 @@ static int get_cur_path(struct send_ctx *sctx, u64 ino, u64 gen, break; break; } } if (is_waiting_for_move(sctx, ino)) { wdm = get_waiting_dir_move(sctx, ino); if (wdm && wdm->orphanized) { ret = gen_unique_name(sctx, ino, gen, name); stop = 1; } else if (wdm) { ret = get_first_ref(sctx->parent_root, ino, ret = get_first_ref(sctx->parent_root, ino, &parent_inode, &parent_gen, name); &parent_inode, &parent_gen, name); } else { } else { Loading Loading @@ -2939,7 +2951,7 @@ static int is_waiting_for_move(struct send_ctx *sctx, u64 ino) return entry != NULL; return entry != NULL; } } static int add_waiting_dir_move(struct send_ctx *sctx, u64 ino) static int add_waiting_dir_move(struct send_ctx *sctx, u64 ino, bool orphanized) { { struct rb_node **p = &sctx->waiting_dir_moves.rb_node; struct rb_node **p = &sctx->waiting_dir_moves.rb_node; struct rb_node *parent = NULL; struct rb_node *parent = NULL; Loading @@ -2950,6 +2962,7 @@ static int add_waiting_dir_move(struct send_ctx *sctx, u64 ino) return -ENOMEM; return -ENOMEM; dm->ino = ino; dm->ino = ino; dm->rmdir_ino = 0; dm->rmdir_ino = 0; dm->orphanized = orphanized; while (*p) { while (*p) { parent = *p; parent = *p; Loading Loading @@ -3046,7 +3059,7 @@ static int add_pending_dir_move(struct send_ctx *sctx, goto out; goto out; } } ret = add_waiting_dir_move(sctx, pm->ino); ret = add_waiting_dir_move(sctx, pm->ino, is_orphan); if (ret) if (ret) goto out; goto out; Loading Loading @@ -3369,8 +3382,40 @@ static int wait_for_dest_dir_move(struct send_ctx *sctx, return ret; return ret; } } /* * Check if ino ino1 is an ancestor of inode ino2 in the given root. * Return 1 if true, 0 if false and < 0 on error. */ static int is_ancestor(struct btrfs_root *root, const u64 ino1, const u64 ino1_gen, const u64 ino2, struct fs_path *fs_path) { u64 ino = ino2; while (ino > BTRFS_FIRST_FREE_OBJECTID) { int ret; u64 parent; u64 parent_gen; fs_path_reset(fs_path); ret = get_first_ref(root, ino, &parent, &parent_gen, fs_path); if (ret < 0) { if (ret == -ENOENT && ino == ino2) ret = 0; return ret; } if (parent == ino1) return parent_gen == ino1_gen ? 1 : 0; ino = parent; } return 0; } static int wait_for_parent_move(struct send_ctx *sctx, static int wait_for_parent_move(struct send_ctx *sctx, struct recorded_ref *parent_ref) struct recorded_ref *parent_ref, const bool is_orphan) { { int ret = 0; int ret = 0; u64 ino = parent_ref->dir; u64 ino = parent_ref->dir; Loading @@ -3390,11 +3435,24 @@ static int wait_for_parent_move(struct send_ctx *sctx, * Our current directory inode may not yet be renamed/moved because some * Our current directory inode may not yet be renamed/moved because some * ancestor (immediate or not) has to be renamed/moved first. So find if * ancestor (immediate or not) has to be renamed/moved first. So find if * such ancestor exists and make sure our own rename/move happens after * such ancestor exists and make sure our own rename/move happens after * that ancestor is processed. * that ancestor is processed to avoid path build infinite loops (done * at get_cur_path()). */ */ while (ino > BTRFS_FIRST_FREE_OBJECTID) { while (ino > BTRFS_FIRST_FREE_OBJECTID) { if (is_waiting_for_move(sctx, ino)) { if (is_waiting_for_move(sctx, ino)) { ret = 1; /* * If the current inode is an ancestor of ino in the * parent root, we need to delay the rename of the * current inode, otherwise don't delayed the rename * because we can end up with a circular dependency * of renames, resulting in some directories never * getting the respective rename operations issued in * the send stream or getting into infinite path build * loops. */ ret = is_ancestor(sctx->parent_root, sctx->cur_ino, sctx->cur_inode_gen, ino, path_before); break; break; } } Loading Loading @@ -3436,7 +3494,7 @@ static int wait_for_parent_move(struct send_ctx *sctx, ino, ino, &sctx->new_refs, &sctx->new_refs, &sctx->deleted_refs, &sctx->deleted_refs, false); is_orphan); if (!ret) if (!ret) ret = 1; ret = 1; } } Loading Loading @@ -3605,6 +3663,17 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino); } } } } if (S_ISDIR(sctx->cur_inode_mode) && sctx->parent_root && can_rename) { ret = wait_for_parent_move(sctx, cur, is_orphan); if (ret < 0) goto out; if (ret == 1) { can_rename = false; *pending_move = 1; } } /* /* * link/move the ref to the new place. If we have an orphan * link/move the ref to the new place. If we have an orphan * inode, move it and update valid_path. If not, link or move * inode, move it and update valid_path. If not, link or move Loading @@ -3625,18 +3694,11 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino); * dirs, we always have one new and one deleted * dirs, we always have one new and one deleted * ref. The deleted ref is ignored later. * ref. The deleted ref is ignored later. */ */ ret = wait_for_parent_move(sctx, cur); if (ret < 0) goto out; if (ret) { *pending_move = 1; } else { ret = send_rename(sctx, valid_path, ret = send_rename(sctx, valid_path, cur->full_path); cur->full_path); if (!ret) if (!ret) ret = fs_path_copy(valid_path, ret = fs_path_copy(valid_path, cur->full_path); cur->full_path); } if (ret < 0) if (ret < 0) goto out; goto out; } else { } else { Loading Loading
fs/btrfs/send.c +83 −21 Original line number Original line Diff line number Diff line Loading @@ -243,6 +243,7 @@ struct waiting_dir_move { * after this directory is moved, we can try to rmdir the ino rmdir_ino. * after this directory is moved, we can try to rmdir the ino rmdir_ino. */ */ u64 rmdir_ino; u64 rmdir_ino; bool orphanized; }; }; struct orphan_dir_info { struct orphan_dir_info { Loading Loading @@ -1916,8 +1917,13 @@ static int did_overwrite_ref(struct send_ctx *sctx, goto out; goto out; } } /* we know that it is or will be overwritten. check this now */ /* if (ow_inode < sctx->send_progress) * We know that it is or will be overwritten. Check this now. * The current inode being processed might have been the one that caused * inode 'ino' to be orphanized, therefore ow_inode can actually be the * same as sctx->send_progress. */ if (ow_inode <= sctx->send_progress) ret = 1; ret = 1; else else ret = 0; ret = 0; Loading Loading @@ -2239,6 +2245,8 @@ static int get_cur_path(struct send_ctx *sctx, u64 ino, u64 gen, fs_path_reset(dest); fs_path_reset(dest); while (!stop && ino != BTRFS_FIRST_FREE_OBJECTID) { while (!stop && ino != BTRFS_FIRST_FREE_OBJECTID) { struct waiting_dir_move *wdm; fs_path_reset(name); fs_path_reset(name); if (is_waiting_for_rm(sctx, ino)) { if (is_waiting_for_rm(sctx, ino)) { Loading @@ -2249,7 +2257,11 @@ static int get_cur_path(struct send_ctx *sctx, u64 ino, u64 gen, break; break; } } if (is_waiting_for_move(sctx, ino)) { wdm = get_waiting_dir_move(sctx, ino); if (wdm && wdm->orphanized) { ret = gen_unique_name(sctx, ino, gen, name); stop = 1; } else if (wdm) { ret = get_first_ref(sctx->parent_root, ino, ret = get_first_ref(sctx->parent_root, ino, &parent_inode, &parent_gen, name); &parent_inode, &parent_gen, name); } else { } else { Loading Loading @@ -2939,7 +2951,7 @@ static int is_waiting_for_move(struct send_ctx *sctx, u64 ino) return entry != NULL; return entry != NULL; } } static int add_waiting_dir_move(struct send_ctx *sctx, u64 ino) static int add_waiting_dir_move(struct send_ctx *sctx, u64 ino, bool orphanized) { { struct rb_node **p = &sctx->waiting_dir_moves.rb_node; struct rb_node **p = &sctx->waiting_dir_moves.rb_node; struct rb_node *parent = NULL; struct rb_node *parent = NULL; Loading @@ -2950,6 +2962,7 @@ static int add_waiting_dir_move(struct send_ctx *sctx, u64 ino) return -ENOMEM; return -ENOMEM; dm->ino = ino; dm->ino = ino; dm->rmdir_ino = 0; dm->rmdir_ino = 0; dm->orphanized = orphanized; while (*p) { while (*p) { parent = *p; parent = *p; Loading Loading @@ -3046,7 +3059,7 @@ static int add_pending_dir_move(struct send_ctx *sctx, goto out; goto out; } } ret = add_waiting_dir_move(sctx, pm->ino); ret = add_waiting_dir_move(sctx, pm->ino, is_orphan); if (ret) if (ret) goto out; goto out; Loading Loading @@ -3369,8 +3382,40 @@ static int wait_for_dest_dir_move(struct send_ctx *sctx, return ret; return ret; } } /* * Check if ino ino1 is an ancestor of inode ino2 in the given root. * Return 1 if true, 0 if false and < 0 on error. */ static int is_ancestor(struct btrfs_root *root, const u64 ino1, const u64 ino1_gen, const u64 ino2, struct fs_path *fs_path) { u64 ino = ino2; while (ino > BTRFS_FIRST_FREE_OBJECTID) { int ret; u64 parent; u64 parent_gen; fs_path_reset(fs_path); ret = get_first_ref(root, ino, &parent, &parent_gen, fs_path); if (ret < 0) { if (ret == -ENOENT && ino == ino2) ret = 0; return ret; } if (parent == ino1) return parent_gen == ino1_gen ? 1 : 0; ino = parent; } return 0; } static int wait_for_parent_move(struct send_ctx *sctx, static int wait_for_parent_move(struct send_ctx *sctx, struct recorded_ref *parent_ref) struct recorded_ref *parent_ref, const bool is_orphan) { { int ret = 0; int ret = 0; u64 ino = parent_ref->dir; u64 ino = parent_ref->dir; Loading @@ -3390,11 +3435,24 @@ static int wait_for_parent_move(struct send_ctx *sctx, * Our current directory inode may not yet be renamed/moved because some * Our current directory inode may not yet be renamed/moved because some * ancestor (immediate or not) has to be renamed/moved first. So find if * ancestor (immediate or not) has to be renamed/moved first. So find if * such ancestor exists and make sure our own rename/move happens after * such ancestor exists and make sure our own rename/move happens after * that ancestor is processed. * that ancestor is processed to avoid path build infinite loops (done * at get_cur_path()). */ */ while (ino > BTRFS_FIRST_FREE_OBJECTID) { while (ino > BTRFS_FIRST_FREE_OBJECTID) { if (is_waiting_for_move(sctx, ino)) { if (is_waiting_for_move(sctx, ino)) { ret = 1; /* * If the current inode is an ancestor of ino in the * parent root, we need to delay the rename of the * current inode, otherwise don't delayed the rename * because we can end up with a circular dependency * of renames, resulting in some directories never * getting the respective rename operations issued in * the send stream or getting into infinite path build * loops. */ ret = is_ancestor(sctx->parent_root, sctx->cur_ino, sctx->cur_inode_gen, ino, path_before); break; break; } } Loading Loading @@ -3436,7 +3494,7 @@ static int wait_for_parent_move(struct send_ctx *sctx, ino, ino, &sctx->new_refs, &sctx->new_refs, &sctx->deleted_refs, &sctx->deleted_refs, false); is_orphan); if (!ret) if (!ret) ret = 1; ret = 1; } } Loading Loading @@ -3605,6 +3663,17 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino); } } } } if (S_ISDIR(sctx->cur_inode_mode) && sctx->parent_root && can_rename) { ret = wait_for_parent_move(sctx, cur, is_orphan); if (ret < 0) goto out; if (ret == 1) { can_rename = false; *pending_move = 1; } } /* /* * link/move the ref to the new place. If we have an orphan * link/move the ref to the new place. If we have an orphan * inode, move it and update valid_path. If not, link or move * inode, move it and update valid_path. If not, link or move Loading @@ -3625,18 +3694,11 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino); * dirs, we always have one new and one deleted * dirs, we always have one new and one deleted * ref. The deleted ref is ignored later. * ref. The deleted ref is ignored later. */ */ ret = wait_for_parent_move(sctx, cur); if (ret < 0) goto out; if (ret) { *pending_move = 1; } else { ret = send_rename(sctx, valid_path, ret = send_rename(sctx, valid_path, cur->full_path); cur->full_path); if (!ret) if (!ret) ret = fs_path_copy(valid_path, ret = fs_path_copy(valid_path, cur->full_path); cur->full_path); } if (ret < 0) if (ret < 0) goto out; goto out; } else { } else { Loading