diff options
author | Kent Overstreet <kent.overstreet@linux.dev> | 2024-03-02 00:16:18 -0500 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@linux.dev> | 2024-03-02 17:31:52 -0500 |
commit | fe2b194e6c0ce8154c85ea34b3ed251d523d654f (patch) | |
tree | 78e88d7eb8a4f427a1d10f576b4464725eadde96 | |
parent | b3d5e76217a6ac4c2248ce8cd10c6ecd9a1befe3 (diff) |
bcachefs: bch2_inum_to_path()
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
-rw-r--r-- | fs/bcachefs/errcode.h | 1 | ||||
-rw-r--r-- | fs/bcachefs/fs-common.c | 85 | ||||
-rw-r--r-- | fs/bcachefs/fs-common.h | 3 | ||||
-rw-r--r-- | fs/bcachefs/fs.c | 31 | ||||
-rw-r--r-- | fs/bcachefs/fs.h | 5 |
5 files changed, 125 insertions, 0 deletions
diff --git a/fs/bcachefs/errcode.h b/fs/bcachefs/errcode.h index af25d8ec60f2..dcf70cfaeaa4 100644 --- a/fs/bcachefs/errcode.h +++ b/fs/bcachefs/errcode.h @@ -114,6 +114,7 @@ x(ENOENT, ENOENT_dirent_doesnt_match_inode) \ x(ENOENT, ENOENT_dev_not_found) \ x(ENOENT, ENOENT_dev_idx_not_found) \ + x(ENOENT, ENOENT_no_backpointer) \ x(ENOTEMPTY, ENOTEMPTY_dir_not_empty) \ x(ENOTEMPTY, ENOTEMPTY_subvol_not_empty) \ x(0, open_buckets_empty) \ diff --git a/fs/bcachefs/fs-common.c b/fs/bcachefs/fs-common.c index 624e6f963240..7d1180036954 100644 --- a/fs/bcachefs/fs-common.c +++ b/fs/bcachefs/fs-common.c @@ -547,3 +547,88 @@ err: bch2_trans_iter_exit(trans, &src_dir_iter); return ret; } + +static void memreverse(char *dst, const char *src, size_t len) +{ + src += len; + while (len--) + *dst++ = *--src; +} + +static void memreverse_inplace(char *p1, size_t len) +{ + char *p2 = p1 + len; + while (p2 > p1) + swap(*p1++, *--p2); +} + +int bch2_inum_to_path_trans(struct btree_trans *trans, subvol_inum inum, darray_char *path) +{ + struct btree_iter iter = {}; + int ret; + + darray_init(path); + + while (true) { + ret = darray_push(path, '/'); + if (ret) + goto err; + + if (inum.subvol == BCACHEFS_ROOT_SUBVOL && + inum.inum == BCACHEFS_ROOT_INO) + break; + + struct bch_inode_unpacked inode_u; + ret = bch2_inode_find_by_inum_trans(trans, inum, &inode_u); + if (ret) + goto err; + + if (!inode_u.bi_dir) { + ret = -BCH_ERR_ENOENT_no_backpointer; + goto err; + } + + u32 subvol = inode_u.bi_parent_subvol ?: inum.subvol; + u32 snapshot; + + ret = bch2_subvolume_get_snapshot(trans, subvol, &snapshot); + if (ret) + goto err; + + struct bkey_s_c_dirent d = + bch2_bkey_get_iter_typed(trans, &iter, BTREE_ID_dirents, + SPOS(inode_u.bi_dir, inode_u.bi_dir_offset, snapshot), + 0, dirent); + ret = bkey_err(d); + if (ret) + goto err; + + struct qstr name = bch2_dirent_get_name(d); + ret = darray_make_room(path, name.len); + if (ret) + goto err; + memreverse(&darray_top(*path), name.name, name.len); + path->nr += name.len; + bch2_trans_iter_exit(trans, &iter); + + inum.inum = inode_u.bi_dir; + inum.subvol = subvol; + } + + memreverse_inplace(path->data, path->nr); + + ret = darray_push(path, '\0'); +err: + if (ret) + darray_exit(path); + bch2_trans_iter_exit(trans, &iter); + return ret; +} + +int bch2_subvol_to_path_trans(struct btree_trans *trans, u32 subvol, darray_char *path) +{ + struct bch_subvolume s; + + return bch2_subvolume_get(trans, subvol, true, 0, &s) ?: + bch2_inum_to_path_trans(trans, (subvol_inum) { subvol, le64_to_cpu(s.inode) }, path); +} diff --git a/fs/bcachefs/fs-common.h b/fs/bcachefs/fs-common.h index dde237859514..d82e4f003a21 100644 --- a/fs/bcachefs/fs-common.h +++ b/fs/bcachefs/fs-common.h @@ -40,4 +40,7 @@ int bch2_rename_trans(struct btree_trans *, bool bch2_reinherit_attrs(struct bch_inode_unpacked *, struct bch_inode_unpacked *); +int bch2_inum_to_path_trans(struct btree_trans *, subvol_inum, darray_char *); +int bch2_subvol_to_path_trans(struct btree_trans *, u32, darray_char *); + #endif /* _BCACHEFS_FS_COMMON_H */ diff --git a/fs/bcachefs/fs.c b/fs/bcachefs/fs.c index d82f7f3f0670..2a4238b8746c 100644 --- a/fs/bcachefs/fs.c +++ b/fs/bcachefs/fs.c @@ -32,6 +32,7 @@ #include <linux/exportfs.h> #include <linux/fiemap.h> #include <linux/module.h> +#include <linux/namei.h> #include <linux/pagemap.h> #include <linux/posix_acl.h> #include <linux/random.h> @@ -1438,6 +1439,36 @@ static const struct export_operations bch_export_ops = { .get_name = bch2_get_name, }; +static int path_permission_check(struct vfsmount *mnt, const char *path) +{ + struct path vfspath; + int ret = vfs_path_lookup(mnt->mnt_root, mnt, path, 0, &vfspath); + path_put(&vfspath); + return ret; +} + +int bch2_inum_to_path_checked(struct bch_fs *c, struct vfsmount *mnt, + subvol_inum inum, darray_char *path) +{ + int ret = bch2_trans_do(c, NULL, NULL, 0, + bch2_inum_to_path_trans(trans, inum, path)) ?: + path_permission_check(mnt, path->data); + if (ret) + darray_exit(path); + return ret; +} + +int bch2_subvol_to_path_checked(struct bch_fs *c, struct vfsmount *mnt, + u32 subvol, darray_char *path) +{ + int ret = bch2_trans_do(c, NULL, NULL, 0, + bch2_subvol_to_path_trans(trans, subvol, path)) ?: + path_permission_check(mnt, path->data); + if (ret) + darray_exit(path); + return ret; +} + static void bch2_vfs_inode_init(struct btree_trans *trans, subvol_inum inum, struct bch_inode_info *inode, struct bch_inode_unpacked *bi, diff --git a/fs/bcachefs/fs.h b/fs/bcachefs/fs.h index c3af7225ff69..8c6f9d42203d 100644 --- a/fs/bcachefs/fs.h +++ b/fs/bcachefs/fs.h @@ -143,6 +143,11 @@ struct bch_inode_unpacked; #ifndef NO_BCACHEFS_FS +int bch2_inum_to_path_checked(struct bch_fs *, struct vfsmount *, + subvol_inum, darray_char *); +int bch2_subvol_to_path_checked(struct bch_fs *, struct vfsmount *, + u32, darray_char *); + struct bch_inode_info * __bch2_create(struct mnt_idmap *, struct bch_inode_info *, struct dentry *, umode_t, dev_t, subvol_inum, unsigned); |