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:
Chris Mason 2015-06-03 19:44:59 -07:00
commit 1ab818b137
1 changed files with 83 additions and 21 deletions

View File

@ -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 {
@ -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;
@ -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)) {
@ -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 {
@ -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;
@ -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;
@ -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;
@ -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;
@ -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;
} }
@ -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;
} }
@ -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
@ -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); ret = send_rename(sctx, valid_path,
if (ret < 0) cur->full_path);
goto out; if (!ret)
if (ret) { ret = fs_path_copy(valid_path,
*pending_move = 1; cur->full_path);
} else {
ret = send_rename(sctx, valid_path,
cur->full_path);
if (!ret)
ret = fs_path_copy(valid_path,
cur->full_path);
}
if (ret < 0) if (ret < 0)
goto out; goto out;
} else { } else {