bcachefs: Check for subvolume children when deleting subvolumes
authorKent Overstreet <kent.overstreet@linux.dev>
Sat, 10 Feb 2024 02:01:04 +0000 (21:01 -0500)
committerKent Overstreet <kent.overstreet@linux.dev>
Thu, 14 Mar 2024 01:22:24 +0000 (21:22 -0400)
Recursively destroying subvolumes isn't allowed yet.

Fixes: https://github.com/koverstreet/bcachefs/issues/634
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/dirent.c
fs/bcachefs/errcode.h
fs/bcachefs/fs-common.c
fs/bcachefs/subvolume.c
fs/bcachefs/subvolume.h

index 882b2a2..d37bd07 100644 (file)
@@ -525,7 +525,7 @@ int bch2_empty_dir_snapshot(struct btree_trans *trans, u64 dir, u32 subvol, u32
                        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;
+                       ret = -BCH_ERR_ENOTEMPTY_dir_not_empty;
                        break;
                }
        bch2_trans_iter_exit(trans, &iter);
index a82a9d7..fe3fc14 100644 (file)
        x(ENOENT,                       ENOENT_dirent_doesnt_match_inode)       \
        x(ENOENT,                       ENOENT_dev_not_found)                   \
        x(ENOENT,                       ENOENT_dev_idx_not_found)               \
+       x(ENOTEMPTY,                    ENOTEMPTY_dir_not_empty)                \
+       x(ENOTEMPTY,                    ENOTEMPTY_subvol_not_empty)             \
        x(0,                            open_buckets_empty)                     \
        x(0,                            freelist_empty)                         \
        x(BCH_ERR_freelist_empty,       no_buckets_found)                       \
index 2aa3881..624e6f9 100644 (file)
@@ -243,7 +243,7 @@ int bch2_unlink_trans(struct btree_trans *trans,
                      struct bch_inode_unpacked *dir_u,
                      struct bch_inode_unpacked *inode_u,
                      const struct qstr *name,
-                     bool deleting_snapshot)
+                     bool deleting_subvol)
 {
        struct bch_fs *c = trans->c;
        struct btree_iter dir_iter = { NULL };
@@ -271,18 +271,25 @@ int bch2_unlink_trans(struct btree_trans *trans,
        if (ret)
                goto err;
 
-       if (!deleting_snapshot && S_ISDIR(inode_u->bi_mode)) {
+       if (!deleting_subvol && S_ISDIR(inode_u->bi_mode)) {
                ret = bch2_empty_dir_trans(trans, inum);
                if (ret)
                        goto err;
        }
 
-       if (deleting_snapshot && !inode_u->bi_subvol) {
+       if (deleting_subvol && !inode_u->bi_subvol) {
                ret = -BCH_ERR_ENOENT_not_subvol;
                goto err;
        }
 
-       if (deleting_snapshot || inode_u->bi_subvol) {
+       if (inode_u->bi_subvol) {
+               /* Recursive subvolume destroy not allowed (yet?) */
+               ret = bch2_subvol_has_children(trans, inode_u->bi_subvol);
+               if (ret)
+                       goto err;
+       }
+
+       if (deleting_subvol || inode_u->bi_subvol) {
                ret = bch2_subvolume_unlink(trans, inode_u->bi_subvol);
                if (ret)
                        goto err;
@@ -479,10 +486,10 @@ int bch2_rename_trans(struct btree_trans *trans,
                        goto err;
                }
 
-               if (S_ISDIR(dst_inode_u->bi_mode) &&
-                   bch2_empty_dir_trans(trans, dst_inum)) {
-                       ret = -ENOTEMPTY;
-                       goto err;
+               if (S_ISDIR(dst_inode_u->bi_mode)) {
+                       ret = bch2_empty_dir_trans(trans, dst_inum);
+                       if (ret)
+                               goto err;
                }
        }
 
index 68be3a4..ce7aed1 100644 (file)
@@ -262,6 +262,19 @@ int bch2_subvolume_trigger(struct btree_trans *trans,
        return 0;
 }
 
+int bch2_subvol_has_children(struct btree_trans *trans, u32 subvol)
+{
+       struct btree_iter iter;
+
+       bch2_trans_iter_init(trans, &iter, BTREE_ID_subvolume_children, POS(subvol, 0), 0);
+       struct bkey_s_c k = bch2_btree_iter_peek(&iter);
+       bch2_trans_iter_exit(trans, &iter);
+
+       return bkey_err(k) ?: k.k && k.k->p.inode == subvol
+               ? -BCH_ERR_ENOTEMPTY_subvol_not_empty
+               : 0;
+}
+
 static __always_inline int
 bch2_subvolume_get_inlined(struct btree_trans *trans, unsigned subvol,
                           bool inconsistent_if_not_found,
index f0979ab..903c051 100644 (file)
@@ -23,6 +23,7 @@ int bch2_subvolume_trigger(struct btree_trans *, enum btree_id, unsigned,
        .min_val_size   = 16,                                   \
 })
 
+int bch2_subvol_has_children(struct btree_trans *, u32);
 int bch2_subvolume_get(struct btree_trans *, unsigned,
                       bool, int, struct bch_subvolume *);
 int bch2_subvolume_get_snapshot(struct btree_trans *, u32, u32 *);