Skip to content

Commit f596278

Browse files
committed
Btrfs: incremental send, fix invalid path for link commands
In some scenarios an incremental send stream can contain link commands with an invalid target path. Such scenarios happen after moving some directory inode A, renaming a regular file inode B into the old name of inode A and finally creating a new hard link for inode B at directory inode A. Consider the following example scenario where this issue happens. Parent snapshot: . (ino 256) | |--- dir1/ (ino 257) | |--- dir2/ (ino 258) | |--- dir3/ (ino 259) | |--- file1 (ino 261) | |--- dir4/ (ino 262) | |--- dir5/ (ino 260) Send snapshot: . (ino 256) | |--- dir1/ (ino 257) |--- dir2/ (ino 258) | |--- dir3/ (ino 259) | |--- dir4 (ino 261) | |--- dir6/ (ino 263) |--- dir44/ (ino 262) |--- file11 (ino 261) |--- dir55/ (ino 260) When attempting to apply the corresponding incremental send stream, a link command contains an invalid target path which makes the receiver fail. The following is the verbose output of the btrfs receive command: receiving snapshot mysnap2 uuid=90076fe6-5ba6-e64a-9321-9279670ed16b (...) utimes utimes dir1 utimes dir1/dir2/dir3 utimes rename dir1/dir2/dir3/dir4 -> o262-7-0 link dir1/dir2/dir3/dir4 -> dir1/dir2/dir3/file1 link dir1/dir2/dir3/dir4/file11 -> dir1/dir2/dir3/file1 ERROR: link dir1/dir2/dir3/dir4/file11 -> dir1/dir2/dir3/file1 failed: Not a directory The following steps happen during the computation of the incremental send stream the lead to this issue: 1) When processing inode 261, we orphanize inode 262 due to a name/location collision with one of the new hard links for inode 261 (created in the second step below). 2) We create one of the 2 new hard links for inode 261, the one whose location is at "dir1/dir2/dir3/dir4". 3) We then attempt to create the other new hard link for inode 261, which has inode 262 as its parent directory. Because the path for this new hard link was computed before we started processing the new references (hard links), it reflects the old name/location of inode 262, that is, it does not account for the orphanization step that happened when we started processing the new references for inode 261, whence it is no longer valid, causing the receiver to fail. So fix this issue by recomputing the full path of new references if we ended up orphanizing other inodes which are directories. A test case for fstests follows soon. Signed-off-by: Filipe Manana <[email protected]>
1 parent 848c23b commit f596278

File tree

1 file changed

+51
-30
lines changed

1 file changed

+51
-30
lines changed

fs/btrfs/send.c

Lines changed: 51 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1856,7 +1856,7 @@ static int is_first_ref(struct btrfs_root *root,
18561856
*/
18571857
static int will_overwrite_ref(struct send_ctx *sctx, u64 dir, u64 dir_gen,
18581858
const char *name, int name_len,
1859-
u64 *who_ino, u64 *who_gen)
1859+
u64 *who_ino, u64 *who_gen, u64 *who_mode)
18601860
{
18611861
int ret = 0;
18621862
u64 gen;
@@ -1905,7 +1905,7 @@ static int will_overwrite_ref(struct send_ctx *sctx, u64 dir, u64 dir_gen,
19051905
if (other_inode > sctx->send_progress ||
19061906
is_waiting_for_move(sctx, other_inode)) {
19071907
ret = get_inode_info(sctx->parent_root, other_inode, NULL,
1908-
who_gen, NULL, NULL, NULL, NULL);
1908+
who_gen, who_mode, NULL, NULL, NULL);
19091909
if (ret < 0)
19101910
goto out;
19111911

@@ -3683,6 +3683,36 @@ static int wait_for_parent_move(struct send_ctx *sctx,
36833683
return ret;
36843684
}
36853685

3686+
static int update_ref_path(struct send_ctx *sctx, struct recorded_ref *ref)
3687+
{
3688+
int ret;
3689+
struct fs_path *new_path;
3690+
3691+
/*
3692+
* Our reference's name member points to its full_path member string, so
3693+
* we use here a new path.
3694+
*/
3695+
new_path = fs_path_alloc();
3696+
if (!new_path)
3697+
return -ENOMEM;
3698+
3699+
ret = get_cur_path(sctx, ref->dir, ref->dir_gen, new_path);
3700+
if (ret < 0) {
3701+
fs_path_free(new_path);
3702+
return ret;
3703+
}
3704+
ret = fs_path_add(new_path, ref->name, ref->name_len);
3705+
if (ret < 0) {
3706+
fs_path_free(new_path);
3707+
return ret;
3708+
}
3709+
3710+
fs_path_free(ref->full_path);
3711+
set_ref_path(ref, new_path);
3712+
3713+
return 0;
3714+
}
3715+
36863716
/*
36873717
* This does all the move/link/unlink/rmdir magic.
36883718
*/
@@ -3696,10 +3726,12 @@ static int process_recorded_refs(struct send_ctx *sctx, int *pending_move)
36963726
struct fs_path *valid_path = NULL;
36973727
u64 ow_inode = 0;
36983728
u64 ow_gen;
3729+
u64 ow_mode;
36993730
int did_overwrite = 0;
37003731
int is_orphan = 0;
37013732
u64 last_dir_ino_rm = 0;
37023733
bool can_rename = true;
3734+
bool orphanized_dir = false;
37033735
bool orphanized_ancestor = false;
37043736

37053737
btrfs_debug(fs_info, "process_recorded_refs %llu", sctx->cur_ino);
@@ -3798,7 +3830,7 @@ static int process_recorded_refs(struct send_ctx *sctx, int *pending_move)
37983830
*/
37993831
ret = will_overwrite_ref(sctx, cur->dir, cur->dir_gen,
38003832
cur->name, cur->name_len,
3801-
&ow_inode, &ow_gen);
3833+
&ow_inode, &ow_gen, &ow_mode);
38023834
if (ret < 0)
38033835
goto out;
38043836
if (ret) {
@@ -3815,6 +3847,8 @@ static int process_recorded_refs(struct send_ctx *sctx, int *pending_move)
38153847
cur->full_path);
38163848
if (ret < 0)
38173849
goto out;
3850+
if (S_ISDIR(ow_mode))
3851+
orphanized_dir = true;
38183852

38193853
/*
38203854
* If ow_inode has its rename operation delayed
@@ -3920,6 +3954,18 @@ static int process_recorded_refs(struct send_ctx *sctx, int *pending_move)
39203954
if (ret < 0)
39213955
goto out;
39223956
} else {
3957+
/*
3958+
* We might have previously orphanized an inode
3959+
* which is an ancestor of our current inode,
3960+
* so our reference's full path, which was
3961+
* computed before any such orphanizations, must
3962+
* be updated.
3963+
*/
3964+
if (orphanized_dir) {
3965+
ret = update_ref_path(sctx, cur);
3966+
if (ret < 0)
3967+
goto out;
3968+
}
39233969
ret = send_link(sctx, cur->full_path,
39243970
valid_path);
39253971
if (ret < 0)
@@ -3990,34 +4036,9 @@ static int process_recorded_refs(struct send_ctx *sctx, int *pending_move)
39904036
* ancestor inode.
39914037
*/
39924038
if (orphanized_ancestor) {
3993-
struct fs_path *new_path;
3994-
3995-
/*
3996-
* Our reference's name member points to
3997-
* its full_path member string, so we
3998-
* use here a new path.
3999-
*/
4000-
new_path = fs_path_alloc();
4001-
if (!new_path) {
4002-
ret = -ENOMEM;
4003-
goto out;
4004-
}
4005-
ret = get_cur_path(sctx, cur->dir,
4006-
cur->dir_gen,
4007-
new_path);
4008-
if (ret < 0) {
4009-
fs_path_free(new_path);
4010-
goto out;
4011-
}
4012-
ret = fs_path_add(new_path,
4013-
cur->name,
4014-
cur->name_len);
4015-
if (ret < 0) {
4016-
fs_path_free(new_path);
4039+
ret = update_ref_path(sctx, cur);
4040+
if (ret < 0)
40174041
goto out;
4018-
}
4019-
fs_path_free(cur->full_path);
4020-
set_ref_path(cur, new_path);
40214042
}
40224043
ret = send_unlink(sctx, cur->full_path);
40234044
if (ret < 0)

0 commit comments

Comments
 (0)