summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@linux.dev>2024-03-02 00:16:18 -0500
committerKent Overstreet <kent.overstreet@linux.dev>2024-03-02 17:31:52 -0500
commitfe2b194e6c0ce8154c85ea34b3ed251d523d654f (patch)
tree78e88d7eb8a4f427a1d10f576b4464725eadde96
parentb3d5e76217a6ac4c2248ce8cd10c6ecd9a1befe3 (diff)
bcachefs: bch2_inum_to_path()
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
-rw-r--r--fs/bcachefs/errcode.h1
-rw-r--r--fs/bcachefs/fs-common.c85
-rw-r--r--fs/bcachefs/fs-common.h3
-rw-r--r--fs/bcachefs/fs.c31
-rw-r--r--fs/bcachefs/fs.h5
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);