14fcd4de0SKent Overstreet // SPDX-License-Identifier: GPL-2.0
24fcd4de0SKent Overstreet
34fcd4de0SKent Overstreet #include "bcachefs.h"
44fcd4de0SKent Overstreet #include "acl.h"
54fcd4de0SKent Overstreet #include "btree_update.h"
64fcd4de0SKent Overstreet #include "dirent.h"
74fcd4de0SKent Overstreet #include "inode.h"
84fcd4de0SKent Overstreet #include "namei.h"
94fcd4de0SKent Overstreet #include "subvolume.h"
104fcd4de0SKent Overstreet #include "xattr.h"
114fcd4de0SKent Overstreet
124fcd4de0SKent Overstreet #include <linux/posix_acl.h>
134fcd4de0SKent Overstreet
is_subdir_for_nlink(struct bch_inode_unpacked * inode)144fcd4de0SKent Overstreet static inline int is_subdir_for_nlink(struct bch_inode_unpacked *inode)
154fcd4de0SKent Overstreet {
164fcd4de0SKent Overstreet return S_ISDIR(inode->bi_mode) && !inode->bi_subvol;
174fcd4de0SKent Overstreet }
184fcd4de0SKent Overstreet
bch2_create_trans(struct btree_trans * trans,subvol_inum dir,struct bch_inode_unpacked * dir_u,struct bch_inode_unpacked * new_inode,const struct qstr * name,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)194fcd4de0SKent Overstreet int bch2_create_trans(struct btree_trans *trans,
204fcd4de0SKent Overstreet subvol_inum dir,
214fcd4de0SKent Overstreet struct bch_inode_unpacked *dir_u,
224fcd4de0SKent Overstreet struct bch_inode_unpacked *new_inode,
234fcd4de0SKent Overstreet const struct qstr *name,
244fcd4de0SKent Overstreet uid_t uid, gid_t gid, umode_t mode, dev_t rdev,
254fcd4de0SKent Overstreet struct posix_acl *default_acl,
264fcd4de0SKent Overstreet struct posix_acl *acl,
274fcd4de0SKent Overstreet subvol_inum snapshot_src,
284fcd4de0SKent Overstreet unsigned flags)
294fcd4de0SKent Overstreet {
304fcd4de0SKent Overstreet struct bch_fs *c = trans->c;
314fcd4de0SKent Overstreet struct btree_iter dir_iter = { NULL };
324fcd4de0SKent Overstreet struct btree_iter inode_iter = { NULL };
334fcd4de0SKent Overstreet subvol_inum new_inum = dir;
344fcd4de0SKent Overstreet u64 now = bch2_current_time(c);
354fcd4de0SKent Overstreet u64 cpu = raw_smp_processor_id();
364fcd4de0SKent Overstreet u64 dir_target;
374fcd4de0SKent Overstreet u32 snapshot;
384fcd4de0SKent Overstreet unsigned dir_type = mode_to_type(mode);
394fcd4de0SKent Overstreet int ret;
404fcd4de0SKent Overstreet
414fcd4de0SKent Overstreet ret = bch2_subvolume_get_snapshot(trans, dir.subvol, &snapshot);
424fcd4de0SKent Overstreet if (ret)
434fcd4de0SKent Overstreet goto err;
444fcd4de0SKent Overstreet
454fcd4de0SKent Overstreet ret = bch2_inode_peek(trans, &dir_iter, dir_u, dir,
464fcd4de0SKent Overstreet BTREE_ITER_intent|BTREE_ITER_with_updates);
474fcd4de0SKent Overstreet if (ret)
484fcd4de0SKent Overstreet goto err;
494fcd4de0SKent Overstreet
504fcd4de0SKent Overstreet /* Inherit casefold state from parent. */
514fcd4de0SKent Overstreet if (S_ISDIR(mode))
524fcd4de0SKent Overstreet new_inode->bi_flags |= dir_u->bi_flags & BCH_INODE_casefolded;
534fcd4de0SKent Overstreet
544fcd4de0SKent Overstreet if (!(flags & BCH_CREATE_SNAPSHOT)) {
554fcd4de0SKent Overstreet /* Normal create path - allocate a new inode: */
564fcd4de0SKent Overstreet bch2_inode_init_late(new_inode, now, uid, gid, mode, rdev, dir_u);
574fcd4de0SKent Overstreet
584fcd4de0SKent Overstreet if (flags & BCH_CREATE_TMPFILE)
594fcd4de0SKent Overstreet new_inode->bi_flags |= BCH_INODE_unlinked;
604fcd4de0SKent Overstreet
614fcd4de0SKent Overstreet ret = bch2_inode_create(trans, &inode_iter, new_inode, snapshot, cpu);
624fcd4de0SKent Overstreet if (ret)
634fcd4de0SKent Overstreet goto err;
644fcd4de0SKent Overstreet
654fcd4de0SKent Overstreet snapshot_src = (subvol_inum) { 0 };
664fcd4de0SKent Overstreet } else {
674fcd4de0SKent Overstreet /*
684fcd4de0SKent Overstreet * Creating a snapshot - we're not allocating a new inode, but
694fcd4de0SKent Overstreet * we do have to lookup the root inode of the subvolume we're
704fcd4de0SKent Overstreet * snapshotting and update it (in the new snapshot):
714fcd4de0SKent Overstreet */
724fcd4de0SKent Overstreet
734fcd4de0SKent Overstreet if (!snapshot_src.inum) {
744fcd4de0SKent Overstreet /* Inode wasn't specified, just snapshot: */
754fcd4de0SKent Overstreet struct bch_subvolume s;
764fcd4de0SKent Overstreet ret = bch2_subvolume_get(trans, snapshot_src.subvol, true, &s);
774fcd4de0SKent Overstreet if (ret)
784fcd4de0SKent Overstreet goto err;
794fcd4de0SKent Overstreet
804fcd4de0SKent Overstreet snapshot_src.inum = le64_to_cpu(s.inode);
814fcd4de0SKent Overstreet }
824fcd4de0SKent Overstreet
834fcd4de0SKent Overstreet ret = bch2_inode_peek(trans, &inode_iter, new_inode, snapshot_src,
844fcd4de0SKent Overstreet BTREE_ITER_intent);
854fcd4de0SKent Overstreet if (ret)
864fcd4de0SKent Overstreet goto err;
874fcd4de0SKent Overstreet
884fcd4de0SKent Overstreet if (new_inode->bi_subvol != snapshot_src.subvol) {
894fcd4de0SKent Overstreet /* Not a subvolume root: */
904fcd4de0SKent Overstreet ret = -EINVAL;
914fcd4de0SKent Overstreet goto err;
924fcd4de0SKent Overstreet }
934fcd4de0SKent Overstreet
944fcd4de0SKent Overstreet /*
954fcd4de0SKent Overstreet * If we're not root, we have to own the subvolume being
964fcd4de0SKent Overstreet * snapshotted:
974fcd4de0SKent Overstreet */
984fcd4de0SKent Overstreet if (uid && new_inode->bi_uid != uid) {
994fcd4de0SKent Overstreet ret = -EPERM;
1004fcd4de0SKent Overstreet goto err;
1014fcd4de0SKent Overstreet }
1024fcd4de0SKent Overstreet
1034fcd4de0SKent Overstreet flags |= BCH_CREATE_SUBVOL;
1044fcd4de0SKent Overstreet }
1054fcd4de0SKent Overstreet
1064fcd4de0SKent Overstreet new_inum.inum = new_inode->bi_inum;
1074fcd4de0SKent Overstreet dir_target = new_inode->bi_inum;
1084fcd4de0SKent Overstreet
1094fcd4de0SKent Overstreet if (flags & BCH_CREATE_SUBVOL) {
1104fcd4de0SKent Overstreet u32 new_subvol, dir_snapshot;
1114fcd4de0SKent Overstreet
1124fcd4de0SKent Overstreet ret = bch2_subvolume_create(trans, new_inode->bi_inum,
1134fcd4de0SKent Overstreet dir.subvol,
1144fcd4de0SKent Overstreet snapshot_src.subvol,
1154fcd4de0SKent Overstreet &new_subvol, &snapshot,
1164fcd4de0SKent Overstreet (flags & BCH_CREATE_SNAPSHOT_RO) != 0);
1174fcd4de0SKent Overstreet if (ret)
1184fcd4de0SKent Overstreet goto err;
1194fcd4de0SKent Overstreet
1204fcd4de0SKent Overstreet new_inode->bi_parent_subvol = dir.subvol;
1214fcd4de0SKent Overstreet new_inode->bi_subvol = new_subvol;
1224fcd4de0SKent Overstreet new_inum.subvol = new_subvol;
1234fcd4de0SKent Overstreet dir_target = new_subvol;
1244fcd4de0SKent Overstreet dir_type = DT_SUBVOL;
1254fcd4de0SKent Overstreet
1264fcd4de0SKent Overstreet ret = bch2_subvolume_get_snapshot(trans, dir.subvol, &dir_snapshot);
1274fcd4de0SKent Overstreet if (ret)
1284fcd4de0SKent Overstreet goto err;
1294fcd4de0SKent Overstreet
1304fcd4de0SKent Overstreet bch2_btree_iter_set_snapshot(&dir_iter, dir_snapshot);
1314fcd4de0SKent Overstreet ret = bch2_btree_iter_traverse(&dir_iter);
1324fcd4de0SKent Overstreet if (ret)
1334fcd4de0SKent Overstreet goto err;
1344fcd4de0SKent Overstreet }
1354fcd4de0SKent Overstreet
1364fcd4de0SKent Overstreet if (!(flags & BCH_CREATE_SNAPSHOT)) {
1374fcd4de0SKent Overstreet if (default_acl) {
1384fcd4de0SKent Overstreet ret = bch2_set_acl_trans(trans, new_inum, new_inode,
1394fcd4de0SKent Overstreet default_acl, ACL_TYPE_DEFAULT);
1404fcd4de0SKent Overstreet if (ret)
1414fcd4de0SKent Overstreet goto err;
1424fcd4de0SKent Overstreet }
1434fcd4de0SKent Overstreet
1444fcd4de0SKent Overstreet if (acl) {
1454fcd4de0SKent Overstreet ret = bch2_set_acl_trans(trans, new_inum, new_inode,
1464fcd4de0SKent Overstreet acl, ACL_TYPE_ACCESS);
1474fcd4de0SKent Overstreet if (ret)
1484fcd4de0SKent Overstreet goto err;
1494fcd4de0SKent Overstreet }
1504fcd4de0SKent Overstreet }
1514fcd4de0SKent Overstreet
1524fcd4de0SKent Overstreet if (!(flags & BCH_CREATE_TMPFILE)) {
1534fcd4de0SKent Overstreet struct bch_hash_info dir_hash = bch2_hash_info_init(c, dir_u);
1544fcd4de0SKent Overstreet u64 dir_offset;
1554fcd4de0SKent Overstreet
1564fcd4de0SKent Overstreet if (is_subdir_for_nlink(new_inode))
1574fcd4de0SKent Overstreet dir_u->bi_nlink++;
1584fcd4de0SKent Overstreet dir_u->bi_mtime = dir_u->bi_ctime = now;
1594fcd4de0SKent Overstreet
1604fcd4de0SKent Overstreet ret = bch2_dirent_create(trans, dir, &dir_hash,
1614fcd4de0SKent Overstreet dir_type,
1624fcd4de0SKent Overstreet name,
1634fcd4de0SKent Overstreet dir_target,
1644fcd4de0SKent Overstreet &dir_offset,
1654fcd4de0SKent Overstreet &dir_u->bi_size,
1664fcd4de0SKent Overstreet STR_HASH_must_create|BTREE_ITER_with_updates) ?:
1674fcd4de0SKent Overstreet bch2_inode_write(trans, &dir_iter, dir_u);
1684fcd4de0SKent Overstreet if (ret)
1694fcd4de0SKent Overstreet goto err;
1704fcd4de0SKent Overstreet
1714fcd4de0SKent Overstreet new_inode->bi_dir = dir_u->bi_inum;
1724fcd4de0SKent Overstreet new_inode->bi_dir_offset = dir_offset;
1734fcd4de0SKent Overstreet }
1744fcd4de0SKent Overstreet
1754fcd4de0SKent Overstreet if (S_ISDIR(mode) &&
1764fcd4de0SKent Overstreet !new_inode->bi_subvol)
1774fcd4de0SKent Overstreet new_inode->bi_depth = dir_u->bi_depth + 1;
1784fcd4de0SKent Overstreet
1794fcd4de0SKent Overstreet inode_iter.flags &= ~BTREE_ITER_all_snapshots;
1804fcd4de0SKent Overstreet bch2_btree_iter_set_snapshot(&inode_iter, snapshot);
1814fcd4de0SKent Overstreet
1824fcd4de0SKent Overstreet ret = bch2_btree_iter_traverse(&inode_iter) ?:
1834fcd4de0SKent Overstreet bch2_inode_write(trans, &inode_iter, new_inode);
1844fcd4de0SKent Overstreet err:
1854fcd4de0SKent Overstreet bch2_trans_iter_exit(trans, &inode_iter);
1864fcd4de0SKent Overstreet bch2_trans_iter_exit(trans, &dir_iter);
1874fcd4de0SKent Overstreet return ret;
1884fcd4de0SKent Overstreet }
1894fcd4de0SKent Overstreet
bch2_link_trans(struct btree_trans * trans,subvol_inum dir,struct bch_inode_unpacked * dir_u,subvol_inum inum,struct bch_inode_unpacked * inode_u,const struct qstr * name)1904fcd4de0SKent Overstreet int bch2_link_trans(struct btree_trans *trans,
1914fcd4de0SKent Overstreet subvol_inum dir, struct bch_inode_unpacked *dir_u,
1924fcd4de0SKent Overstreet subvol_inum inum, struct bch_inode_unpacked *inode_u,
1934fcd4de0SKent Overstreet const struct qstr *name)
1944fcd4de0SKent Overstreet {
1954fcd4de0SKent Overstreet struct bch_fs *c = trans->c;
1964fcd4de0SKent Overstreet struct btree_iter dir_iter = { NULL };
1974fcd4de0SKent Overstreet struct btree_iter inode_iter = { NULL };
1984fcd4de0SKent Overstreet struct bch_hash_info dir_hash;
1994fcd4de0SKent Overstreet u64 now = bch2_current_time(c);
2004fcd4de0SKent Overstreet u64 dir_offset = 0;
2014fcd4de0SKent Overstreet int ret;
2024fcd4de0SKent Overstreet
2034fcd4de0SKent Overstreet if (dir.subvol != inum.subvol)
2044fcd4de0SKent Overstreet return -EXDEV;
2054fcd4de0SKent Overstreet
2064fcd4de0SKent Overstreet ret = bch2_inode_peek(trans, &inode_iter, inode_u, inum, BTREE_ITER_intent);
2074fcd4de0SKent Overstreet if (ret)
2084fcd4de0SKent Overstreet return ret;
2094fcd4de0SKent Overstreet
2104fcd4de0SKent Overstreet inode_u->bi_ctime = now;
2114fcd4de0SKent Overstreet ret = bch2_inode_nlink_inc(inode_u);
2124fcd4de0SKent Overstreet if (ret)
2134fcd4de0SKent Overstreet goto err;
2144fcd4de0SKent Overstreet
2154fcd4de0SKent Overstreet ret = bch2_inode_peek(trans, &dir_iter, dir_u, dir, BTREE_ITER_intent);
2164fcd4de0SKent Overstreet if (ret)
2174fcd4de0SKent Overstreet goto err;
2184fcd4de0SKent Overstreet
2194fcd4de0SKent Overstreet if (bch2_reinherit_attrs(inode_u, dir_u)) {
2204fcd4de0SKent Overstreet ret = -EXDEV;
2214fcd4de0SKent Overstreet goto err;
2224fcd4de0SKent Overstreet }
2234fcd4de0SKent Overstreet
2244fcd4de0SKent Overstreet dir_u->bi_mtime = dir_u->bi_ctime = now;
2254fcd4de0SKent Overstreet
2264fcd4de0SKent Overstreet dir_hash = bch2_hash_info_init(c, dir_u);
2274fcd4de0SKent Overstreet
2284fcd4de0SKent Overstreet ret = bch2_dirent_create(trans, dir, &dir_hash,
2294fcd4de0SKent Overstreet mode_to_type(inode_u->bi_mode),
2304fcd4de0SKent Overstreet name, inum.inum,
2314fcd4de0SKent Overstreet &dir_offset,
2324fcd4de0SKent Overstreet &dir_u->bi_size,
2334fcd4de0SKent Overstreet STR_HASH_must_create);
2344fcd4de0SKent Overstreet if (ret)
2354fcd4de0SKent Overstreet goto err;
2364fcd4de0SKent Overstreet
2374fcd4de0SKent Overstreet inode_u->bi_dir = dir.inum;
2384fcd4de0SKent Overstreet inode_u->bi_dir_offset = dir_offset;
2394fcd4de0SKent Overstreet
2404fcd4de0SKent Overstreet ret = bch2_inode_write(trans, &dir_iter, dir_u) ?:
2414fcd4de0SKent Overstreet bch2_inode_write(trans, &inode_iter, inode_u);
2424fcd4de0SKent Overstreet err:
2434fcd4de0SKent Overstreet bch2_trans_iter_exit(trans, &dir_iter);
2444fcd4de0SKent Overstreet bch2_trans_iter_exit(trans, &inode_iter);
2454fcd4de0SKent Overstreet return ret;
2464fcd4de0SKent Overstreet }
2474fcd4de0SKent Overstreet
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,bool deleting_subvol)2484fcd4de0SKent Overstreet int bch2_unlink_trans(struct btree_trans *trans,
2494fcd4de0SKent Overstreet subvol_inum dir,
2504fcd4de0SKent Overstreet struct bch_inode_unpacked *dir_u,
2514fcd4de0SKent Overstreet struct bch_inode_unpacked *inode_u,
2524fcd4de0SKent Overstreet const struct qstr *name,
2534fcd4de0SKent Overstreet bool deleting_subvol)
2544fcd4de0SKent Overstreet {
2554fcd4de0SKent Overstreet struct bch_fs *c = trans->c;
2564fcd4de0SKent Overstreet struct btree_iter dir_iter = { NULL };
2574fcd4de0SKent Overstreet struct btree_iter dirent_iter = { NULL };
2584fcd4de0SKent Overstreet struct btree_iter inode_iter = { NULL };
2594fcd4de0SKent Overstreet struct bch_hash_info dir_hash;
2604fcd4de0SKent Overstreet subvol_inum inum;
2614fcd4de0SKent Overstreet u64 now = bch2_current_time(c);
2624fcd4de0SKent Overstreet struct bkey_s_c k;
2634fcd4de0SKent Overstreet int ret;
2644fcd4de0SKent Overstreet
2654fcd4de0SKent Overstreet ret = bch2_inode_peek(trans, &dir_iter, dir_u, dir, BTREE_ITER_intent);
2664fcd4de0SKent Overstreet if (ret)
2674fcd4de0SKent Overstreet goto err;
2684fcd4de0SKent Overstreet
2694fcd4de0SKent Overstreet dir_hash = bch2_hash_info_init(c, dir_u);
2704fcd4de0SKent Overstreet
2714fcd4de0SKent Overstreet ret = bch2_dirent_lookup_trans(trans, &dirent_iter, dir, &dir_hash,
2724fcd4de0SKent Overstreet name, &inum, BTREE_ITER_intent);
2734fcd4de0SKent Overstreet if (ret)
2744fcd4de0SKent Overstreet goto err;
2754fcd4de0SKent Overstreet
2764fcd4de0SKent Overstreet ret = bch2_inode_peek(trans, &inode_iter, inode_u, inum,
2774fcd4de0SKent Overstreet BTREE_ITER_intent);
2784fcd4de0SKent Overstreet if (ret)
2794fcd4de0SKent Overstreet goto err;
2804fcd4de0SKent Overstreet
2814fcd4de0SKent Overstreet if (!deleting_subvol && S_ISDIR(inode_u->bi_mode)) {
2824fcd4de0SKent Overstreet ret = bch2_empty_dir_trans(trans, inum);
2834fcd4de0SKent Overstreet if (ret)
2844fcd4de0SKent Overstreet goto err;
2854fcd4de0SKent Overstreet }
2864fcd4de0SKent Overstreet
2874fcd4de0SKent Overstreet if (deleting_subvol && !inode_u->bi_subvol) {
2884fcd4de0SKent Overstreet ret = -BCH_ERR_ENOENT_not_subvol;
2894fcd4de0SKent Overstreet goto err;
2904fcd4de0SKent Overstreet }
2914fcd4de0SKent Overstreet
2924fcd4de0SKent Overstreet if (inode_u->bi_subvol) {
2934fcd4de0SKent Overstreet /* Recursive subvolume destroy not allowed (yet?) */
2944fcd4de0SKent Overstreet ret = bch2_subvol_has_children(trans, inode_u->bi_subvol);
2954fcd4de0SKent Overstreet if (ret)
2964fcd4de0SKent Overstreet goto err;
2974fcd4de0SKent Overstreet }
2984fcd4de0SKent Overstreet
2994fcd4de0SKent Overstreet if (deleting_subvol || inode_u->bi_subvol) {
3004fcd4de0SKent Overstreet ret = bch2_subvolume_unlink(trans, inode_u->bi_subvol);
3014fcd4de0SKent Overstreet if (ret)
3024fcd4de0SKent Overstreet goto err;
3034fcd4de0SKent Overstreet
3044fcd4de0SKent Overstreet k = bch2_btree_iter_peek_slot(&dirent_iter);
3054fcd4de0SKent Overstreet ret = bkey_err(k);
3064fcd4de0SKent Overstreet if (ret)
3074fcd4de0SKent Overstreet goto err;
3084fcd4de0SKent Overstreet
3094fcd4de0SKent Overstreet /*
3104fcd4de0SKent Overstreet * If we're deleting a subvolume, we need to really delete the
3114fcd4de0SKent Overstreet * dirent, not just emit a whiteout in the current snapshot:
3124fcd4de0SKent Overstreet */
3134fcd4de0SKent Overstreet bch2_btree_iter_set_snapshot(&dirent_iter, k.k->p.snapshot);
3144fcd4de0SKent Overstreet ret = bch2_btree_iter_traverse(&dirent_iter);
3154fcd4de0SKent Overstreet if (ret)
3164fcd4de0SKent Overstreet goto err;
3174fcd4de0SKent Overstreet } else {
3184fcd4de0SKent Overstreet bch2_inode_nlink_dec(trans, inode_u);
3194fcd4de0SKent Overstreet }
3204fcd4de0SKent Overstreet
3214fcd4de0SKent Overstreet if (inode_u->bi_dir == dirent_iter.pos.inode &&
3224fcd4de0SKent Overstreet inode_u->bi_dir_offset == dirent_iter.pos.offset) {
3234fcd4de0SKent Overstreet inode_u->bi_dir = 0;
3244fcd4de0SKent Overstreet inode_u->bi_dir_offset = 0;
3254fcd4de0SKent Overstreet }
3264fcd4de0SKent Overstreet
3274fcd4de0SKent Overstreet dir_u->bi_mtime = dir_u->bi_ctime = inode_u->bi_ctime = now;
3284fcd4de0SKent Overstreet dir_u->bi_nlink -= is_subdir_for_nlink(inode_u);
3294fcd4de0SKent Overstreet
3304fcd4de0SKent Overstreet ret = bch2_hash_delete_at(trans, bch2_dirent_hash_desc,
3314fcd4de0SKent Overstreet &dir_hash, &dirent_iter,
3324fcd4de0SKent Overstreet BTREE_UPDATE_internal_snapshot_node) ?:
3334fcd4de0SKent Overstreet bch2_inode_write(trans, &dir_iter, dir_u) ?:
3344fcd4de0SKent Overstreet bch2_inode_write(trans, &inode_iter, inode_u);
3354fcd4de0SKent Overstreet err:
3364fcd4de0SKent Overstreet bch2_trans_iter_exit(trans, &inode_iter);
3374fcd4de0SKent Overstreet bch2_trans_iter_exit(trans, &dirent_iter);
3384fcd4de0SKent Overstreet bch2_trans_iter_exit(trans, &dir_iter);
3394fcd4de0SKent Overstreet return ret;
3404fcd4de0SKent Overstreet }
3414fcd4de0SKent Overstreet
bch2_reinherit_attrs(struct bch_inode_unpacked * dst_u,struct bch_inode_unpacked * src_u)3424fcd4de0SKent Overstreet bool bch2_reinherit_attrs(struct bch_inode_unpacked *dst_u,
3434fcd4de0SKent Overstreet struct bch_inode_unpacked *src_u)
3444fcd4de0SKent Overstreet {
3454fcd4de0SKent Overstreet u64 src, dst;
3464fcd4de0SKent Overstreet unsigned id;
3474fcd4de0SKent Overstreet bool ret = false;
3484fcd4de0SKent Overstreet
3494fcd4de0SKent Overstreet for (id = 0; id < Inode_opt_nr; id++) {
3504fcd4de0SKent Overstreet /* Skip attributes that were explicitly set on this inode */
3514fcd4de0SKent Overstreet if (dst_u->bi_fields_set & (1 << id))
3524fcd4de0SKent Overstreet continue;
3534fcd4de0SKent Overstreet
3544fcd4de0SKent Overstreet src = bch2_inode_opt_get(src_u, id);
3554fcd4de0SKent Overstreet dst = bch2_inode_opt_get(dst_u, id);
3564fcd4de0SKent Overstreet
3574fcd4de0SKent Overstreet if (src == dst)
3584fcd4de0SKent Overstreet continue;
3594fcd4de0SKent Overstreet
3604fcd4de0SKent Overstreet bch2_inode_opt_set(dst_u, id, src);
3614fcd4de0SKent Overstreet ret = true;
3624fcd4de0SKent Overstreet }
3634fcd4de0SKent Overstreet
3644fcd4de0SKent Overstreet return ret;
3654fcd4de0SKent Overstreet }
3664fcd4de0SKent Overstreet
subvol_update_parent(struct btree_trans * trans,u32 subvol,u32 new_parent)3674fcd4de0SKent Overstreet static int subvol_update_parent(struct btree_trans *trans, u32 subvol, u32 new_parent)
3684fcd4de0SKent Overstreet {
3694fcd4de0SKent Overstreet struct btree_iter iter;
3704fcd4de0SKent Overstreet struct bkey_i_subvolume *s =
3714fcd4de0SKent Overstreet bch2_bkey_get_mut_typed(trans, &iter,
3724fcd4de0SKent Overstreet BTREE_ID_subvolumes, POS(0, subvol),
3734fcd4de0SKent Overstreet BTREE_ITER_cached, subvolume);
3744fcd4de0SKent Overstreet int ret = PTR_ERR_OR_ZERO(s);
3754fcd4de0SKent Overstreet if (ret)
3764fcd4de0SKent Overstreet return ret;
3774fcd4de0SKent Overstreet
3784fcd4de0SKent Overstreet s->v.fs_path_parent = cpu_to_le32(new_parent);
3794fcd4de0SKent Overstreet bch2_trans_iter_exit(trans, &iter);
3804fcd4de0SKent Overstreet return 0;
3814fcd4de0SKent Overstreet }
3824fcd4de0SKent Overstreet
bch2_rename_trans(struct btree_trans * trans,subvol_inum src_dir,struct bch_inode_unpacked * src_dir_u,subvol_inum dst_dir,struct bch_inode_unpacked * dst_dir_u,struct bch_inode_unpacked * src_inode_u,struct bch_inode_unpacked * dst_inode_u,const struct qstr * src_name,const struct qstr * dst_name,enum bch_rename_mode mode)3834fcd4de0SKent Overstreet int bch2_rename_trans(struct btree_trans *trans,
3844fcd4de0SKent Overstreet subvol_inum src_dir, struct bch_inode_unpacked *src_dir_u,
3854fcd4de0SKent Overstreet subvol_inum dst_dir, struct bch_inode_unpacked *dst_dir_u,
3864fcd4de0SKent Overstreet struct bch_inode_unpacked *src_inode_u,
3874fcd4de0SKent Overstreet struct bch_inode_unpacked *dst_inode_u,
3884fcd4de0SKent Overstreet const struct qstr *src_name,
3894fcd4de0SKent Overstreet const struct qstr *dst_name,
3904fcd4de0SKent Overstreet enum bch_rename_mode mode)
3914fcd4de0SKent Overstreet {
3924fcd4de0SKent Overstreet struct bch_fs *c = trans->c;
3934fcd4de0SKent Overstreet struct btree_iter src_dir_iter = { NULL };
3944fcd4de0SKent Overstreet struct btree_iter dst_dir_iter = { NULL };
3954fcd4de0SKent Overstreet struct btree_iter src_inode_iter = { NULL };
3964fcd4de0SKent Overstreet struct btree_iter dst_inode_iter = { NULL };
3974fcd4de0SKent Overstreet struct bch_hash_info src_hash, dst_hash;
3984fcd4de0SKent Overstreet subvol_inum src_inum, dst_inum;
3994fcd4de0SKent Overstreet u64 src_offset, dst_offset;
4004fcd4de0SKent Overstreet u64 now = bch2_current_time(c);
4014fcd4de0SKent Overstreet int ret;
4024fcd4de0SKent Overstreet
4034fcd4de0SKent Overstreet ret = bch2_inode_peek(trans, &src_dir_iter, src_dir_u, src_dir,
4044fcd4de0SKent Overstreet BTREE_ITER_intent);
4054fcd4de0SKent Overstreet if (ret)
4064fcd4de0SKent Overstreet goto err;
4074fcd4de0SKent Overstreet
4084fcd4de0SKent Overstreet src_hash = bch2_hash_info_init(c, src_dir_u);
4094fcd4de0SKent Overstreet
4104fcd4de0SKent Overstreet if (dst_dir.inum != src_dir.inum ||
4114fcd4de0SKent Overstreet dst_dir.subvol != src_dir.subvol) {
4124fcd4de0SKent Overstreet ret = bch2_inode_peek(trans, &dst_dir_iter, dst_dir_u, dst_dir,
4134fcd4de0SKent Overstreet BTREE_ITER_intent);
4144fcd4de0SKent Overstreet if (ret)
4154fcd4de0SKent Overstreet goto err;
4164fcd4de0SKent Overstreet
4174fcd4de0SKent Overstreet dst_hash = bch2_hash_info_init(c, dst_dir_u);
4184fcd4de0SKent Overstreet } else {
4194fcd4de0SKent Overstreet dst_dir_u = src_dir_u;
4204fcd4de0SKent Overstreet dst_hash = src_hash;
4214fcd4de0SKent Overstreet }
4224fcd4de0SKent Overstreet
4234fcd4de0SKent Overstreet ret = bch2_dirent_rename(trans,
4244fcd4de0SKent Overstreet src_dir, &src_hash, &src_dir_u->bi_size,
4254fcd4de0SKent Overstreet dst_dir, &dst_hash, &dst_dir_u->bi_size,
4264fcd4de0SKent Overstreet src_name, &src_inum, &src_offset,
4274fcd4de0SKent Overstreet dst_name, &dst_inum, &dst_offset,
4284fcd4de0SKent Overstreet mode);
4294fcd4de0SKent Overstreet if (ret)
4304fcd4de0SKent Overstreet goto err;
4314fcd4de0SKent Overstreet
4324fcd4de0SKent Overstreet ret = bch2_inode_peek(trans, &src_inode_iter, src_inode_u, src_inum,
4334fcd4de0SKent Overstreet BTREE_ITER_intent);
4344fcd4de0SKent Overstreet if (ret)
4354fcd4de0SKent Overstreet goto err;
4364fcd4de0SKent Overstreet
4374fcd4de0SKent Overstreet if (dst_inum.inum) {
4384fcd4de0SKent Overstreet ret = bch2_inode_peek(trans, &dst_inode_iter, dst_inode_u, dst_inum,
4394fcd4de0SKent Overstreet BTREE_ITER_intent);
4404fcd4de0SKent Overstreet if (ret)
4414fcd4de0SKent Overstreet goto err;
4424fcd4de0SKent Overstreet }
4434fcd4de0SKent Overstreet
4444fcd4de0SKent Overstreet if (src_inode_u->bi_subvol &&
4454fcd4de0SKent Overstreet dst_dir.subvol != src_inode_u->bi_parent_subvol) {
4464fcd4de0SKent Overstreet ret = subvol_update_parent(trans, src_inode_u->bi_subvol, dst_dir.subvol);
4474fcd4de0SKent Overstreet if (ret)
4484fcd4de0SKent Overstreet goto err;
4494fcd4de0SKent Overstreet }
4504fcd4de0SKent Overstreet
4514fcd4de0SKent Overstreet if (mode == BCH_RENAME_EXCHANGE &&
4524fcd4de0SKent Overstreet dst_inode_u->bi_subvol &&
4534fcd4de0SKent Overstreet src_dir.subvol != dst_inode_u->bi_parent_subvol) {
4544fcd4de0SKent Overstreet ret = subvol_update_parent(trans, dst_inode_u->bi_subvol, src_dir.subvol);
4554fcd4de0SKent Overstreet if (ret)
4564fcd4de0SKent Overstreet goto err;
4574fcd4de0SKent Overstreet }
4584fcd4de0SKent Overstreet
4594fcd4de0SKent Overstreet /* Can't move across subvolumes, unless it's a subvolume root: */
4604fcd4de0SKent Overstreet if (src_dir.subvol != dst_dir.subvol &&
4614fcd4de0SKent Overstreet (!src_inode_u->bi_subvol ||
4624fcd4de0SKent Overstreet (dst_inum.inum && !dst_inode_u->bi_subvol))) {
4634fcd4de0SKent Overstreet ret = -EXDEV;
4644fcd4de0SKent Overstreet goto err;
4654fcd4de0SKent Overstreet }
4664fcd4de0SKent Overstreet
4674fcd4de0SKent Overstreet if (src_inode_u->bi_parent_subvol)
4684fcd4de0SKent Overstreet src_inode_u->bi_parent_subvol = dst_dir.subvol;
4694fcd4de0SKent Overstreet
4704fcd4de0SKent Overstreet if ((mode == BCH_RENAME_EXCHANGE) &&
4714fcd4de0SKent Overstreet dst_inode_u->bi_parent_subvol)
4724fcd4de0SKent Overstreet dst_inode_u->bi_parent_subvol = src_dir.subvol;
4734fcd4de0SKent Overstreet
4744fcd4de0SKent Overstreet src_inode_u->bi_dir = dst_dir_u->bi_inum;
4754fcd4de0SKent Overstreet src_inode_u->bi_dir_offset = dst_offset;
4764fcd4de0SKent Overstreet
4774fcd4de0SKent Overstreet if (mode == BCH_RENAME_EXCHANGE) {
4784fcd4de0SKent Overstreet dst_inode_u->bi_dir = src_dir_u->bi_inum;
4794fcd4de0SKent Overstreet dst_inode_u->bi_dir_offset = src_offset;
4804fcd4de0SKent Overstreet }
4814fcd4de0SKent Overstreet
4824fcd4de0SKent Overstreet if (mode == BCH_RENAME_OVERWRITE &&
4834fcd4de0SKent Overstreet dst_inode_u->bi_dir == dst_dir_u->bi_inum &&
4844fcd4de0SKent Overstreet dst_inode_u->bi_dir_offset == src_offset) {
4854fcd4de0SKent Overstreet dst_inode_u->bi_dir = 0;
4864fcd4de0SKent Overstreet dst_inode_u->bi_dir_offset = 0;
4874fcd4de0SKent Overstreet }
4884fcd4de0SKent Overstreet
4894fcd4de0SKent Overstreet if (mode == BCH_RENAME_OVERWRITE) {
4904fcd4de0SKent Overstreet if (S_ISDIR(src_inode_u->bi_mode) !=
4914fcd4de0SKent Overstreet S_ISDIR(dst_inode_u->bi_mode)) {
4924fcd4de0SKent Overstreet ret = -ENOTDIR;
4934fcd4de0SKent Overstreet goto err;
4944fcd4de0SKent Overstreet }
4954fcd4de0SKent Overstreet
4964fcd4de0SKent Overstreet if (S_ISDIR(dst_inode_u->bi_mode)) {
4974fcd4de0SKent Overstreet ret = bch2_empty_dir_trans(trans, dst_inum);
4984fcd4de0SKent Overstreet if (ret)
4994fcd4de0SKent Overstreet goto err;
5004fcd4de0SKent Overstreet }
5014fcd4de0SKent Overstreet }
5024fcd4de0SKent Overstreet
5034fcd4de0SKent Overstreet if (bch2_reinherit_attrs(src_inode_u, dst_dir_u) &&
5044fcd4de0SKent Overstreet S_ISDIR(src_inode_u->bi_mode)) {
5054fcd4de0SKent Overstreet ret = -EXDEV;
5064fcd4de0SKent Overstreet goto err;
5074fcd4de0SKent Overstreet }
5084fcd4de0SKent Overstreet
5094fcd4de0SKent Overstreet if (mode == BCH_RENAME_EXCHANGE &&
5104fcd4de0SKent Overstreet bch2_reinherit_attrs(dst_inode_u, src_dir_u) &&
5114fcd4de0SKent Overstreet S_ISDIR(dst_inode_u->bi_mode)) {
5124fcd4de0SKent Overstreet ret = -EXDEV;
5134fcd4de0SKent Overstreet goto err;
5144fcd4de0SKent Overstreet }
5154fcd4de0SKent Overstreet
5164fcd4de0SKent Overstreet if (is_subdir_for_nlink(src_inode_u)) {
5174fcd4de0SKent Overstreet src_dir_u->bi_nlink--;
5184fcd4de0SKent Overstreet dst_dir_u->bi_nlink++;
5194fcd4de0SKent Overstreet }
5204fcd4de0SKent Overstreet
5214fcd4de0SKent Overstreet if (S_ISDIR(src_inode_u->bi_mode) &&
5224fcd4de0SKent Overstreet !src_inode_u->bi_subvol)
5234fcd4de0SKent Overstreet src_inode_u->bi_depth = dst_dir_u->bi_depth + 1;
5244fcd4de0SKent Overstreet
5254fcd4de0SKent Overstreet if (mode == BCH_RENAME_EXCHANGE &&
5264fcd4de0SKent Overstreet S_ISDIR(dst_inode_u->bi_mode) &&
5274fcd4de0SKent Overstreet !dst_inode_u->bi_subvol)
5284fcd4de0SKent Overstreet dst_inode_u->bi_depth = src_dir_u->bi_depth + 1;
5294fcd4de0SKent Overstreet
5304fcd4de0SKent Overstreet if (dst_inum.inum && is_subdir_for_nlink(dst_inode_u)) {
5314fcd4de0SKent Overstreet dst_dir_u->bi_nlink--;
5324fcd4de0SKent Overstreet src_dir_u->bi_nlink += mode == BCH_RENAME_EXCHANGE;
5334fcd4de0SKent Overstreet }
5344fcd4de0SKent Overstreet
5354fcd4de0SKent Overstreet if (mode == BCH_RENAME_OVERWRITE)
5364fcd4de0SKent Overstreet bch2_inode_nlink_dec(trans, dst_inode_u);
5374fcd4de0SKent Overstreet
5384fcd4de0SKent Overstreet src_dir_u->bi_mtime = now;
5394fcd4de0SKent Overstreet src_dir_u->bi_ctime = now;
5404fcd4de0SKent Overstreet
5414fcd4de0SKent Overstreet if (src_dir.inum != dst_dir.inum) {
5424fcd4de0SKent Overstreet dst_dir_u->bi_mtime = now;
5434fcd4de0SKent Overstreet dst_dir_u->bi_ctime = now;
5444fcd4de0SKent Overstreet }
5454fcd4de0SKent Overstreet
5464fcd4de0SKent Overstreet src_inode_u->bi_ctime = now;
5474fcd4de0SKent Overstreet
5484fcd4de0SKent Overstreet if (dst_inum.inum)
5494fcd4de0SKent Overstreet dst_inode_u->bi_ctime = now;
5504fcd4de0SKent Overstreet
5514fcd4de0SKent Overstreet ret = bch2_inode_write(trans, &src_dir_iter, src_dir_u) ?:
5524fcd4de0SKent Overstreet (src_dir.inum != dst_dir.inum
5534fcd4de0SKent Overstreet ? bch2_inode_write(trans, &dst_dir_iter, dst_dir_u)
5544fcd4de0SKent Overstreet : 0) ?:
5554fcd4de0SKent Overstreet bch2_inode_write(trans, &src_inode_iter, src_inode_u) ?:
5564fcd4de0SKent Overstreet (dst_inum.inum
5574fcd4de0SKent Overstreet ? bch2_inode_write(trans, &dst_inode_iter, dst_inode_u)
5584fcd4de0SKent Overstreet : 0);
5594fcd4de0SKent Overstreet err:
5604fcd4de0SKent Overstreet bch2_trans_iter_exit(trans, &dst_inode_iter);
5614fcd4de0SKent Overstreet bch2_trans_iter_exit(trans, &src_inode_iter);
5624fcd4de0SKent Overstreet bch2_trans_iter_exit(trans, &dst_dir_iter);
5634fcd4de0SKent Overstreet bch2_trans_iter_exit(trans, &src_dir_iter);
5644fcd4de0SKent Overstreet return ret;
5654fcd4de0SKent Overstreet }
5664fcd4de0SKent Overstreet
567758ea4ffSKent Overstreet /* inum_to_path */
568758ea4ffSKent Overstreet
prt_bytes_reversed(struct printbuf * out,const void * b,unsigned n)5694fcd4de0SKent Overstreet static inline void prt_bytes_reversed(struct printbuf *out, const void *b, unsigned n)
5704fcd4de0SKent Overstreet {
5714fcd4de0SKent Overstreet bch2_printbuf_make_room(out, n);
5724fcd4de0SKent Overstreet
5734fcd4de0SKent Overstreet unsigned can_print = min(n, printbuf_remaining(out));
5744fcd4de0SKent Overstreet
5754fcd4de0SKent Overstreet b += n;
5764fcd4de0SKent Overstreet
5774fcd4de0SKent Overstreet for (unsigned i = 0; i < can_print; i++)
5784fcd4de0SKent Overstreet out->buf[out->pos++] = *((char *) --b);
5794fcd4de0SKent Overstreet
5804fcd4de0SKent Overstreet printbuf_nul_terminate(out);
5814fcd4de0SKent Overstreet }
5824fcd4de0SKent Overstreet
prt_str_reversed(struct printbuf * out,const char * s)5834fcd4de0SKent Overstreet static inline void prt_str_reversed(struct printbuf *out, const char *s)
5844fcd4de0SKent Overstreet {
5854fcd4de0SKent Overstreet prt_bytes_reversed(out, s, strlen(s));
5864fcd4de0SKent Overstreet }
5874fcd4de0SKent Overstreet
reverse_bytes(void * b,size_t n)5884fcd4de0SKent Overstreet static inline void reverse_bytes(void *b, size_t n)
5894fcd4de0SKent Overstreet {
5904fcd4de0SKent Overstreet char *e = b + n, *s = b;
5914fcd4de0SKent Overstreet
5924fcd4de0SKent Overstreet while (s < e) {
5934fcd4de0SKent Overstreet --e;
5944fcd4de0SKent Overstreet swap(*s, *e);
5954fcd4de0SKent Overstreet s++;
5964fcd4de0SKent Overstreet }
5974fcd4de0SKent Overstreet }
5984fcd4de0SKent Overstreet
5994fcd4de0SKent Overstreet /* XXX: we don't yet attempt to print paths when we don't know the subvol */
bch2_inum_to_path(struct btree_trans * trans,subvol_inum inum,struct printbuf * path)6004fcd4de0SKent Overstreet int bch2_inum_to_path(struct btree_trans *trans, subvol_inum inum, struct printbuf *path)
6014fcd4de0SKent Overstreet {
6024fcd4de0SKent Overstreet unsigned orig_pos = path->pos;
6034fcd4de0SKent Overstreet int ret = 0;
6044fcd4de0SKent Overstreet
6054fcd4de0SKent Overstreet while (!(inum.subvol == BCACHEFS_ROOT_SUBVOL &&
6064fcd4de0SKent Overstreet inum.inum == BCACHEFS_ROOT_INO)) {
6074fcd4de0SKent Overstreet struct bch_inode_unpacked inode;
6084fcd4de0SKent Overstreet ret = bch2_inode_find_by_inum_trans(trans, inum, &inode);
6094fcd4de0SKent Overstreet if (ret)
6104fcd4de0SKent Overstreet goto disconnected;
6114fcd4de0SKent Overstreet
6124fcd4de0SKent Overstreet if (!inode.bi_dir && !inode.bi_dir_offset) {
6134fcd4de0SKent Overstreet ret = -BCH_ERR_ENOENT_inode_no_backpointer;
6144fcd4de0SKent Overstreet goto disconnected;
6154fcd4de0SKent Overstreet }
6164fcd4de0SKent Overstreet
6174fcd4de0SKent Overstreet inum.subvol = inode.bi_parent_subvol ?: inum.subvol;
6184fcd4de0SKent Overstreet inum.inum = inode.bi_dir;
6194fcd4de0SKent Overstreet
6204fcd4de0SKent Overstreet u32 snapshot;
6214fcd4de0SKent Overstreet ret = bch2_subvolume_get_snapshot(trans, inum.subvol, &snapshot);
6224fcd4de0SKent Overstreet if (ret)
6234fcd4de0SKent Overstreet goto disconnected;
6244fcd4de0SKent Overstreet
6254fcd4de0SKent Overstreet struct btree_iter d_iter;
6264fcd4de0SKent Overstreet struct bkey_s_c_dirent d = bch2_bkey_get_iter_typed(trans, &d_iter,
6274fcd4de0SKent Overstreet BTREE_ID_dirents, SPOS(inode.bi_dir, inode.bi_dir_offset, snapshot),
6284fcd4de0SKent Overstreet 0, dirent);
6294fcd4de0SKent Overstreet ret = bkey_err(d.s_c);
6304fcd4de0SKent Overstreet if (ret)
6314fcd4de0SKent Overstreet goto disconnected;
6324fcd4de0SKent Overstreet
6334fcd4de0SKent Overstreet struct qstr dirent_name = bch2_dirent_get_name(d);
6344fcd4de0SKent Overstreet prt_bytes_reversed(path, dirent_name.name, dirent_name.len);
6354fcd4de0SKent Overstreet
6364fcd4de0SKent Overstreet prt_char(path, '/');
6374fcd4de0SKent Overstreet
6384fcd4de0SKent Overstreet bch2_trans_iter_exit(trans, &d_iter);
6394fcd4de0SKent Overstreet }
6404fcd4de0SKent Overstreet
6414fcd4de0SKent Overstreet if (orig_pos == path->pos)
6424fcd4de0SKent Overstreet prt_char(path, '/');
6434fcd4de0SKent Overstreet out:
6444fcd4de0SKent Overstreet ret = path->allocation_failure ? -ENOMEM : 0;
6454fcd4de0SKent Overstreet if (ret)
6464fcd4de0SKent Overstreet goto err;
6474fcd4de0SKent Overstreet
6484fcd4de0SKent Overstreet reverse_bytes(path->buf + orig_pos, path->pos - orig_pos);
6494fcd4de0SKent Overstreet return 0;
6504fcd4de0SKent Overstreet err:
6514fcd4de0SKent Overstreet return ret;
6524fcd4de0SKent Overstreet disconnected:
6534fcd4de0SKent Overstreet if (bch2_err_matches(ret, BCH_ERR_transaction_restart))
6544fcd4de0SKent Overstreet goto err;
6554fcd4de0SKent Overstreet
6564fcd4de0SKent Overstreet prt_str_reversed(path, "(disconnected)");
6574fcd4de0SKent Overstreet goto out;
6584fcd4de0SKent Overstreet }
659758ea4ffSKent Overstreet
660758ea4ffSKent Overstreet /* fsck */
661758ea4ffSKent Overstreet
bch2_check_dirent_inode_dirent(struct btree_trans * trans,struct bkey_s_c_dirent d,struct bch_inode_unpacked * target,bool in_fsck)662758ea4ffSKent Overstreet static int bch2_check_dirent_inode_dirent(struct btree_trans *trans,
663758ea4ffSKent Overstreet struct bkey_s_c_dirent d,
664*9b0d00a3SKent Overstreet struct bch_inode_unpacked *target,
665*9b0d00a3SKent Overstreet bool in_fsck)
666758ea4ffSKent Overstreet {
667758ea4ffSKent Overstreet struct bch_fs *c = trans->c;
668758ea4ffSKent Overstreet struct printbuf buf = PRINTBUF;
669758ea4ffSKent Overstreet struct btree_iter bp_iter = { NULL };
670758ea4ffSKent Overstreet int ret = 0;
671758ea4ffSKent Overstreet
672758ea4ffSKent Overstreet if (inode_points_to_dirent(target, d))
673758ea4ffSKent Overstreet return 0;
674758ea4ffSKent Overstreet
675758ea4ffSKent Overstreet if (!target->bi_dir &&
676758ea4ffSKent Overstreet !target->bi_dir_offset) {
677758ea4ffSKent Overstreet fsck_err_on(S_ISDIR(target->bi_mode),
678758ea4ffSKent Overstreet trans, inode_dir_missing_backpointer,
679758ea4ffSKent Overstreet "directory with missing backpointer\n%s",
680758ea4ffSKent Overstreet (printbuf_reset(&buf),
681758ea4ffSKent Overstreet bch2_bkey_val_to_text(&buf, c, d.s_c),
682758ea4ffSKent Overstreet prt_printf(&buf, "\n"),
683758ea4ffSKent Overstreet bch2_inode_unpacked_to_text(&buf, target),
684758ea4ffSKent Overstreet buf.buf));
685758ea4ffSKent Overstreet
686758ea4ffSKent Overstreet fsck_err_on(target->bi_flags & BCH_INODE_unlinked,
687758ea4ffSKent Overstreet trans, inode_unlinked_but_has_dirent,
688758ea4ffSKent Overstreet "inode unlinked but has dirent\n%s",
689758ea4ffSKent Overstreet (printbuf_reset(&buf),
690758ea4ffSKent Overstreet bch2_bkey_val_to_text(&buf, c, d.s_c),
691758ea4ffSKent Overstreet prt_printf(&buf, "\n"),
692758ea4ffSKent Overstreet bch2_inode_unpacked_to_text(&buf, target),
693758ea4ffSKent Overstreet buf.buf));
694758ea4ffSKent Overstreet
695758ea4ffSKent Overstreet target->bi_flags &= ~BCH_INODE_unlinked;
696758ea4ffSKent Overstreet target->bi_dir = d.k->p.inode;
697758ea4ffSKent Overstreet target->bi_dir_offset = d.k->p.offset;
698758ea4ffSKent Overstreet return __bch2_fsck_write_inode(trans, target);
699758ea4ffSKent Overstreet }
700758ea4ffSKent Overstreet
701758ea4ffSKent Overstreet if (bch2_inode_should_have_single_bp(target) &&
702758ea4ffSKent Overstreet !fsck_err(trans, inode_wrong_backpointer,
703758ea4ffSKent Overstreet "dirent points to inode that does not point back:\n %s",
704758ea4ffSKent Overstreet (bch2_bkey_val_to_text(&buf, c, d.s_c),
705758ea4ffSKent Overstreet prt_printf(&buf, "\n "),
706758ea4ffSKent Overstreet bch2_inode_unpacked_to_text(&buf, target),
707758ea4ffSKent Overstreet buf.buf)))
708758ea4ffSKent Overstreet goto err;
709758ea4ffSKent Overstreet
710758ea4ffSKent Overstreet struct bkey_s_c_dirent bp_dirent =
711758ea4ffSKent Overstreet bch2_bkey_get_iter_typed(trans, &bp_iter, BTREE_ID_dirents,
712758ea4ffSKent Overstreet SPOS(target->bi_dir, target->bi_dir_offset, target->bi_snapshot),
713758ea4ffSKent Overstreet 0, dirent);
714758ea4ffSKent Overstreet ret = bkey_err(bp_dirent);
715758ea4ffSKent Overstreet if (ret && !bch2_err_matches(ret, ENOENT))
716758ea4ffSKent Overstreet goto err;
717758ea4ffSKent Overstreet
718758ea4ffSKent Overstreet bool backpointer_exists = !ret;
719758ea4ffSKent Overstreet ret = 0;
720758ea4ffSKent Overstreet
721*9b0d00a3SKent Overstreet if (!backpointer_exists) {
722*9b0d00a3SKent Overstreet if (fsck_err(trans, inode_wrong_backpointer,
723758ea4ffSKent Overstreet "inode %llu:%u has wrong backpointer:\n"
724758ea4ffSKent Overstreet "got %llu:%llu\n"
725758ea4ffSKent Overstreet "should be %llu:%llu",
726758ea4ffSKent Overstreet target->bi_inum, target->bi_snapshot,
727758ea4ffSKent Overstreet target->bi_dir,
728758ea4ffSKent Overstreet target->bi_dir_offset,
729758ea4ffSKent Overstreet d.k->p.inode,
730758ea4ffSKent Overstreet d.k->p.offset)) {
731758ea4ffSKent Overstreet target->bi_dir = d.k->p.inode;
732758ea4ffSKent Overstreet target->bi_dir_offset = d.k->p.offset;
733758ea4ffSKent Overstreet ret = __bch2_fsck_write_inode(trans, target);
734758ea4ffSKent Overstreet }
735*9b0d00a3SKent Overstreet } else {
736758ea4ffSKent Overstreet bch2_bkey_val_to_text(&buf, c, d.s_c);
737758ea4ffSKent Overstreet prt_newline(&buf);
738758ea4ffSKent Overstreet bch2_bkey_val_to_text(&buf, c, bp_dirent.s_c);
739758ea4ffSKent Overstreet
740*9b0d00a3SKent Overstreet if (S_ISDIR(target->bi_mode) || target->bi_subvol) {
741*9b0d00a3SKent Overstreet /*
742*9b0d00a3SKent Overstreet * XXX: verify connectivity of the other dirent
743*9b0d00a3SKent Overstreet * up to the root before removing this one
744*9b0d00a3SKent Overstreet *
745*9b0d00a3SKent Overstreet * Additionally, bch2_lookup would need to cope with the
746*9b0d00a3SKent Overstreet * dirent it found being removed - or should we remove
747*9b0d00a3SKent Overstreet * the other one, even though the inode points to it?
748*9b0d00a3SKent Overstreet */
749*9b0d00a3SKent Overstreet if (in_fsck) {
750*9b0d00a3SKent Overstreet if (fsck_err(trans, inode_dir_multiple_links,
751758ea4ffSKent Overstreet "%s %llu:%u with multiple links\n%s",
752758ea4ffSKent Overstreet S_ISDIR(target->bi_mode) ? "directory" : "subvolume",
753*9b0d00a3SKent Overstreet target->bi_inum, target->bi_snapshot, buf.buf))
754758ea4ffSKent Overstreet ret = bch2_fsck_remove_dirent(trans, d.k->p);
755*9b0d00a3SKent Overstreet } else {
756*9b0d00a3SKent Overstreet bch2_fs_inconsistent(c,
757*9b0d00a3SKent Overstreet "%s %llu:%u with multiple links\n%s",
758*9b0d00a3SKent Overstreet S_ISDIR(target->bi_mode) ? "directory" : "subvolume",
759*9b0d00a3SKent Overstreet target->bi_inum, target->bi_snapshot, buf.buf);
760758ea4ffSKent Overstreet }
761758ea4ffSKent Overstreet
762*9b0d00a3SKent Overstreet goto out;
763*9b0d00a3SKent Overstreet } else {
764758ea4ffSKent Overstreet /*
765758ea4ffSKent Overstreet * hardlinked file with nlink 0:
766758ea4ffSKent Overstreet * We're just adjusting nlink here so check_nlinks() will pick
767758ea4ffSKent Overstreet * it up, it ignores inodes with nlink 0
768758ea4ffSKent Overstreet */
769*9b0d00a3SKent Overstreet if (fsck_err_on(!target->bi_nlink,
770758ea4ffSKent Overstreet trans, inode_multiple_links_but_nlink_0,
771758ea4ffSKent Overstreet "inode %llu:%u type %s has multiple links but i_nlink 0\n%s",
772758ea4ffSKent Overstreet target->bi_inum, target->bi_snapshot, bch2_d_types[d.v->d_type], buf.buf)) {
773758ea4ffSKent Overstreet target->bi_nlink++;
774758ea4ffSKent Overstreet target->bi_flags &= ~BCH_INODE_unlinked;
775758ea4ffSKent Overstreet ret = __bch2_fsck_write_inode(trans, target);
776758ea4ffSKent Overstreet if (ret)
777758ea4ffSKent Overstreet goto err;
778758ea4ffSKent Overstreet }
779*9b0d00a3SKent Overstreet }
780*9b0d00a3SKent Overstreet }
781758ea4ffSKent Overstreet out:
782758ea4ffSKent Overstreet err:
783758ea4ffSKent Overstreet fsck_err:
784758ea4ffSKent Overstreet bch2_trans_iter_exit(trans, &bp_iter);
785758ea4ffSKent Overstreet printbuf_exit(&buf);
786758ea4ffSKent Overstreet bch_err_fn(c, ret);
787758ea4ffSKent Overstreet return ret;
788758ea4ffSKent Overstreet }
789758ea4ffSKent Overstreet
__bch2_check_dirent_target(struct btree_trans * trans,struct btree_iter * dirent_iter,struct bkey_s_c_dirent d,struct bch_inode_unpacked * target,bool in_fsck)790*9b0d00a3SKent Overstreet int __bch2_check_dirent_target(struct btree_trans *trans,
791*9b0d00a3SKent Overstreet struct btree_iter *dirent_iter,
792758ea4ffSKent Overstreet struct bkey_s_c_dirent d,
793*9b0d00a3SKent Overstreet struct bch_inode_unpacked *target,
794*9b0d00a3SKent Overstreet bool in_fsck)
795758ea4ffSKent Overstreet {
796758ea4ffSKent Overstreet struct bch_fs *c = trans->c;
797758ea4ffSKent Overstreet struct printbuf buf = PRINTBUF;
798758ea4ffSKent Overstreet int ret = 0;
799758ea4ffSKent Overstreet
800*9b0d00a3SKent Overstreet ret = bch2_check_dirent_inode_dirent(trans, d, target, in_fsck);
801758ea4ffSKent Overstreet if (ret)
802758ea4ffSKent Overstreet goto err;
803758ea4ffSKent Overstreet
804758ea4ffSKent Overstreet if (fsck_err_on(d.v->d_type != inode_d_type(target),
805758ea4ffSKent Overstreet trans, dirent_d_type_wrong,
806758ea4ffSKent Overstreet "incorrect d_type: got %s, should be %s:\n%s",
807758ea4ffSKent Overstreet bch2_d_type_str(d.v->d_type),
808758ea4ffSKent Overstreet bch2_d_type_str(inode_d_type(target)),
809758ea4ffSKent Overstreet (printbuf_reset(&buf),
810758ea4ffSKent Overstreet bch2_bkey_val_to_text(&buf, c, d.s_c), buf.buf))) {
811758ea4ffSKent Overstreet struct bkey_i_dirent *n = bch2_trans_kmalloc(trans, bkey_bytes(d.k));
812758ea4ffSKent Overstreet ret = PTR_ERR_OR_ZERO(n);
813758ea4ffSKent Overstreet if (ret)
814758ea4ffSKent Overstreet goto err;
815758ea4ffSKent Overstreet
816758ea4ffSKent Overstreet bkey_reassemble(&n->k_i, d.s_c);
817758ea4ffSKent Overstreet n->v.d_type = inode_d_type(target);
818758ea4ffSKent Overstreet if (n->v.d_type == DT_SUBVOL) {
819758ea4ffSKent Overstreet n->v.d_parent_subvol = cpu_to_le32(target->bi_parent_subvol);
820758ea4ffSKent Overstreet n->v.d_child_subvol = cpu_to_le32(target->bi_subvol);
821758ea4ffSKent Overstreet } else {
822758ea4ffSKent Overstreet n->v.d_inum = cpu_to_le64(target->bi_inum);
823758ea4ffSKent Overstreet }
824758ea4ffSKent Overstreet
825*9b0d00a3SKent Overstreet ret = bch2_trans_update(trans, dirent_iter, &n->k_i, 0);
826758ea4ffSKent Overstreet if (ret)
827758ea4ffSKent Overstreet goto err;
828758ea4ffSKent Overstreet }
829758ea4ffSKent Overstreet err:
830758ea4ffSKent Overstreet fsck_err:
831758ea4ffSKent Overstreet printbuf_exit(&buf);
832758ea4ffSKent Overstreet bch_err_fn(c, ret);
833758ea4ffSKent Overstreet return ret;
834758ea4ffSKent Overstreet }
835