On Sat, Feb 10, 2024 at 10:06:37PM +0800, Guoyu Ou wrote: > When we are checking whether a subvolume is empty in the specified snapshot, > entries that do not belong to this subvolume should be skipped. > > This fixes the following case: > > $ bcachefs subvolume create ./sub > $ cd sub > $ bcachefs subvolume create ./sub2 > $ bcachefs subvolume snapshot . ./snap > $ ls -a snap > . .. > $ rmdir snap > rmdir: failed to remove 'snap': Directory not empty > > Signed-off-by: Guoyu Ou <[email protected]>
So that's an interesting corner case - can you add a ktest test for it? https://evilpiepirate.org/git/ktest.git/ If you're not using it yet, please jump on IRC and ask if you run into any issues getting it going; There's an issue with your fix: in may_delete_deleted_inode(), you're getting the subvol from inode.bi_subvol. That's incorrect, because bi_subvol is only set on subvolume roots, because we can't go through every inode in the subvolume and change bi_subvol when taking a snapshot. You're doing it right in bch2_empty_dir_trans(), which is the path that matters more; may_delete_deleted_inode() is only checking "did this filesystem get borked". I would just pass in 0 from may_delete_deleted_inode(), and then have empty_dir_snapshot() ignore DT_SUBVOL dirents when subvol == 0. It makes the check less strict, but that's ok, the rest of fsck will still catch it. > --- > fs/bcachefs/dirent.c | 7 +++++-- > fs/bcachefs/dirent.h | 2 +- > fs/bcachefs/inode.c | 2 +- > 3 files changed, 7 insertions(+), 4 deletions(-) > > diff --git a/fs/bcachefs/dirent.c b/fs/bcachefs/dirent.c > index 4ae1e9f002a0..82c5bff01411 100644 > --- a/fs/bcachefs/dirent.c > +++ b/fs/bcachefs/dirent.c > @@ -508,7 +508,7 @@ u64 bch2_dirent_lookup(struct bch_fs *c, subvol_inum dir, > return ret; > } > > -int bch2_empty_dir_snapshot(struct btree_trans *trans, u64 dir, u32 snapshot) > +int bch2_empty_dir_snapshot(struct btree_trans *trans, u64 dir, u32 subvol, > u32 snapshot) > { > struct btree_iter iter; > struct bkey_s_c k; > @@ -518,6 +518,9 @@ int bch2_empty_dir_snapshot(struct btree_trans *trans, > u64 dir, u32 snapshot) > SPOS(dir, 0, snapshot), > POS(dir, U64_MAX), 0, k, ret) > if (k.k->type == KEY_TYPE_dirent) { > + struct bkey_s_c_dirent d = bkey_s_c_to_dirent(k); > + if (d.v->d_type == DT_SUBVOL && > le32_to_cpu(d.v->d_parent_subvol) != subvol) > + continue; > ret = -ENOTEMPTY; > break; > } > @@ -531,7 +534,7 @@ int bch2_empty_dir_trans(struct btree_trans *trans, > subvol_inum dir) > u32 snapshot; > > return bch2_subvolume_get_snapshot(trans, dir.subvol, &snapshot) ?: > - bch2_empty_dir_snapshot(trans, dir.inum, snapshot); > + bch2_empty_dir_snapshot(trans, dir.inum, dir.subvol, snapshot); > } > > int bch2_readdir(struct bch_fs *c, subvol_inum inum, struct dir_context *ctx) > diff --git a/fs/bcachefs/dirent.h b/fs/bcachefs/dirent.h > index 21ffeb78f02e..aeb8207ca9f2 100644 > --- a/fs/bcachefs/dirent.h > +++ b/fs/bcachefs/dirent.h > @@ -69,7 +69,7 @@ u64 bch2_dirent_lookup(struct bch_fs *, subvol_inum, > const struct bch_hash_info *, > const struct qstr *, subvol_inum *); > > -int bch2_empty_dir_snapshot(struct btree_trans *, u64, u32); > +int bch2_empty_dir_snapshot(struct btree_trans *, u64, u32, u32); > int bch2_empty_dir_trans(struct btree_trans *, subvol_inum); > int bch2_readdir(struct bch_fs *, subvol_inum, struct dir_context *); > > diff --git a/fs/bcachefs/inode.c b/fs/bcachefs/inode.c > index 086f0090b03a..8f85d0132cc2 100644 > --- a/fs/bcachefs/inode.c > +++ b/fs/bcachefs/inode.c > @@ -1088,7 +1088,7 @@ static int may_delete_deleted_inode(struct btree_trans > *trans, > goto out; > > if (S_ISDIR(inode.bi_mode)) { > - ret = bch2_empty_dir_snapshot(trans, pos.offset, pos.snapshot); > + ret = bch2_empty_dir_snapshot(trans, pos.offset, > inode.bi_subvol, pos.snapshot); > if (fsck_err_on(ret == -ENOTEMPTY, c, deleted_inode_is_dir, > "non empty directory %llu:%u in deleted_inodes > btree", > pos.offset, pos.snapshot)) > -- > 2.43.0 >
