Skip to content

Commit 0b3f407

Browse files
fdmananakdave
authored andcommitted
btrfs: send: fix wrong file path when there is an inode with a pending rmdir
When doing an incremental send, if we have a new inode that happens to have the same number that an old directory inode had in the base snapshot and that old directory has a pending rmdir operation, we end up computing a wrong path for the new inode, causing the receiver to fail. Example reproducer: $ cat test-send-rmdir.sh #!/bin/bash DEV=/dev/sdi MNT=/mnt/sdi mkfs.btrfs -f $DEV >/dev/null mount $DEV $MNT mkdir $MNT/dir touch $MNT/dir/file1 touch $MNT/dir/file2 touch $MNT/dir/file3 # Filesystem looks like: # # . (ino 256) # |----- dir/ (ino 257) # |----- file1 (ino 258) # |----- file2 (ino 259) # |----- file3 (ino 260) # btrfs subvolume snapshot -r $MNT $MNT/snap1 btrfs send -f /tmp/snap1.send $MNT/snap1 # Now remove our directory and all its files. rm -fr $MNT/dir # Unmount the filesystem and mount it again. This is to ensure that # the next inode that is created ends up with the same inode number # that our directory "dir" had, 257, which is the first free "objectid" # available after mounting again the filesystem. umount $MNT mount $DEV $MNT # Now create a new file (it could be a directory as well). touch $MNT/newfile # Filesystem now looks like: # # . (ino 256) # |----- newfile (ino 257) # btrfs subvolume snapshot -r $MNT $MNT/snap2 btrfs send -f /tmp/snap2.send -p $MNT/snap1 $MNT/snap2 # Now unmount the filesystem, create a new one, mount it and try to apply # both send streams to recreate both snapshots. umount $DEV mkfs.btrfs -f $DEV >/dev/null mount $DEV $MNT btrfs receive -f /tmp/snap1.send $MNT btrfs receive -f /tmp/snap2.send $MNT umount $MNT When running the test, the receive operation for the incremental stream fails: $ ./test-send-rmdir.sh Create a readonly snapshot of '/mnt/sdi' in '/mnt/sdi/snap1' At subvol /mnt/sdi/snap1 Create a readonly snapshot of '/mnt/sdi' in '/mnt/sdi/snap2' At subvol /mnt/sdi/snap2 At subvol snap1 At snapshot snap2 ERROR: chown o257-9-0 failed: No such file or directory So fix this by tracking directories that have a pending rmdir by inode number and generation number, instead of only inode number. A test case for fstests follows soon. Reported-by: Massimo B. <[email protected]> Tested-by: Massimo B. <[email protected]> Link: https://lore.kernel.org/linux-btrfs/[email protected]/ CC: [email protected] # 4.19+ Signed-off-by: Filipe Manana <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent ae5e070 commit 0b3f407

1 file changed

Lines changed: 31 additions & 18 deletions

File tree

fs/btrfs/send.c

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ struct waiting_dir_move {
236236
* after this directory is moved, we can try to rmdir the ino rmdir_ino.
237237
*/
238238
u64 rmdir_ino;
239+
u64 rmdir_gen;
239240
bool orphanized;
240241
};
241242

@@ -316,7 +317,7 @@ static int is_waiting_for_move(struct send_ctx *sctx, u64 ino);
316317
static struct waiting_dir_move *
317318
get_waiting_dir_move(struct send_ctx *sctx, u64 ino);
318319

319-
static int is_waiting_for_rm(struct send_ctx *sctx, u64 dir_ino);
320+
static int is_waiting_for_rm(struct send_ctx *sctx, u64 dir_ino, u64 gen);
320321

321322
static int need_send_hole(struct send_ctx *sctx)
322323
{
@@ -2299,7 +2300,7 @@ static int get_cur_path(struct send_ctx *sctx, u64 ino, u64 gen,
22992300

23002301
fs_path_reset(name);
23012302

2302-
if (is_waiting_for_rm(sctx, ino)) {
2303+
if (is_waiting_for_rm(sctx, ino, gen)) {
23032304
ret = gen_unique_name(sctx, ino, gen, name);
23042305
if (ret < 0)
23052306
goto out;
@@ -2858,8 +2859,8 @@ static int orphanize_inode(struct send_ctx *sctx, u64 ino, u64 gen,
28582859
return ret;
28592860
}
28602861

2861-
static struct orphan_dir_info *
2862-
add_orphan_dir_info(struct send_ctx *sctx, u64 dir_ino)
2862+
static struct orphan_dir_info *add_orphan_dir_info(struct send_ctx *sctx,
2863+
u64 dir_ino, u64 dir_gen)
28632864
{
28642865
struct rb_node **p = &sctx->orphan_dirs.rb_node;
28652866
struct rb_node *parent = NULL;
@@ -2868,29 +2869,32 @@ add_orphan_dir_info(struct send_ctx *sctx, u64 dir_ino)
28682869
while (*p) {
28692870
parent = *p;
28702871
entry = rb_entry(parent, struct orphan_dir_info, node);
2871-
if (dir_ino < entry->ino) {
2872+
if (dir_ino < entry->ino)
28722873
p = &(*p)->rb_left;
2873-
} else if (dir_ino > entry->ino) {
2874+
else if (dir_ino > entry->ino)
28742875
p = &(*p)->rb_right;
2875-
} else {
2876+
else if (dir_gen < entry->gen)
2877+
p = &(*p)->rb_left;
2878+
else if (dir_gen > entry->gen)
2879+
p = &(*p)->rb_right;
2880+
else
28762881
return entry;
2877-
}
28782882
}
28792883

28802884
odi = kmalloc(sizeof(*odi), GFP_KERNEL);
28812885
if (!odi)
28822886
return ERR_PTR(-ENOMEM);
28832887
odi->ino = dir_ino;
2884-
odi->gen = 0;
2888+
odi->gen = dir_gen;
28852889
odi->last_dir_index_offset = 0;
28862890

28872891
rb_link_node(&odi->node, parent, p);
28882892
rb_insert_color(&odi->node, &sctx->orphan_dirs);
28892893
return odi;
28902894
}
28912895

2892-
static struct orphan_dir_info *
2893-
get_orphan_dir_info(struct send_ctx *sctx, u64 dir_ino)
2896+
static struct orphan_dir_info *get_orphan_dir_info(struct send_ctx *sctx,
2897+
u64 dir_ino, u64 gen)
28942898
{
28952899
struct rb_node *n = sctx->orphan_dirs.rb_node;
28962900
struct orphan_dir_info *entry;
@@ -2901,15 +2905,19 @@ get_orphan_dir_info(struct send_ctx *sctx, u64 dir_ino)
29012905
n = n->rb_left;
29022906
else if (dir_ino > entry->ino)
29032907
n = n->rb_right;
2908+
else if (gen < entry->gen)
2909+
n = n->rb_left;
2910+
else if (gen > entry->gen)
2911+
n = n->rb_right;
29042912
else
29052913
return entry;
29062914
}
29072915
return NULL;
29082916
}
29092917

2910-
static int is_waiting_for_rm(struct send_ctx *sctx, u64 dir_ino)
2918+
static int is_waiting_for_rm(struct send_ctx *sctx, u64 dir_ino, u64 gen)
29112919
{
2912-
struct orphan_dir_info *odi = get_orphan_dir_info(sctx, dir_ino);
2920+
struct orphan_dir_info *odi = get_orphan_dir_info(sctx, dir_ino, gen);
29132921

29142922
return odi != NULL;
29152923
}
@@ -2954,7 +2962,7 @@ static int can_rmdir(struct send_ctx *sctx, u64 dir, u64 dir_gen,
29542962
key.type = BTRFS_DIR_INDEX_KEY;
29552963
key.offset = 0;
29562964

2957-
odi = get_orphan_dir_info(sctx, dir);
2965+
odi = get_orphan_dir_info(sctx, dir, dir_gen);
29582966
if (odi)
29592967
key.offset = odi->last_dir_index_offset;
29602968

@@ -2985,20 +2993,21 @@ static int can_rmdir(struct send_ctx *sctx, u64 dir, u64 dir_gen,
29852993

29862994
dm = get_waiting_dir_move(sctx, loc.objectid);
29872995
if (dm) {
2988-
odi = add_orphan_dir_info(sctx, dir);
2996+
odi = add_orphan_dir_info(sctx, dir, dir_gen);
29892997
if (IS_ERR(odi)) {
29902998
ret = PTR_ERR(odi);
29912999
goto out;
29923000
}
29933001
odi->gen = dir_gen;
29943002
odi->last_dir_index_offset = found_key.offset;
29953003
dm->rmdir_ino = dir;
3004+
dm->rmdir_gen = dir_gen;
29963005
ret = 0;
29973006
goto out;
29983007
}
29993008

30003009
if (loc.objectid > send_progress) {
3001-
odi = add_orphan_dir_info(sctx, dir);
3010+
odi = add_orphan_dir_info(sctx, dir, dir_gen);
30023011
if (IS_ERR(odi)) {
30033012
ret = PTR_ERR(odi);
30043013
goto out;
@@ -3038,6 +3047,7 @@ static int add_waiting_dir_move(struct send_ctx *sctx, u64 ino, bool orphanized)
30383047
return -ENOMEM;
30393048
dm->ino = ino;
30403049
dm->rmdir_ino = 0;
3050+
dm->rmdir_gen = 0;
30413051
dm->orphanized = orphanized;
30423052

30433053
while (*p) {
@@ -3183,7 +3193,7 @@ static int path_loop(struct send_ctx *sctx, struct fs_path *name,
31833193
while (ino != BTRFS_FIRST_FREE_OBJECTID) {
31843194
fs_path_reset(name);
31853195

3186-
if (is_waiting_for_rm(sctx, ino))
3196+
if (is_waiting_for_rm(sctx, ino, gen))
31873197
break;
31883198
if (is_waiting_for_move(sctx, ino)) {
31893199
if (*ancestor_ino == 0)
@@ -3223,6 +3233,7 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm)
32233233
u64 parent_ino, parent_gen;
32243234
struct waiting_dir_move *dm = NULL;
32253235
u64 rmdir_ino = 0;
3236+
u64 rmdir_gen;
32263237
u64 ancestor;
32273238
bool is_orphan;
32283239
int ret;
@@ -3237,6 +3248,7 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm)
32373248
dm = get_waiting_dir_move(sctx, pm->ino);
32383249
ASSERT(dm);
32393250
rmdir_ino = dm->rmdir_ino;
3251+
rmdir_gen = dm->rmdir_gen;
32403252
is_orphan = dm->orphanized;
32413253
free_waiting_dir_move(sctx, dm);
32423254

@@ -3273,6 +3285,7 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm)
32733285
dm = get_waiting_dir_move(sctx, pm->ino);
32743286
ASSERT(dm);
32753287
dm->rmdir_ino = rmdir_ino;
3288+
dm->rmdir_gen = rmdir_gen;
32763289
}
32773290
goto out;
32783291
}
@@ -3291,7 +3304,7 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm)
32913304
struct orphan_dir_info *odi;
32923305
u64 gen;
32933306

3294-
odi = get_orphan_dir_info(sctx, rmdir_ino);
3307+
odi = get_orphan_dir_info(sctx, rmdir_ino, rmdir_gen);
32953308
if (!odi) {
32963309
/* already deleted */
32973310
goto finish;

0 commit comments

Comments
 (0)