bcachefs: Check for subvolume children when deleting subvolumes
Recursively destroying subvolumes isn't allowed yet. Fixes: https://github.com/koverstreet/bcachefs/issues/634 Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
parent
b26d79147f
commit
835cd3e147
|
@ -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);
|
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)
|
if (d.v->d_type == DT_SUBVOL && le32_to_cpu(d.v->d_parent_subvol) != subvol)
|
||||||
continue;
|
continue;
|
||||||
ret = -ENOTEMPTY;
|
ret = -BCH_ERR_ENOTEMPTY_dir_not_empty;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
bch2_trans_iter_exit(trans, &iter);
|
bch2_trans_iter_exit(trans, &iter);
|
||||||
|
|
|
@ -109,6 +109,8 @@
|
||||||
x(ENOENT, ENOENT_dirent_doesnt_match_inode) \
|
x(ENOENT, ENOENT_dirent_doesnt_match_inode) \
|
||||||
x(ENOENT, ENOENT_dev_not_found) \
|
x(ENOENT, ENOENT_dev_not_found) \
|
||||||
x(ENOENT, ENOENT_dev_idx_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, open_buckets_empty) \
|
||||||
x(0, freelist_empty) \
|
x(0, freelist_empty) \
|
||||||
x(BCH_ERR_freelist_empty, no_buckets_found) \
|
x(BCH_ERR_freelist_empty, no_buckets_found) \
|
||||||
|
|
|
@ -243,7 +243,7 @@ int bch2_unlink_trans(struct btree_trans *trans,
|
||||||
struct bch_inode_unpacked *dir_u,
|
struct bch_inode_unpacked *dir_u,
|
||||||
struct bch_inode_unpacked *inode_u,
|
struct bch_inode_unpacked *inode_u,
|
||||||
const struct qstr *name,
|
const struct qstr *name,
|
||||||
bool deleting_snapshot)
|
bool deleting_subvol)
|
||||||
{
|
{
|
||||||
struct bch_fs *c = trans->c;
|
struct bch_fs *c = trans->c;
|
||||||
struct btree_iter dir_iter = { NULL };
|
struct btree_iter dir_iter = { NULL };
|
||||||
|
@ -271,18 +271,25 @@ int bch2_unlink_trans(struct btree_trans *trans,
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
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);
|
ret = bch2_empty_dir_trans(trans, inum);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deleting_snapshot && !inode_u->bi_subvol) {
|
if (deleting_subvol && !inode_u->bi_subvol) {
|
||||||
ret = -BCH_ERR_ENOENT_not_subvol;
|
ret = -BCH_ERR_ENOENT_not_subvol;
|
||||||
goto err;
|
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);
|
ret = bch2_subvolume_unlink(trans, inode_u->bi_subvol);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
@ -479,10 +486,10 @@ int bch2_rename_trans(struct btree_trans *trans,
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (S_ISDIR(dst_inode_u->bi_mode) &&
|
if (S_ISDIR(dst_inode_u->bi_mode)) {
|
||||||
bch2_empty_dir_trans(trans, dst_inum)) {
|
ret = bch2_empty_dir_trans(trans, dst_inum);
|
||||||
ret = -ENOTEMPTY;
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -262,6 +262,19 @@ int bch2_subvolume_trigger(struct btree_trans *trans,
|
||||||
return 0;
|
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
|
static __always_inline int
|
||||||
bch2_subvolume_get_inlined(struct btree_trans *trans, unsigned subvol,
|
bch2_subvolume_get_inlined(struct btree_trans *trans, unsigned subvol,
|
||||||
bool inconsistent_if_not_found,
|
bool inconsistent_if_not_found,
|
||||||
|
|
|
@ -23,6 +23,7 @@ int bch2_subvolume_trigger(struct btree_trans *, enum btree_id, unsigned,
|
||||||
.min_val_size = 16, \
|
.min_val_size = 16, \
|
||||||
})
|
})
|
||||||
|
|
||||||
|
int bch2_subvol_has_children(struct btree_trans *, u32);
|
||||||
int bch2_subvolume_get(struct btree_trans *, unsigned,
|
int bch2_subvolume_get(struct btree_trans *, unsigned,
|
||||||
bool, int, struct bch_subvolume *);
|
bool, int, struct bch_subvolume *);
|
||||||
int bch2_subvolume_get_snapshot(struct btree_trans *, u32, u32 *);
|
int bch2_subvolume_get_snapshot(struct btree_trans *, u32, u32 *);
|
||||||
|
|
Loading…
Reference in New Issue