Merge branch 'send_fixes_4.2' of git://git.kernel.org/pub/scm/linux/kernel/git/fdmanana/linux into for-linus-4.2
This commit is contained in:
commit
1ab818b137
104
fs/btrfs/send.c
104
fs/btrfs/send.c
@ -243,6 +243,7 @@ struct waiting_dir_move {
|
||||
* after this directory is moved, we can try to rmdir the ino rmdir_ino.
|
||||
*/
|
||||
u64 rmdir_ino;
|
||||
bool orphanized;
|
||||
};
|
||||
|
||||
struct orphan_dir_info {
|
||||
@ -1916,8 +1917,13 @@ static int did_overwrite_ref(struct send_ctx *sctx,
|
||||
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;
|
||||
else
|
||||
ret = 0;
|
||||
@ -2239,6 +2245,8 @@ static int get_cur_path(struct send_ctx *sctx, u64 ino, u64 gen,
|
||||
fs_path_reset(dest);
|
||||
|
||||
while (!stop && ino != BTRFS_FIRST_FREE_OBJECTID) {
|
||||
struct waiting_dir_move *wdm;
|
||||
|
||||
fs_path_reset(name);
|
||||
|
||||
if (is_waiting_for_rm(sctx, ino)) {
|
||||
@ -2249,7 +2257,11 @@ static int get_cur_path(struct send_ctx *sctx, u64 ino, u64 gen,
|
||||
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,
|
||||
&parent_inode, &parent_gen, name);
|
||||
} else {
|
||||
@ -2939,7 +2951,7 @@ static int is_waiting_for_move(struct send_ctx *sctx, u64 ino)
|
||||
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 *parent = NULL;
|
||||
@ -2950,6 +2962,7 @@ static int add_waiting_dir_move(struct send_ctx *sctx, u64 ino)
|
||||
return -ENOMEM;
|
||||
dm->ino = ino;
|
||||
dm->rmdir_ino = 0;
|
||||
dm->orphanized = orphanized;
|
||||
|
||||
while (*p) {
|
||||
parent = *p;
|
||||
@ -3046,7 +3059,7 @@ static int add_pending_dir_move(struct send_ctx *sctx,
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = add_waiting_dir_move(sctx, pm->ino);
|
||||
ret = add_waiting_dir_move(sctx, pm->ino, is_orphan);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
@ -3369,8 +3382,40 @@ out:
|
||||
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,
|
||||
struct recorded_ref *parent_ref)
|
||||
struct recorded_ref *parent_ref,
|
||||
const bool is_orphan)
|
||||
{
|
||||
int ret = 0;
|
||||
u64 ino = parent_ref->dir;
|
||||
@ -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
|
||||
* 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
|
||||
* 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) {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -3436,7 +3494,7 @@ out:
|
||||
ino,
|
||||
&sctx->new_refs,
|
||||
&sctx->deleted_refs,
|
||||
false);
|
||||
is_orphan);
|
||||
if (!ret)
|
||||
ret = 1;
|
||||
}
|
||||
@ -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
|
||||
* inode, move it and update valid_path. If not, link or move
|
||||
@ -3625,18 +3694,11 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
|
||||
* dirs, we always have one new and one deleted
|
||||
* 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,
|
||||
cur->full_path);
|
||||
if (!ret)
|
||||
ret = fs_path_copy(valid_path,
|
||||
cur->full_path);
|
||||
}
|
||||
ret = send_rename(sctx, valid_path,
|
||||
cur->full_path);
|
||||
if (!ret)
|
||||
ret = fs_path_copy(valid_path,
|
||||
cur->full_path);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
} else {
|
||||
|
Loading…
Reference in New Issue
Block a user