xref: /linux/fs/bcachefs/namei.c (revision 4a4b30ea80d8cb5e8c4c62bb86201f4ea0d9b030)
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