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 882b2a217b513fc9d68a689b621f220b595cbeba..d37bd07afbfe4088ebb9b92feb56ff86127ae1ab 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 a82a9d754fdab0d739dd518e70244f183f116694..fe3fc14d3c9a19452b7a4ad54f589156d523258b 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 2aa3881105972be8666224bc1e24cdbff06e5626..624e6f963240f82f56d4a111f5041e6fb1d9daa9 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 68be3a450ca12f2ebdd4e7f152a6a85e1758e482..ce7aed12194238071f8fbf37aa111160ced286c9 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 f0979ab56a47d06e6ac9aa58e8b8b2bf2a9d9e5e..903c05162c0688ae902321aace955ca27fa5e2f9 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 *);