Skip to content

Commit

Permalink
bcachefs: Snapshot creation, deletion
Browse files Browse the repository at this point in the history
This is the final patch in the patch series implementing snapshots.
This patch implements two new ioctls that work like creation and
deletion of directories, but fancier.

 - BCH_IOCTL_SUBVOLUME_CREATE, for creating new subvolumes and snaphots
 - BCH_IOCTL_SUBVOLUME_DESTROY, for deleting subvolumes and snapshots

Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
  • Loading branch information
koverstreet authored and Kent Overstreet committed Oct 22, 2023
1 parent a861c72 commit 42d2373
Show file tree
Hide file tree
Showing 10 changed files with 348 additions and 69 deletions.
8 changes: 0 additions & 8 deletions fs/bcachefs/dirent.c
Original file line number Diff line number Diff line change
Expand Up @@ -383,14 +383,6 @@ int bch2_dirent_rename(struct btree_trans *trans,
return ret;
}

int bch2_dirent_delete_at(struct btree_trans *trans,
const struct bch_hash_info *hash_info,
struct btree_iter *iter)
{
return bch2_hash_delete_at(trans, bch2_dirent_hash_desc,
hash_info, iter);
}

int __bch2_dirent_lookup_trans(struct btree_trans *trans,
struct btree_iter *iter,
subvol_inum dir,
Expand Down
4 changes: 0 additions & 4 deletions fs/bcachefs/dirent.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,6 @@ int bch2_dirent_create(struct btree_trans *, subvol_inum,
const struct bch_hash_info *, u8,
const struct qstr *, u64, u64 *, int);

int bch2_dirent_delete_at(struct btree_trans *,
const struct bch_hash_info *,
struct btree_iter *);

int __bch2_dirent_read_target(struct btree_trans *, struct bkey_s_c_dirent,
u32 *, u32 *, u64 *, bool);

Expand Down
182 changes: 148 additions & 34 deletions fs/bcachefs/fs-common.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@

#include <linux/posix_acl.h>

static inline int is_subdir_for_nlink(struct bch_inode_unpacked *inode)
{
return S_ISDIR(inode->bi_mode) && !inode->bi_subvol;
}

int bch2_create_trans(struct btree_trans *trans,
subvol_inum dir,
struct bch_inode_unpacked *dir_u,
Expand All @@ -19,6 +24,7 @@ int bch2_create_trans(struct btree_trans *trans,
uid_t uid, gid_t gid, umode_t mode, dev_t rdev,
struct posix_acl *default_acl,
struct posix_acl *acl,
subvol_inum snapshot_src,
unsigned flags)
{
struct bch_fs *c = trans->c;
Expand All @@ -27,10 +33,9 @@ int bch2_create_trans(struct btree_trans *trans,
subvol_inum new_inum = dir;
u64 now = bch2_current_time(c);
u64 cpu = raw_smp_processor_id();
u64 dir_offset = 0;
u64 dir_target;
u32 snapshot;
unsigned dir_type;
unsigned dir_type = mode_to_type(mode);
int ret;

ret = bch2_subvolume_get_snapshot(trans, dir.subvol, &snapshot);
Expand All @@ -41,37 +46,122 @@ int bch2_create_trans(struct btree_trans *trans,
if (ret)
goto err;

bch2_inode_init_late(new_inode, now, uid, gid, mode, rdev, dir_u);
if (!(flags & BCH_CREATE_SNAPSHOT)) {
/* Normal create path - allocate a new inode: */
bch2_inode_init_late(new_inode, now, uid, gid, mode, rdev, dir_u);

if (!name)
new_inode->bi_flags |= BCH_INODE_UNLINKED;
if (flags & BCH_CREATE_TMPFILE)
new_inode->bi_flags |= BCH_INODE_UNLINKED;

ret = bch2_inode_create(trans, &inode_iter, new_inode, snapshot, cpu);
if (ret)
goto err;
ret = bch2_inode_create(trans, &inode_iter, new_inode, snapshot, cpu);
if (ret)
goto err;

snapshot_src = (subvol_inum) { 0 };
} else {
/*
* Creating a snapshot - we're not allocating a new inode, but
* we do have to lookup the root inode of the subvolume we're
* snapshotting and update it (in the new snapshot):
*/

if (!snapshot_src.inum) {
/* Inode wasn't specified, just snapshot: */
struct btree_iter subvol_iter;
struct bkey_s_c k;

bch2_trans_iter_init(trans, &subvol_iter, BTREE_ID_subvolumes,
POS(0, snapshot_src.subvol), 0);
k = bch2_btree_iter_peek_slot(&subvol_iter);

ret = bkey_err(k);
if (!ret && k.k->type != KEY_TYPE_subvolume) {
bch_err(c, "subvolume %u not found",
snapshot_src.subvol);
ret = -ENOENT;
}

if (!ret)
snapshot_src.inum = le64_to_cpu(bkey_s_c_to_subvolume(k).v->inode);
bch2_trans_iter_exit(trans, &subvol_iter);

if (ret)
goto err;
}

ret = bch2_inode_peek(trans, &inode_iter, new_inode, snapshot_src,
BTREE_ITER_INTENT);
if (ret)
goto err;

if (new_inode->bi_subvol != snapshot_src.subvol) {
/* Not a subvolume root: */
ret = -EINVAL;
goto err;
}

/*
* If we're not root, we have to own the subvolume being
* snapshotted:
*/
if (uid && new_inode->bi_uid != uid) {
ret = -EPERM;
goto err;
}

flags |= BCH_CREATE_SUBVOL;
}

new_inum.inum = new_inode->bi_inum;
dir_target = new_inode->bi_inum;
dir_type = mode_to_type(new_inode->bi_mode);

if (default_acl) {
ret = bch2_set_acl_trans(trans, new_inum, new_inode,
default_acl, ACL_TYPE_DEFAULT);
if (flags & BCH_CREATE_SUBVOL) {
u32 new_subvol, dir_snapshot;

ret = bch2_subvolume_create(trans, new_inode->bi_inum,
snapshot_src.subvol,
&new_subvol, &snapshot,
(flags & BCH_CREATE_SNAPSHOT_RO) != 0);
if (ret)
goto err;
}

if (acl) {
ret = bch2_set_acl_trans(trans, new_inum, new_inode,
acl, ACL_TYPE_ACCESS);
new_inode->bi_parent_subvol = dir.subvol;
new_inode->bi_subvol = new_subvol;
new_inum.subvol = new_subvol;
dir_target = new_subvol;
dir_type = DT_SUBVOL;

ret = bch2_subvolume_get_snapshot(trans, dir.subvol, &dir_snapshot);
if (ret)
goto err;

bch2_btree_iter_set_snapshot(&dir_iter, dir_snapshot);
ret = bch2_btree_iter_traverse(&dir_iter);
if (ret)
goto err;
}

if (name) {
if (!(flags & BCH_CREATE_SNAPSHOT)) {
if (default_acl) {
ret = bch2_set_acl_trans(trans, new_inum, new_inode,
default_acl, ACL_TYPE_DEFAULT);
if (ret)
goto err;
}

if (acl) {
ret = bch2_set_acl_trans(trans, new_inum, new_inode,
acl, ACL_TYPE_ACCESS);
if (ret)
goto err;
}
}

if (!(flags & BCH_CREATE_TMPFILE)) {
struct bch_hash_info dir_hash = bch2_hash_info_init(c, dir_u);
u64 dir_offset;

if (S_ISDIR(new_inode->bi_mode))
if (is_subdir_for_nlink(new_inode))
dir_u->bi_nlink++;
dir_u->bi_mtime = dir_u->bi_ctime = now;

Expand All @@ -87,11 +177,11 @@ int bch2_create_trans(struct btree_trans *trans,
BCH_HASH_SET_MUST_CREATE);
if (ret)
goto err;
}

if (c->sb.version >= bcachefs_metadata_version_inode_backpointers) {
new_inode->bi_dir = dir_u->bi_inum;
new_inode->bi_dir_offset = dir_offset;
if (c->sb.version >= bcachefs_metadata_version_inode_backpointers) {
new_inode->bi_dir = dir_u->bi_inum;
new_inode->bi_dir_offset = dir_offset;
}
}

inode_iter.flags &= ~BTREE_ITER_ALL_SNAPSHOTS;
Expand Down Expand Up @@ -160,7 +250,8 @@ int bch2_unlink_trans(struct btree_trans *trans,
subvol_inum dir,
struct bch_inode_unpacked *dir_u,
struct bch_inode_unpacked *inode_u,
const struct qstr *name)
const struct qstr *name,
int deleting_snapshot)
{
struct bch_fs *c = trans->c;
struct btree_iter dir_iter = { NULL };
Expand All @@ -169,6 +260,7 @@ int bch2_unlink_trans(struct btree_trans *trans,
struct bch_hash_info dir_hash;
subvol_inum inum;
u64 now = bch2_current_time(c);
struct bkey_s_c k;
int ret;

ret = bch2_inode_peek(trans, &dir_iter, dir_u, dir, BTREE_ITER_INTENT);
Expand All @@ -187,29 +279,51 @@ int bch2_unlink_trans(struct btree_trans *trans,
if (ret)
goto err;

if (inode_u->bi_dir == dirent_iter.pos.inode &&
inode_u->bi_dir_offset == dirent_iter.pos.offset) {
inode_u->bi_dir = 0;
inode_u->bi_dir_offset = 0;
if (deleting_snapshot == 1 && !inode_u->bi_subvol) {
ret = -ENOENT;
goto err;
}

if (S_ISDIR(inode_u->bi_mode)) {
if (deleting_snapshot <= 0 && S_ISDIR(inode_u->bi_mode)) {
ret = bch2_empty_dir_trans(trans, inum);
if (ret)
goto err;
}

if (dir.subvol != inum.subvol) {
ret = bch2_subvolume_delete(trans, inum.subvol, false);
if (inode_u->bi_subvol) {
ret = bch2_subvolume_delete(trans, inode_u->bi_subvol,
deleting_snapshot);
if (ret)
goto err;

k = bch2_btree_iter_peek_slot(&dirent_iter);
ret = bkey_err(k);
if (ret)
goto err;

/*
* If we're deleting a subvolume, we need to really delete the
* dirent, not just emit a whiteout in the current snapshot:
*/
bch2_btree_iter_set_snapshot(&dirent_iter, k.k->p.snapshot);
ret = bch2_btree_iter_traverse(&dirent_iter);
if (ret)
goto err;
}

if (inode_u->bi_dir == dirent_iter.pos.inode &&
inode_u->bi_dir_offset == dirent_iter.pos.offset) {
inode_u->bi_dir = 0;
inode_u->bi_dir_offset = 0;
}

dir_u->bi_mtime = dir_u->bi_ctime = inode_u->bi_ctime = now;
dir_u->bi_nlink -= S_ISDIR(inode_u->bi_mode);
dir_u->bi_nlink -= is_subdir_for_nlink(inode_u);
bch2_inode_nlink_dec(inode_u);

ret = bch2_dirent_delete_at(trans, &dir_hash, &dirent_iter) ?:
ret = bch2_hash_delete_at(trans, bch2_dirent_hash_desc,
&dir_hash, &dirent_iter,
BTREE_UPDATE_INTERNAL_SNAPSHOT_NODE) ?:
bch2_inode_write(trans, &dir_iter, dir_u) ?:
bch2_inode_write(trans, &inode_iter, inode_u);
err:
Expand Down Expand Up @@ -348,12 +462,12 @@ int bch2_rename_trans(struct btree_trans *trans,
goto err;
}

if (S_ISDIR(src_inode_u->bi_mode)) {
if (is_subdir_for_nlink(src_inode_u)) {
src_dir_u->bi_nlink--;
dst_dir_u->bi_nlink++;
}

if (dst_inum.inum && S_ISDIR(dst_inode_u->bi_mode)) {
if (dst_inum.inum && is_subdir_for_nlink(dst_inode_u)) {
dst_dir_u->bi_nlink--;
src_dir_u->bi_nlink += mode == BCH_RENAME_EXCHANGE;
}
Expand Down
7 changes: 5 additions & 2 deletions fs/bcachefs/fs-common.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
struct posix_acl;

#define BCH_CREATE_TMPFILE (1U << 0)
#define BCH_CREATE_SUBVOL (1U << 1)
#define BCH_CREATE_SNAPSHOT (1U << 2)
#define BCH_CREATE_SNAPSHOT_RO (1U << 3)

int bch2_create_trans(struct btree_trans *, subvol_inum,
struct bch_inode_unpacked *,
Expand All @@ -13,7 +16,7 @@ int bch2_create_trans(struct btree_trans *, subvol_inum,
uid_t, gid_t, umode_t, dev_t,
struct posix_acl *,
struct posix_acl *,
unsigned);
subvol_inum, unsigned);

int bch2_link_trans(struct btree_trans *,
subvol_inum, struct bch_inode_unpacked *,
Expand All @@ -23,7 +26,7 @@ int bch2_link_trans(struct btree_trans *,
int bch2_unlink_trans(struct btree_trans *, subvol_inum,
struct bch_inode_unpacked *,
struct bch_inode_unpacked *,
const struct qstr *);
const struct qstr *, int);

int bch2_rename_trans(struct btree_trans *,
subvol_inum, struct bch_inode_unpacked *,
Expand Down
Loading

0 comments on commit 42d2373

Please sign in to comment.