summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorIan Moffett <ian@osmora.org>2024-07-11 21:31:36 -0400
committerIan Moffett <ian@osmora.org>2024-07-11 21:39:47 -0400
commited5e50f7402842716d00f45faff15be4d8e5bfb6 (patch)
tree28d66ed9f5cc8f9f231d824fd8bb710143aa8bbd /sys
parentb0af158b666cf58bbdc6c6a73328052be43d99bf (diff)
kernel: namei: Allow lookups farther than '/'
Signed-off-by: Ian Moffett <ian@osmora.org>
Diffstat (limited to 'sys')
-rw-r--r--sys/kern/vfs_lookup.c159
1 files changed, 151 insertions, 8 deletions
diff --git a/sys/kern/vfs_lookup.c b/sys/kern/vfs_lookup.c
index 83924c6..658e908 100644
--- a/sys/kern/vfs_lookup.c
+++ b/sys/kern/vfs_lookup.c
@@ -29,25 +29,141 @@
#include <sys/namei.h>
#include <sys/vnode.h>
+#include <sys/mount.h>
#include <sys/errno.h>
#include <vm/dynalloc.h>
#include <string.h>
/*
+ * Fetches the filename within a path at
+ * the nth index denoted by `idx'
+ *
+ * Returns memory allocated by dynalloc()
+ * containing the filename.
+ *
+ * XXX: MUST FREE RETURN VALUE WITH dynfree() WHEN
+ * DONE!
+ */
+static char *
+vfs_get_fname_at(const char *path, size_t idx)
+{
+ size_t pathlen = strlen(path);
+ size_t fname_len;
+
+ char *path_tmp = dynalloc(pathlen + 2);
+ char *ret = NULL;
+ char *start_ptr, *ptr;
+
+ /* Make one-based */
+ ++idx;
+
+ if (path_tmp == NULL) {
+ return NULL;
+ }
+
+ ptr = path_tmp;
+ memcpy(path_tmp, path, pathlen + 1);
+
+ /*
+ * We want to by default have a '/' at the end
+ * to keep the parsing logic from getting more
+ * complicated than it needs to be.
+ */
+ path_tmp[pathlen] = '/';
+ path_tmp[pathlen + 1] = '\0';
+
+ /* Skip any leading slashes */
+ while (*ptr == '/')
+ ++ptr;
+
+ start_ptr = ptr;
+
+ /* Get each filename */
+ while (*ptr != '\0') {
+ /* Handle duplicate delimiter */
+ if (*ptr == '/' && *(ptr + 1) == '/') {
+ /*
+ * Snip this delimiter and skip, the next
+ * will be read and filename returned (if of course
+ * the index is reached).
+ */
+ *(ptr++) = '\0';
+ continue;
+ }
+
+ if (*ptr == '/') {
+ *(ptr++) = '\0';
+
+ /* Continue if index not reached */
+ if ((--idx) != 0) {
+ start_ptr = ptr;
+ continue;
+ }
+
+ /* Index has been reached, start_ptr contains name */
+ fname_len = strlen(start_ptr);
+ ret = dynalloc(fname_len + 1);
+
+ if (ret != NULL) {
+ memcpy(ret, start_ptr, fname_len + 1);
+ }
+ break;
+ }
+
+ ++ptr;
+ }
+
+ dynfree(path_tmp);
+ return ret;
+}
+
+/*
+ * Search for a path within a mountpoint.
+ *
+ * @mp: Mountpoint to search in.
+ * @path: Path to search for.
+ */
+static struct vnode *
+namei_mp_search(struct mount *mp, const char *path)
+{
+ struct vop_lookup_args lookup_args;
+ struct vnode *vp = mp->vp;
+ char *name;
+ int status;
+
+ for (size_t i = 1;; ++i) {
+ name = vfs_get_fname_at(path, i);
+ if (name == NULL)
+ break;
+
+ lookup_args.name = name;
+ lookup_args.dirvp = vp;
+ lookup_args.vpp = &vp;
+
+ status = vfs_vop_lookup(vp, &lookup_args);
+ dynfree(name);
+
+ if (status == 0)
+ return vp;
+ }
+
+ return NULL;
+}
+
+/*
* Convert a path to a vnode.
*
* @ndp: Nameidata containing the path and resulting
* vnode.
- *
- * TODO: Add support for lookups with individual
- * path components
*/
int
namei(struct nameidata *ndp)
{
- struct vnode *vp;
+ struct vnode *vp = NULL;
+ struct mount *mp;
struct vop_lookup_args lookup_args;
const char *path = ndp->path;
+ char *name;
int status;
if (path == NULL) {
@@ -66,6 +182,10 @@ namei(struct nameidata *ndp)
}
/*
+ * Start looking at the root vnode. If we can't find
+ * what we are looking for, we'll try traversing the
+ * mountlist.
+ *
* Some filesystems (like initramfs) may only understand
* full paths, so try passing it through.
*/
@@ -74,10 +194,33 @@ namei(struct nameidata *ndp)
lookup_args.vpp = &vp;
status = vfs_vop_lookup(lookup_args.dirvp, &lookup_args);
- if (status != 0) {
- return status;
+ /* Did we find it in the root */
+ if (status == 0) {
+ ndp->vp = vp;
+ return 0;
+ }
+
+ /* Look through the mountlist */
+ TAILQ_FOREACH(mp, &g_mountlist, mnt_list) {
+ /* If it is unamed, can't do anything */
+ if (mp->name == NULL)
+ continue;
+
+ lookup_args.dirvp = mp->vp;
+ name = vfs_get_fname_at(path, 0);
+
+ /* If the name matches, search within */
+ if (strcmp(mp->name, name) == 0)
+ vp = namei_mp_search(mp, path);
+
+ /* Did we find it at this mountpoint? */
+ if (vp != NULL) {
+ ndp->vp = vp;
+ return 0;
+ }
+
+ dynfree(name);
}
- ndp->vp = vp;
- return 0;
+ return -ENOENT;
}