Skip to content

Commit 42d2373

Browse files
koverstreetKent Overstreet
authored and
Kent Overstreet
committed
bcachefs: Snapshot creation, deletion
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 <[email protected]>
1 parent a861c72 commit 42d2373

File tree

10 files changed

+348
-69
lines changed

10 files changed

+348
-69
lines changed

fs/bcachefs/dirent.c

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -383,14 +383,6 @@ int bch2_dirent_rename(struct btree_trans *trans,
383383
return ret;
384384
}
385385

386-
int bch2_dirent_delete_at(struct btree_trans *trans,
387-
const struct bch_hash_info *hash_info,
388-
struct btree_iter *iter)
389-
{
390-
return bch2_hash_delete_at(trans, bch2_dirent_hash_desc,
391-
hash_info, iter);
392-
}
393-
394386
int __bch2_dirent_lookup_trans(struct btree_trans *trans,
395387
struct btree_iter *iter,
396388
subvol_inum dir,

fs/bcachefs/dirent.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,6 @@ int bch2_dirent_create(struct btree_trans *, subvol_inum,
3333
const struct bch_hash_info *, u8,
3434
const struct qstr *, u64, u64 *, int);
3535

36-
int bch2_dirent_delete_at(struct btree_trans *,
37-
const struct bch_hash_info *,
38-
struct btree_iter *);
39-
4036
int __bch2_dirent_read_target(struct btree_trans *, struct bkey_s_c_dirent,
4137
u32 *, u32 *, u64 *, bool);
4238

fs/bcachefs/fs-common.c

Lines changed: 148 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@
1111

1212
#include <linux/posix_acl.h>
1313

14+
static inline int is_subdir_for_nlink(struct bch_inode_unpacked *inode)
15+
{
16+
return S_ISDIR(inode->bi_mode) && !inode->bi_subvol;
17+
}
18+
1419
int bch2_create_trans(struct btree_trans *trans,
1520
subvol_inum dir,
1621
struct bch_inode_unpacked *dir_u,
@@ -19,6 +24,7 @@ int bch2_create_trans(struct btree_trans *trans,
1924
uid_t uid, gid_t gid, umode_t mode, dev_t rdev,
2025
struct posix_acl *default_acl,
2126
struct posix_acl *acl,
27+
subvol_inum snapshot_src,
2228
unsigned flags)
2329
{
2430
struct bch_fs *c = trans->c;
@@ -27,10 +33,9 @@ int bch2_create_trans(struct btree_trans *trans,
2733
subvol_inum new_inum = dir;
2834
u64 now = bch2_current_time(c);
2935
u64 cpu = raw_smp_processor_id();
30-
u64 dir_offset = 0;
3136
u64 dir_target;
3237
u32 snapshot;
33-
unsigned dir_type;
38+
unsigned dir_type = mode_to_type(mode);
3439
int ret;
3540

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

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

46-
if (!name)
47-
new_inode->bi_flags |= BCH_INODE_UNLINKED;
53+
if (flags & BCH_CREATE_TMPFILE)
54+
new_inode->bi_flags |= BCH_INODE_UNLINKED;
4855

49-
ret = bch2_inode_create(trans, &inode_iter, new_inode, snapshot, cpu);
50-
if (ret)
51-
goto err;
56+
ret = bch2_inode_create(trans, &inode_iter, new_inode, snapshot, cpu);
57+
if (ret)
58+
goto err;
59+
60+
snapshot_src = (subvol_inum) { 0 };
61+
} else {
62+
/*
63+
* Creating a snapshot - we're not allocating a new inode, but
64+
* we do have to lookup the root inode of the subvolume we're
65+
* snapshotting and update it (in the new snapshot):
66+
*/
67+
68+
if (!snapshot_src.inum) {
69+
/* Inode wasn't specified, just snapshot: */
70+
struct btree_iter subvol_iter;
71+
struct bkey_s_c k;
72+
73+
bch2_trans_iter_init(trans, &subvol_iter, BTREE_ID_subvolumes,
74+
POS(0, snapshot_src.subvol), 0);
75+
k = bch2_btree_iter_peek_slot(&subvol_iter);
76+
77+
ret = bkey_err(k);
78+
if (!ret && k.k->type != KEY_TYPE_subvolume) {
79+
bch_err(c, "subvolume %u not found",
80+
snapshot_src.subvol);
81+
ret = -ENOENT;
82+
}
83+
84+
if (!ret)
85+
snapshot_src.inum = le64_to_cpu(bkey_s_c_to_subvolume(k).v->inode);
86+
bch2_trans_iter_exit(trans, &subvol_iter);
87+
88+
if (ret)
89+
goto err;
90+
}
91+
92+
ret = bch2_inode_peek(trans, &inode_iter, new_inode, snapshot_src,
93+
BTREE_ITER_INTENT);
94+
if (ret)
95+
goto err;
96+
97+
if (new_inode->bi_subvol != snapshot_src.subvol) {
98+
/* Not a subvolume root: */
99+
ret = -EINVAL;
100+
goto err;
101+
}
102+
103+
/*
104+
* If we're not root, we have to own the subvolume being
105+
* snapshotted:
106+
*/
107+
if (uid && new_inode->bi_uid != uid) {
108+
ret = -EPERM;
109+
goto err;
110+
}
111+
112+
flags |= BCH_CREATE_SUBVOL;
113+
}
52114

53115
new_inum.inum = new_inode->bi_inum;
54116
dir_target = new_inode->bi_inum;
55-
dir_type = mode_to_type(new_inode->bi_mode);
56117

57-
if (default_acl) {
58-
ret = bch2_set_acl_trans(trans, new_inum, new_inode,
59-
default_acl, ACL_TYPE_DEFAULT);
118+
if (flags & BCH_CREATE_SUBVOL) {
119+
u32 new_subvol, dir_snapshot;
120+
121+
ret = bch2_subvolume_create(trans, new_inode->bi_inum,
122+
snapshot_src.subvol,
123+
&new_subvol, &snapshot,
124+
(flags & BCH_CREATE_SNAPSHOT_RO) != 0);
60125
if (ret)
61126
goto err;
62-
}
63127

64-
if (acl) {
65-
ret = bch2_set_acl_trans(trans, new_inum, new_inode,
66-
acl, ACL_TYPE_ACCESS);
128+
new_inode->bi_parent_subvol = dir.subvol;
129+
new_inode->bi_subvol = new_subvol;
130+
new_inum.subvol = new_subvol;
131+
dir_target = new_subvol;
132+
dir_type = DT_SUBVOL;
133+
134+
ret = bch2_subvolume_get_snapshot(trans, dir.subvol, &dir_snapshot);
135+
if (ret)
136+
goto err;
137+
138+
bch2_btree_iter_set_snapshot(&dir_iter, dir_snapshot);
139+
ret = bch2_btree_iter_traverse(&dir_iter);
67140
if (ret)
68141
goto err;
69142
}
70143

71-
if (name) {
144+
if (!(flags & BCH_CREATE_SNAPSHOT)) {
145+
if (default_acl) {
146+
ret = bch2_set_acl_trans(trans, new_inum, new_inode,
147+
default_acl, ACL_TYPE_DEFAULT);
148+
if (ret)
149+
goto err;
150+
}
151+
152+
if (acl) {
153+
ret = bch2_set_acl_trans(trans, new_inum, new_inode,
154+
acl, ACL_TYPE_ACCESS);
155+
if (ret)
156+
goto err;
157+
}
158+
}
159+
160+
if (!(flags & BCH_CREATE_TMPFILE)) {
72161
struct bch_hash_info dir_hash = bch2_hash_info_init(c, dir_u);
162+
u64 dir_offset;
73163

74-
if (S_ISDIR(new_inode->bi_mode))
164+
if (is_subdir_for_nlink(new_inode))
75165
dir_u->bi_nlink++;
76166
dir_u->bi_mtime = dir_u->bi_ctime = now;
77167

@@ -87,11 +177,11 @@ int bch2_create_trans(struct btree_trans *trans,
87177
BCH_HASH_SET_MUST_CREATE);
88178
if (ret)
89179
goto err;
90-
}
91180

92-
if (c->sb.version >= bcachefs_metadata_version_inode_backpointers) {
93-
new_inode->bi_dir = dir_u->bi_inum;
94-
new_inode->bi_dir_offset = dir_offset;
181+
if (c->sb.version >= bcachefs_metadata_version_inode_backpointers) {
182+
new_inode->bi_dir = dir_u->bi_inum;
183+
new_inode->bi_dir_offset = dir_offset;
184+
}
95185
}
96186

97187
inode_iter.flags &= ~BTREE_ITER_ALL_SNAPSHOTS;
@@ -160,7 +250,8 @@ int bch2_unlink_trans(struct btree_trans *trans,
160250
subvol_inum dir,
161251
struct bch_inode_unpacked *dir_u,
162252
struct bch_inode_unpacked *inode_u,
163-
const struct qstr *name)
253+
const struct qstr *name,
254+
int deleting_snapshot)
164255
{
165256
struct bch_fs *c = trans->c;
166257
struct btree_iter dir_iter = { NULL };
@@ -169,6 +260,7 @@ int bch2_unlink_trans(struct btree_trans *trans,
169260
struct bch_hash_info dir_hash;
170261
subvol_inum inum;
171262
u64 now = bch2_current_time(c);
263+
struct bkey_s_c k;
172264
int ret;
173265

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

190-
if (inode_u->bi_dir == dirent_iter.pos.inode &&
191-
inode_u->bi_dir_offset == dirent_iter.pos.offset) {
192-
inode_u->bi_dir = 0;
193-
inode_u->bi_dir_offset = 0;
282+
if (deleting_snapshot == 1 && !inode_u->bi_subvol) {
283+
ret = -ENOENT;
284+
goto err;
194285
}
195286

196-
if (S_ISDIR(inode_u->bi_mode)) {
287+
if (deleting_snapshot <= 0 && S_ISDIR(inode_u->bi_mode)) {
197288
ret = bch2_empty_dir_trans(trans, inum);
198289
if (ret)
199290
goto err;
200291
}
201292

202-
if (dir.subvol != inum.subvol) {
203-
ret = bch2_subvolume_delete(trans, inum.subvol, false);
293+
if (inode_u->bi_subvol) {
294+
ret = bch2_subvolume_delete(trans, inode_u->bi_subvol,
295+
deleting_snapshot);
296+
if (ret)
297+
goto err;
298+
299+
k = bch2_btree_iter_peek_slot(&dirent_iter);
300+
ret = bkey_err(k);
301+
if (ret)
302+
goto err;
303+
304+
/*
305+
* If we're deleting a subvolume, we need to really delete the
306+
* dirent, not just emit a whiteout in the current snapshot:
307+
*/
308+
bch2_btree_iter_set_snapshot(&dirent_iter, k.k->p.snapshot);
309+
ret = bch2_btree_iter_traverse(&dirent_iter);
204310
if (ret)
205311
goto err;
206312
}
207313

314+
if (inode_u->bi_dir == dirent_iter.pos.inode &&
315+
inode_u->bi_dir_offset == dirent_iter.pos.offset) {
316+
inode_u->bi_dir = 0;
317+
inode_u->bi_dir_offset = 0;
318+
}
319+
208320
dir_u->bi_mtime = dir_u->bi_ctime = inode_u->bi_ctime = now;
209-
dir_u->bi_nlink -= S_ISDIR(inode_u->bi_mode);
321+
dir_u->bi_nlink -= is_subdir_for_nlink(inode_u);
210322
bch2_inode_nlink_dec(inode_u);
211323

212-
ret = bch2_dirent_delete_at(trans, &dir_hash, &dirent_iter) ?:
324+
ret = bch2_hash_delete_at(trans, bch2_dirent_hash_desc,
325+
&dir_hash, &dirent_iter,
326+
BTREE_UPDATE_INTERNAL_SNAPSHOT_NODE) ?:
213327
bch2_inode_write(trans, &dir_iter, dir_u) ?:
214328
bch2_inode_write(trans, &inode_iter, inode_u);
215329
err:
@@ -348,12 +462,12 @@ int bch2_rename_trans(struct btree_trans *trans,
348462
goto err;
349463
}
350464

351-
if (S_ISDIR(src_inode_u->bi_mode)) {
465+
if (is_subdir_for_nlink(src_inode_u)) {
352466
src_dir_u->bi_nlink--;
353467
dst_dir_u->bi_nlink++;
354468
}
355469

356-
if (dst_inum.inum && S_ISDIR(dst_inode_u->bi_mode)) {
470+
if (dst_inum.inum && is_subdir_for_nlink(dst_inode_u)) {
357471
dst_dir_u->bi_nlink--;
358472
src_dir_u->bi_nlink += mode == BCH_RENAME_EXCHANGE;
359473
}

fs/bcachefs/fs-common.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
struct posix_acl;
66

77
#define BCH_CREATE_TMPFILE (1U << 0)
8+
#define BCH_CREATE_SUBVOL (1U << 1)
9+
#define BCH_CREATE_SNAPSHOT (1U << 2)
10+
#define BCH_CREATE_SNAPSHOT_RO (1U << 3)
811

912
int bch2_create_trans(struct btree_trans *, subvol_inum,
1013
struct bch_inode_unpacked *,
@@ -13,7 +16,7 @@ int bch2_create_trans(struct btree_trans *, subvol_inum,
1316
uid_t, gid_t, umode_t, dev_t,
1417
struct posix_acl *,
1518
struct posix_acl *,
16-
unsigned);
19+
subvol_inum, unsigned);
1720

1821
int bch2_link_trans(struct btree_trans *,
1922
subvol_inum, struct bch_inode_unpacked *,
@@ -23,7 +26,7 @@ int bch2_link_trans(struct btree_trans *,
2326
int bch2_unlink_trans(struct btree_trans *, subvol_inum,
2427
struct bch_inode_unpacked *,
2528
struct bch_inode_unpacked *,
26-
const struct qstr *);
29+
const struct qstr *, int);
2730

2831
int bch2_rename_trans(struct btree_trans *,
2932
subvol_inum, struct bch_inode_unpacked *,

0 commit comments

Comments
 (0)