bcachefs: skip invisible entries in empty subvolume checking
authorGuoyu Ou <benogy@gmail.com>
Tue, 13 Feb 2024 08:20:04 +0000 (16:20 +0800)
committerKent Overstreet <kent.overstreet@linux.dev>
Sun, 10 Mar 2024 19:34:07 +0000 (15:34 -0400)
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

As Kent suggested, we pass 0 in may_delete_deleted_inode() to ignore subvols
in the subvol we are checking, because inode.bi_subvol is only set on
subvolume roots, and we can't go through every inode in the subvolume and
change bi_subvol when taking a snapshot. It makes the check less strict, but
that's ok, the rest of fsck will still catch it.

Signed-off-by: Guoyu Ou <benogy@gmail.com>
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/dirent.c
fs/bcachefs/dirent.h
fs/bcachefs/inode.c

index 4ae1e9f002a09b9c7ea3bed1709334f35373b061..82c5bff01411c1181438dd0b923bcbe859798c17 100644 (file)
@@ -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)
index 21ffeb78f02ee3a750a39512f2fb353b594567b5..aeb8207ca9f26efbb426f60c8843083c72ae586c 100644 (file)
@@ -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 *);
 
index 086f0090b03a4015388dce49388ba5951940cb0a..b07ab98e460efc682ed732311f2e89f8529cb799 100644 (file)
@@ -1088,8 +1088,9 @@ 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);
-               if (fsck_err_on(ret == -ENOTEMPTY, c, deleted_inode_is_dir,
+               ret = bch2_empty_dir_snapshot(trans, pos.offset, 0, pos.snapshot);
+               if (fsck_err_on(bch2_err_matches(ret, ENOTEMPTY),
+                               c, deleted_inode_is_dir,
                                "non empty directory %llu:%u in deleted_inodes btree",
                                pos.offset, pos.snapshot))
                        goto delete;