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 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 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 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 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 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 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 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 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 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 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 */ 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 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 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