1 | // SPDX-License-Identifier: GPL-2.0 |
2 | |
3 | #include "bcachefs.h" |
4 | #include "acl.h" |
5 | #include "btree_update.h" |
6 | #include "dirent.h" |
7 | #include "fs-common.h" |
8 | #include "inode.h" |
9 | #include "subvolume.h" |
10 | #include "xattr.h" |
11 | |
12 | #include <linux/posix_acl.h> |
13 | |
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 | |
19 | int bch2_create_trans(struct btree_trans *trans, |
20 | subvol_inum dir, |
21 | struct bch_inode_unpacked *dir_u, |
22 | struct bch_inode_unpacked *new_inode, |
23 | const struct qstr *name, |
24 | uid_t uid, gid_t gid, umode_t mode, dev_t rdev, |
25 | struct posix_acl *default_acl, |
26 | struct posix_acl *acl, |
27 | subvol_inum snapshot_src, |
28 | unsigned flags) |
29 | { |
30 | struct bch_fs *c = trans->c; |
31 | struct btree_iter dir_iter = { NULL }; |
32 | struct btree_iter inode_iter = { NULL }; |
33 | subvol_inum new_inum = dir; |
34 | u64 now = bch2_current_time(c); |
35 | u64 cpu = raw_smp_processor_id(); |
36 | u64 dir_target; |
37 | u32 snapshot; |
38 | unsigned dir_type = mode_to_type(mode); |
39 | int ret; |
40 | |
41 | ret = bch2_subvolume_get_snapshot(trans, dir.subvol, &snapshot); |
42 | if (ret) |
43 | goto err; |
44 | |
45 | ret = bch2_inode_peek(trans, &dir_iter, dir_u, dir, BTREE_ITER_INTENT); |
46 | if (ret) |
47 | goto err; |
48 | |
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); |
52 | |
53 | if (flags & BCH_CREATE_TMPFILE) |
54 | new_inode->bi_flags |= BCH_INODE_unlinked; |
55 | |
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 bch_subvolume s; |
71 | |
72 | ret = bch2_subvolume_get(trans, snapshot_src.subvol, true, |
73 | BTREE_ITER_CACHED, &s); |
74 | if (ret) |
75 | goto err; |
76 | |
77 | snapshot_src.inum = le64_to_cpu(s.inode); |
78 | } |
79 | |
80 | ret = bch2_inode_peek(trans, &inode_iter, new_inode, snapshot_src, |
81 | BTREE_ITER_INTENT); |
82 | if (ret) |
83 | goto err; |
84 | |
85 | if (new_inode->bi_subvol != snapshot_src.subvol) { |
86 | /* Not a subvolume root: */ |
87 | ret = -EINVAL; |
88 | goto err; |
89 | } |
90 | |
91 | /* |
92 | * If we're not root, we have to own the subvolume being |
93 | * snapshotted: |
94 | */ |
95 | if (uid && new_inode->bi_uid != uid) { |
96 | ret = -EPERM; |
97 | goto err; |
98 | } |
99 | |
100 | flags |= BCH_CREATE_SUBVOL; |
101 | } |
102 | |
103 | new_inum.inum = new_inode->bi_inum; |
104 | dir_target = new_inode->bi_inum; |
105 | |
106 | if (flags & BCH_CREATE_SUBVOL) { |
107 | u32 new_subvol, dir_snapshot; |
108 | |
109 | ret = bch2_subvolume_create(trans, new_inode->bi_inum, |
110 | dir.subvol, |
111 | snapshot_src.subvol, |
112 | &new_subvol, &snapshot, |
113 | (flags & BCH_CREATE_SNAPSHOT_RO) != 0); |
114 | if (ret) |
115 | goto err; |
116 | |
117 | new_inode->bi_parent_subvol = dir.subvol; |
118 | new_inode->bi_subvol = new_subvol; |
119 | new_inum.subvol = new_subvol; |
120 | dir_target = new_subvol; |
121 | dir_type = DT_SUBVOL; |
122 | |
123 | ret = bch2_subvolume_get_snapshot(trans, dir.subvol, &dir_snapshot); |
124 | if (ret) |
125 | goto err; |
126 | |
127 | bch2_btree_iter_set_snapshot(&dir_iter, dir_snapshot); |
128 | ret = bch2_btree_iter_traverse(&dir_iter); |
129 | if (ret) |
130 | goto err; |
131 | } |
132 | |
133 | if (!(flags & BCH_CREATE_SNAPSHOT)) { |
134 | if (default_acl) { |
135 | ret = bch2_set_acl_trans(trans, new_inum, new_inode, |
136 | default_acl, ACL_TYPE_DEFAULT); |
137 | if (ret) |
138 | goto err; |
139 | } |
140 | |
141 | if (acl) { |
142 | ret = bch2_set_acl_trans(trans, new_inum, new_inode, |
143 | acl, ACL_TYPE_ACCESS); |
144 | if (ret) |
145 | goto err; |
146 | } |
147 | } |
148 | |
149 | if (!(flags & BCH_CREATE_TMPFILE)) { |
150 | struct bch_hash_info dir_hash = bch2_hash_info_init(c, dir_u); |
151 | u64 dir_offset; |
152 | |
153 | if (is_subdir_for_nlink(new_inode)) |
154 | dir_u->bi_nlink++; |
155 | dir_u->bi_mtime = dir_u->bi_ctime = now; |
156 | |
157 | ret = bch2_inode_write(trans, &dir_iter, dir_u); |
158 | if (ret) |
159 | goto err; |
160 | |
161 | ret = bch2_dirent_create(trans, dir, &dir_hash, |
162 | dir_type, |
163 | name, |
164 | dir_target, |
165 | &dir_offset, |
166 | BCH_HASH_SET_MUST_CREATE); |
167 | if (ret) |
168 | goto err; |
169 | |
170 | new_inode->bi_dir = dir_u->bi_inum; |
171 | new_inode->bi_dir_offset = dir_offset; |
172 | } |
173 | |
174 | inode_iter.flags &= ~BTREE_ITER_ALL_SNAPSHOTS; |
175 | bch2_btree_iter_set_snapshot(&inode_iter, snapshot); |
176 | |
177 | ret = bch2_btree_iter_traverse(&inode_iter) ?: |
178 | bch2_inode_write(trans, &inode_iter, new_inode); |
179 | err: |
180 | bch2_trans_iter_exit(trans, &inode_iter); |
181 | bch2_trans_iter_exit(trans, &dir_iter); |
182 | return ret; |
183 | } |
184 | |
185 | int bch2_link_trans(struct btree_trans *trans, |
186 | subvol_inum dir, struct bch_inode_unpacked *dir_u, |
187 | subvol_inum inum, struct bch_inode_unpacked *inode_u, |
188 | const struct qstr *name) |
189 | { |
190 | struct bch_fs *c = trans->c; |
191 | struct btree_iter dir_iter = { NULL }; |
192 | struct btree_iter inode_iter = { NULL }; |
193 | struct bch_hash_info dir_hash; |
194 | u64 now = bch2_current_time(c); |
195 | u64 dir_offset = 0; |
196 | int ret; |
197 | |
198 | if (dir.subvol != inum.subvol) |
199 | return -EXDEV; |
200 | |
201 | ret = bch2_inode_peek(trans, &inode_iter, inode_u, inum, BTREE_ITER_INTENT); |
202 | if (ret) |
203 | goto err; |
204 | |
205 | inode_u->bi_ctime = now; |
206 | ret = bch2_inode_nlink_inc(inode_u); |
207 | if (ret) |
208 | return ret; |
209 | |
210 | ret = bch2_inode_peek(trans, &dir_iter, dir_u, dir, BTREE_ITER_INTENT); |
211 | if (ret) |
212 | goto err; |
213 | |
214 | if (bch2_reinherit_attrs(inode_u, dir_u)) { |
215 | ret = -EXDEV; |
216 | goto err; |
217 | } |
218 | |
219 | dir_u->bi_mtime = dir_u->bi_ctime = now; |
220 | |
221 | dir_hash = bch2_hash_info_init(c, bi: dir_u); |
222 | |
223 | ret = bch2_dirent_create(trans, dir, &dir_hash, |
224 | mode_to_type(mode: inode_u->bi_mode), |
225 | name, inum.inum, &dir_offset, |
226 | BCH_HASH_SET_MUST_CREATE); |
227 | if (ret) |
228 | goto err; |
229 | |
230 | inode_u->bi_dir = dir.inum; |
231 | inode_u->bi_dir_offset = dir_offset; |
232 | |
233 | ret = bch2_inode_write(trans, iter: &dir_iter, inode: dir_u) ?: |
234 | bch2_inode_write(trans, iter: &inode_iter, inode: inode_u); |
235 | err: |
236 | bch2_trans_iter_exit(trans, &dir_iter); |
237 | bch2_trans_iter_exit(trans, &inode_iter); |
238 | return ret; |
239 | } |
240 | |
241 | int bch2_unlink_trans(struct btree_trans *trans, |
242 | subvol_inum dir, |
243 | struct bch_inode_unpacked *dir_u, |
244 | struct bch_inode_unpacked *inode_u, |
245 | const struct qstr *name, |
246 | bool deleting_subvol) |
247 | { |
248 | struct bch_fs *c = trans->c; |
249 | struct btree_iter dir_iter = { NULL }; |
250 | struct btree_iter dirent_iter = { NULL }; |
251 | struct btree_iter inode_iter = { NULL }; |
252 | struct bch_hash_info dir_hash; |
253 | subvol_inum inum; |
254 | u64 now = bch2_current_time(c); |
255 | struct bkey_s_c k; |
256 | int ret; |
257 | |
258 | ret = bch2_inode_peek(trans, &dir_iter, dir_u, dir, BTREE_ITER_INTENT); |
259 | if (ret) |
260 | goto err; |
261 | |
262 | dir_hash = bch2_hash_info_init(c, bi: dir_u); |
263 | |
264 | ret = bch2_dirent_lookup_trans(trans, &dirent_iter, dir, &dir_hash, |
265 | name, &inum, BTREE_ITER_INTENT); |
266 | if (ret) |
267 | goto err; |
268 | |
269 | ret = bch2_inode_peek(trans, &inode_iter, inode_u, inum, |
270 | BTREE_ITER_INTENT); |
271 | if (ret) |
272 | goto err; |
273 | |
274 | if (!deleting_subvol && S_ISDIR(inode_u->bi_mode)) { |
275 | ret = bch2_empty_dir_trans(trans, inum); |
276 | if (ret) |
277 | goto err; |
278 | } |
279 | |
280 | if (deleting_subvol && !inode_u->bi_subvol) { |
281 | ret = -BCH_ERR_ENOENT_not_subvol; |
282 | goto err; |
283 | } |
284 | |
285 | if (inode_u->bi_subvol) { |
286 | /* Recursive subvolume destroy not allowed (yet?) */ |
287 | ret = bch2_subvol_has_children(trans, inode_u->bi_subvol); |
288 | if (ret) |
289 | goto err; |
290 | } |
291 | |
292 | if (deleting_subvol || inode_u->bi_subvol) { |
293 | ret = bch2_subvolume_unlink(trans, inode_u->bi_subvol); |
294 | if (ret) |
295 | goto err; |
296 | |
297 | k = bch2_btree_iter_peek_slot(&dirent_iter); |
298 | ret = bkey_err(k); |
299 | if (ret) |
300 | goto err; |
301 | |
302 | /* |
303 | * If we're deleting a subvolume, we need to really delete the |
304 | * dirent, not just emit a whiteout in the current snapshot: |
305 | */ |
306 | bch2_btree_iter_set_snapshot(iter: &dirent_iter, snapshot: k.k->p.snapshot); |
307 | ret = bch2_btree_iter_traverse(&dirent_iter); |
308 | if (ret) |
309 | goto err; |
310 | } else { |
311 | bch2_inode_nlink_dec(trans, inode_u); |
312 | } |
313 | |
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 | |
320 | dir_u->bi_mtime = dir_u->bi_ctime = inode_u->bi_ctime = now; |
321 | dir_u->bi_nlink -= is_subdir_for_nlink(inode: inode_u); |
322 | |
323 | ret = bch2_hash_delete_at(trans, desc: bch2_dirent_hash_desc, |
324 | info: &dir_hash, iter: &dirent_iter, |
325 | BTREE_UPDATE_INTERNAL_SNAPSHOT_NODE) ?: |
326 | bch2_inode_write(trans, iter: &dir_iter, inode: dir_u) ?: |
327 | bch2_inode_write(trans, iter: &inode_iter, inode: inode_u); |
328 | err: |
329 | bch2_trans_iter_exit(trans, &inode_iter); |
330 | bch2_trans_iter_exit(trans, &dirent_iter); |
331 | bch2_trans_iter_exit(trans, &dir_iter); |
332 | return ret; |
333 | } |
334 | |
335 | bool bch2_reinherit_attrs(struct bch_inode_unpacked *dst_u, |
336 | struct bch_inode_unpacked *src_u) |
337 | { |
338 | u64 src, dst; |
339 | unsigned id; |
340 | bool ret = false; |
341 | |
342 | for (id = 0; id < Inode_opt_nr; id++) { |
343 | /* Skip attributes that were explicitly set on this inode */ |
344 | if (dst_u->bi_fields_set & (1 << id)) |
345 | continue; |
346 | |
347 | src = bch2_inode_opt_get(inode: src_u, id); |
348 | dst = bch2_inode_opt_get(inode: dst_u, id); |
349 | |
350 | if (src == dst) |
351 | continue; |
352 | |
353 | bch2_inode_opt_set(inode: dst_u, id, v: src); |
354 | ret = true; |
355 | } |
356 | |
357 | return ret; |
358 | } |
359 | |
360 | static int subvol_update_parent(struct btree_trans *trans, u32 subvol, u32 new_parent) |
361 | { |
362 | struct btree_iter iter; |
363 | struct bkey_i_subvolume *s = |
364 | bch2_bkey_get_mut_typed(trans, &iter, |
365 | BTREE_ID_subvolumes, POS(0, subvol), |
366 | BTREE_ITER_CACHED, subvolume); |
367 | int ret = PTR_ERR_OR_ZERO(ptr: s); |
368 | if (ret) |
369 | return ret; |
370 | |
371 | s->v.fs_path_parent = cpu_to_le32(new_parent); |
372 | bch2_trans_iter_exit(trans, &iter); |
373 | return 0; |
374 | } |
375 | |
376 | int bch2_rename_trans(struct btree_trans *trans, |
377 | subvol_inum src_dir, struct bch_inode_unpacked *src_dir_u, |
378 | subvol_inum dst_dir, struct bch_inode_unpacked *dst_dir_u, |
379 | struct bch_inode_unpacked *src_inode_u, |
380 | struct bch_inode_unpacked *dst_inode_u, |
381 | const struct qstr *src_name, |
382 | const struct qstr *dst_name, |
383 | enum bch_rename_mode mode) |
384 | { |
385 | struct bch_fs *c = trans->c; |
386 | struct btree_iter src_dir_iter = { NULL }; |
387 | struct btree_iter dst_dir_iter = { NULL }; |
388 | struct btree_iter src_inode_iter = { NULL }; |
389 | struct btree_iter dst_inode_iter = { NULL }; |
390 | struct bch_hash_info src_hash, dst_hash; |
391 | subvol_inum src_inum, dst_inum; |
392 | u64 src_offset, dst_offset; |
393 | u64 now = bch2_current_time(c); |
394 | int ret; |
395 | |
396 | ret = bch2_inode_peek(trans, &src_dir_iter, src_dir_u, src_dir, |
397 | BTREE_ITER_INTENT); |
398 | if (ret) |
399 | goto err; |
400 | |
401 | src_hash = bch2_hash_info_init(c, bi: src_dir_u); |
402 | |
403 | if (dst_dir.inum != src_dir.inum || |
404 | dst_dir.subvol != src_dir.subvol) { |
405 | ret = bch2_inode_peek(trans, &dst_dir_iter, dst_dir_u, dst_dir, |
406 | BTREE_ITER_INTENT); |
407 | if (ret) |
408 | goto err; |
409 | |
410 | dst_hash = bch2_hash_info_init(c, bi: dst_dir_u); |
411 | } else { |
412 | dst_dir_u = src_dir_u; |
413 | dst_hash = src_hash; |
414 | } |
415 | |
416 | ret = bch2_dirent_rename(trans, |
417 | src_dir, &src_hash, |
418 | dst_dir, &dst_hash, |
419 | src_name, &src_inum, &src_offset, |
420 | dst_name, &dst_inum, &dst_offset, |
421 | mode); |
422 | if (ret) |
423 | goto err; |
424 | |
425 | ret = bch2_inode_peek(trans, &src_inode_iter, src_inode_u, src_inum, |
426 | BTREE_ITER_INTENT); |
427 | if (ret) |
428 | goto err; |
429 | |
430 | if (dst_inum.inum) { |
431 | ret = bch2_inode_peek(trans, &dst_inode_iter, dst_inode_u, dst_inum, |
432 | BTREE_ITER_INTENT); |
433 | if (ret) |
434 | goto err; |
435 | } |
436 | |
437 | if (src_inode_u->bi_subvol && |
438 | dst_dir.subvol != src_inode_u->bi_parent_subvol) { |
439 | ret = subvol_update_parent(trans, subvol: src_inode_u->bi_subvol, new_parent: dst_dir.subvol); |
440 | if (ret) |
441 | goto err; |
442 | } |
443 | |
444 | if (mode == BCH_RENAME_EXCHANGE && |
445 | dst_inode_u->bi_subvol && |
446 | src_dir.subvol != dst_inode_u->bi_parent_subvol) { |
447 | ret = subvol_update_parent(trans, subvol: dst_inode_u->bi_subvol, new_parent: src_dir.subvol); |
448 | if (ret) |
449 | goto err; |
450 | } |
451 | |
452 | /* Can't move across subvolumes, unless it's a subvolume root: */ |
453 | if (src_dir.subvol != dst_dir.subvol && |
454 | (!src_inode_u->bi_subvol || |
455 | (dst_inum.inum && !dst_inode_u->bi_subvol))) { |
456 | ret = -EXDEV; |
457 | goto err; |
458 | } |
459 | |
460 | if (src_inode_u->bi_parent_subvol) |
461 | src_inode_u->bi_parent_subvol = dst_dir.subvol; |
462 | |
463 | if ((mode == BCH_RENAME_EXCHANGE) && |
464 | dst_inode_u->bi_parent_subvol) |
465 | dst_inode_u->bi_parent_subvol = src_dir.subvol; |
466 | |
467 | src_inode_u->bi_dir = dst_dir_u->bi_inum; |
468 | src_inode_u->bi_dir_offset = dst_offset; |
469 | |
470 | if (mode == BCH_RENAME_EXCHANGE) { |
471 | dst_inode_u->bi_dir = src_dir_u->bi_inum; |
472 | dst_inode_u->bi_dir_offset = src_offset; |
473 | } |
474 | |
475 | if (mode == BCH_RENAME_OVERWRITE && |
476 | dst_inode_u->bi_dir == dst_dir_u->bi_inum && |
477 | dst_inode_u->bi_dir_offset == src_offset) { |
478 | dst_inode_u->bi_dir = 0; |
479 | dst_inode_u->bi_dir_offset = 0; |
480 | } |
481 | |
482 | if (mode == BCH_RENAME_OVERWRITE) { |
483 | if (S_ISDIR(src_inode_u->bi_mode) != |
484 | S_ISDIR(dst_inode_u->bi_mode)) { |
485 | ret = -ENOTDIR; |
486 | goto err; |
487 | } |
488 | |
489 | if (S_ISDIR(dst_inode_u->bi_mode)) { |
490 | ret = bch2_empty_dir_trans(trans, dst_inum); |
491 | if (ret) |
492 | goto err; |
493 | } |
494 | } |
495 | |
496 | if (bch2_reinherit_attrs(dst_u: src_inode_u, src_u: dst_dir_u) && |
497 | S_ISDIR(src_inode_u->bi_mode)) { |
498 | ret = -EXDEV; |
499 | goto err; |
500 | } |
501 | |
502 | if (mode == BCH_RENAME_EXCHANGE && |
503 | bch2_reinherit_attrs(dst_u: dst_inode_u, src_u: src_dir_u) && |
504 | S_ISDIR(dst_inode_u->bi_mode)) { |
505 | ret = -EXDEV; |
506 | goto err; |
507 | } |
508 | |
509 | if (is_subdir_for_nlink(inode: src_inode_u)) { |
510 | src_dir_u->bi_nlink--; |
511 | dst_dir_u->bi_nlink++; |
512 | } |
513 | |
514 | if (dst_inum.inum && is_subdir_for_nlink(inode: dst_inode_u)) { |
515 | dst_dir_u->bi_nlink--; |
516 | src_dir_u->bi_nlink += mode == BCH_RENAME_EXCHANGE; |
517 | } |
518 | |
519 | if (mode == BCH_RENAME_OVERWRITE) |
520 | bch2_inode_nlink_dec(trans, dst_inode_u); |
521 | |
522 | src_dir_u->bi_mtime = now; |
523 | src_dir_u->bi_ctime = now; |
524 | |
525 | if (src_dir.inum != dst_dir.inum) { |
526 | dst_dir_u->bi_mtime = now; |
527 | dst_dir_u->bi_ctime = now; |
528 | } |
529 | |
530 | src_inode_u->bi_ctime = now; |
531 | |
532 | if (dst_inum.inum) |
533 | dst_inode_u->bi_ctime = now; |
534 | |
535 | ret = bch2_inode_write(trans, iter: &src_dir_iter, inode: src_dir_u) ?: |
536 | (src_dir.inum != dst_dir.inum |
537 | ? bch2_inode_write(trans, iter: &dst_dir_iter, inode: dst_dir_u) |
538 | : 0) ?: |
539 | bch2_inode_write(trans, iter: &src_inode_iter, inode: src_inode_u) ?: |
540 | (dst_inum.inum |
541 | ? bch2_inode_write(trans, iter: &dst_inode_iter, inode: dst_inode_u) |
542 | : 0); |
543 | err: |
544 | bch2_trans_iter_exit(trans, &dst_inode_iter); |
545 | bch2_trans_iter_exit(trans, &src_inode_iter); |
546 | bch2_trans_iter_exit(trans, &dst_dir_iter); |
547 | bch2_trans_iter_exit(trans, &src_dir_iter); |
548 | return ret; |
549 | } |
550 | |