Btrfs: incremental send, don't rename a directory too soon
There's one more case where we can't issue a rename operation for a directory as soon as we process it. We used to delay directory renames only if they have some ancestor directory with a higher inode number that got renamed too, but there's another case where we need to delay the rename too - when a directory A is renamed to the old name of a directory B but that directory B has its rename delayed because it has now (in the send root) an ancestor with a higher inode number that was renamed. If we don't delay the directory rename in this case, the receiving end of the send stream will attempt to rename A to the old name of B before B got renamed to its new name, which results in a "directory not empty" error. So fix this by delaying directory renames for this case too. Steps to reproduce: $ mkfs.btrfs -f /dev/sdb $ mount /dev/sdb /mnt $ mkdir /mnt/a $ mkdir /mnt/b $ mkdir /mnt/c $ touch /mnt/a/file $ btrfs subvolume snapshot -r /mnt /mnt/snap1 $ mv /mnt/c /mnt/x $ mv /mnt/a /mnt/x/y $ mv /mnt/b /mnt/a $ btrfs subvolume snapshot -r /mnt /mnt/snap2 $ btrfs send /mnt/snap1 -f /tmp/1.send $ btrfs send -p /mnt/snap1 /mnt/snap2 -f /tmp/2.send $ mkfs.btrfs -f /dev/sdc $ mount /dev/sdc /mnt2 $ btrfs receive /mnt2 -f /tmp/1.send $ btrfs receive /mnt2 -f /tmp/2.send ERROR: rename b -> a failed. Directory not empty A test case for xfstests follows soon. Reported-by: Ames Cornish <ames@cornishes.net> Signed-off-by: Filipe Manana <fdmanana@suse.com> Signed-off-by: Chris Mason <clm@fb.com>
This commit is contained in:
committed by
Chris Mason
parent
1932b7be97
commit
84471e2429
171
fs/btrfs/send.c
171
fs/btrfs/send.c
@@ -230,6 +230,7 @@ struct pending_dir_move {
|
|||||||
u64 parent_ino;
|
u64 parent_ino;
|
||||||
u64 ino;
|
u64 ino;
|
||||||
u64 gen;
|
u64 gen;
|
||||||
|
bool is_orphan;
|
||||||
struct list_head update_refs;
|
struct list_head update_refs;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -2984,7 +2985,8 @@ static int add_pending_dir_move(struct send_ctx *sctx,
|
|||||||
u64 ino_gen,
|
u64 ino_gen,
|
||||||
u64 parent_ino,
|
u64 parent_ino,
|
||||||
struct list_head *new_refs,
|
struct list_head *new_refs,
|
||||||
struct list_head *deleted_refs)
|
struct list_head *deleted_refs,
|
||||||
|
const bool is_orphan)
|
||||||
{
|
{
|
||||||
struct rb_node **p = &sctx->pending_dir_moves.rb_node;
|
struct rb_node **p = &sctx->pending_dir_moves.rb_node;
|
||||||
struct rb_node *parent = NULL;
|
struct rb_node *parent = NULL;
|
||||||
@@ -2999,6 +3001,7 @@ static int add_pending_dir_move(struct send_ctx *sctx,
|
|||||||
pm->parent_ino = parent_ino;
|
pm->parent_ino = parent_ino;
|
||||||
pm->ino = ino;
|
pm->ino = ino;
|
||||||
pm->gen = ino_gen;
|
pm->gen = ino_gen;
|
||||||
|
pm->is_orphan = is_orphan;
|
||||||
INIT_LIST_HEAD(&pm->list);
|
INIT_LIST_HEAD(&pm->list);
|
||||||
INIT_LIST_HEAD(&pm->update_refs);
|
INIT_LIST_HEAD(&pm->update_refs);
|
||||||
RB_CLEAR_NODE(&pm->node);
|
RB_CLEAR_NODE(&pm->node);
|
||||||
@@ -3131,16 +3134,20 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm)
|
|||||||
rmdir_ino = dm->rmdir_ino;
|
rmdir_ino = dm->rmdir_ino;
|
||||||
free_waiting_dir_move(sctx, dm);
|
free_waiting_dir_move(sctx, dm);
|
||||||
|
|
||||||
ret = get_first_ref(sctx->parent_root, pm->ino,
|
if (pm->is_orphan) {
|
||||||
&parent_ino, &parent_gen, name);
|
ret = gen_unique_name(sctx, pm->ino,
|
||||||
if (ret < 0)
|
pm->gen, from_path);
|
||||||
goto out;
|
} else {
|
||||||
|
ret = get_first_ref(sctx->parent_root, pm->ino,
|
||||||
ret = get_cur_path(sctx, parent_ino, parent_gen,
|
&parent_ino, &parent_gen, name);
|
||||||
from_path);
|
if (ret < 0)
|
||||||
if (ret < 0)
|
goto out;
|
||||||
goto out;
|
ret = get_cur_path(sctx, parent_ino, parent_gen,
|
||||||
ret = fs_path_add_path(from_path, name);
|
from_path);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
ret = fs_path_add_path(from_path, name);
|
||||||
|
}
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
@@ -3150,7 +3157,8 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm)
|
|||||||
LIST_HEAD(deleted_refs);
|
LIST_HEAD(deleted_refs);
|
||||||
ASSERT(ancestor > BTRFS_FIRST_FREE_OBJECTID);
|
ASSERT(ancestor > BTRFS_FIRST_FREE_OBJECTID);
|
||||||
ret = add_pending_dir_move(sctx, pm->ino, pm->gen, ancestor,
|
ret = add_pending_dir_move(sctx, pm->ino, pm->gen, ancestor,
|
||||||
&pm->update_refs, &deleted_refs);
|
&pm->update_refs, &deleted_refs,
|
||||||
|
pm->is_orphan);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto out;
|
goto out;
|
||||||
if (rmdir_ino) {
|
if (rmdir_ino) {
|
||||||
@@ -3283,6 +3291,127 @@ out:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We might need to delay a directory rename even when no ancestor directory
|
||||||
|
* (in the send root) with a higher inode number than ours (sctx->cur_ino) was
|
||||||
|
* renamed. This happens when we rename a directory to the old name (the name
|
||||||
|
* in the parent root) of some other unrelated directory that got its rename
|
||||||
|
* delayed due to some ancestor with higher number that got renamed.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
*
|
||||||
|
* Parent snapshot:
|
||||||
|
* . (ino 256)
|
||||||
|
* |---- a/ (ino 257)
|
||||||
|
* | |---- file (ino 260)
|
||||||
|
* |
|
||||||
|
* |---- b/ (ino 258)
|
||||||
|
* |---- c/ (ino 259)
|
||||||
|
*
|
||||||
|
* Send snapshot:
|
||||||
|
* . (ino 256)
|
||||||
|
* |---- a/ (ino 258)
|
||||||
|
* |---- x/ (ino 259)
|
||||||
|
* |---- y/ (ino 257)
|
||||||
|
* |----- file (ino 260)
|
||||||
|
*
|
||||||
|
* Here we can not rename 258 from 'b' to 'a' without the rename of inode 257
|
||||||
|
* from 'a' to 'x/y' happening first, which in turn depends on the rename of
|
||||||
|
* inode 259 from 'c' to 'x'. So the order of rename commands the send stream
|
||||||
|
* must issue is:
|
||||||
|
*
|
||||||
|
* 1 - rename 259 from 'c' to 'x'
|
||||||
|
* 2 - rename 257 from 'a' to 'x/y'
|
||||||
|
* 3 - rename 258 from 'b' to 'a'
|
||||||
|
*
|
||||||
|
* Returns 1 if the rename of sctx->cur_ino needs to be delayed, 0 if it can
|
||||||
|
* be done right away and < 0 on error.
|
||||||
|
*/
|
||||||
|
static int wait_for_dest_dir_move(struct send_ctx *sctx,
|
||||||
|
struct recorded_ref *parent_ref,
|
||||||
|
const bool is_orphan)
|
||||||
|
{
|
||||||
|
struct btrfs_path *path;
|
||||||
|
struct btrfs_key key;
|
||||||
|
struct btrfs_key di_key;
|
||||||
|
struct btrfs_dir_item *di;
|
||||||
|
u64 left_gen;
|
||||||
|
u64 right_gen;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (RB_EMPTY_ROOT(&sctx->waiting_dir_moves))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
path = alloc_path_for_send();
|
||||||
|
if (!path)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
key.objectid = parent_ref->dir;
|
||||||
|
key.type = BTRFS_DIR_ITEM_KEY;
|
||||||
|
key.offset = btrfs_name_hash(parent_ref->name, parent_ref->name_len);
|
||||||
|
|
||||||
|
ret = btrfs_search_slot(NULL, sctx->parent_root, &key, path, 0, 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto out;
|
||||||
|
} else if (ret > 0) {
|
||||||
|
ret = 0;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
di = btrfs_match_dir_item_name(sctx->parent_root, path,
|
||||||
|
parent_ref->name, parent_ref->name_len);
|
||||||
|
if (!di) {
|
||||||
|
ret = 0;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* di_key.objectid has the number of the inode that has a dentry in the
|
||||||
|
* parent directory with the same name that sctx->cur_ino is being
|
||||||
|
* renamed to. We need to check if that inode is in the send root as
|
||||||
|
* well and if it is currently marked as an inode with a pending rename,
|
||||||
|
* if it is, we need to delay the rename of sctx->cur_ino as well, so
|
||||||
|
* that it happens after that other inode is renamed.
|
||||||
|
*/
|
||||||
|
btrfs_dir_item_key_to_cpu(path->nodes[0], di, &di_key);
|
||||||
|
if (di_key.type != BTRFS_INODE_ITEM_KEY) {
|
||||||
|
ret = 0;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = get_inode_info(sctx->parent_root, di_key.objectid, NULL,
|
||||||
|
&left_gen, NULL, NULL, NULL, NULL);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
ret = get_inode_info(sctx->send_root, di_key.objectid, NULL,
|
||||||
|
&right_gen, NULL, NULL, NULL, NULL);
|
||||||
|
if (ret < 0) {
|
||||||
|
if (ret == -ENOENT)
|
||||||
|
ret = 0;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Different inode, no need to delay the rename of sctx->cur_ino */
|
||||||
|
if (right_gen != left_gen) {
|
||||||
|
ret = 0;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_waiting_for_move(sctx, di_key.objectid)) {
|
||||||
|
ret = add_pending_dir_move(sctx,
|
||||||
|
sctx->cur_ino,
|
||||||
|
sctx->cur_inode_gen,
|
||||||
|
di_key.objectid,
|
||||||
|
&sctx->new_refs,
|
||||||
|
&sctx->deleted_refs,
|
||||||
|
is_orphan);
|
||||||
|
if (!ret)
|
||||||
|
ret = 1;
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
btrfs_free_path(path);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
@@ -3349,7 +3478,8 @@ out:
|
|||||||
sctx->cur_inode_gen,
|
sctx->cur_inode_gen,
|
||||||
ino,
|
ino,
|
||||||
&sctx->new_refs,
|
&sctx->new_refs,
|
||||||
&sctx->deleted_refs);
|
&sctx->deleted_refs,
|
||||||
|
false);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
ret = 1;
|
ret = 1;
|
||||||
}
|
}
|
||||||
@@ -3372,6 +3502,7 @@ static int process_recorded_refs(struct send_ctx *sctx, int *pending_move)
|
|||||||
int did_overwrite = 0;
|
int did_overwrite = 0;
|
||||||
int is_orphan = 0;
|
int is_orphan = 0;
|
||||||
u64 last_dir_ino_rm = 0;
|
u64 last_dir_ino_rm = 0;
|
||||||
|
bool can_rename = true;
|
||||||
|
|
||||||
verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
|
verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
|
||||||
|
|
||||||
@@ -3490,12 +3621,22 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (S_ISDIR(sctx->cur_inode_mode) && sctx->parent_root) {
|
||||||
|
ret = wait_for_dest_dir_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
|
||||||
* it depending on the inode mode.
|
* it depending on the inode mode.
|
||||||
*/
|
*/
|
||||||
if (is_orphan) {
|
if (is_orphan && can_rename) {
|
||||||
ret = send_rename(sctx, valid_path, cur->full_path);
|
ret = send_rename(sctx, valid_path, cur->full_path);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto out;
|
goto out;
|
||||||
@@ -3503,7 +3644,7 @@ verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
|
|||||||
ret = fs_path_copy(valid_path, cur->full_path);
|
ret = fs_path_copy(valid_path, cur->full_path);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto out;
|
goto out;
|
||||||
} else {
|
} else if (can_rename) {
|
||||||
if (S_ISDIR(sctx->cur_inode_mode)) {
|
if (S_ISDIR(sctx->cur_inode_mode)) {
|
||||||
/*
|
/*
|
||||||
* Dirs can't be linked, so move it. For moved
|
* Dirs can't be linked, so move it. For moved
|
||||||
|
|||||||
Reference in New Issue
Block a user