diff options
author | Ian Moffett <ian@osmora.org> | 2024-07-11 21:31:36 -0400 |
---|---|---|
committer | Ian Moffett <ian@osmora.org> | 2024-07-11 21:39:47 -0400 |
commit | ed5e50f7402842716d00f45faff15be4d8e5bfb6 (patch) | |
tree | 28d66ed9f5cc8f9f231d824fd8bb710143aa8bbd /sys/kern | |
parent | b0af158b666cf58bbdc6c6a73328052be43d99bf (diff) |
kernel: namei: Allow lookups farther than '/'
Signed-off-by: Ian Moffett <ian@osmora.org>
Diffstat (limited to 'sys/kern')
-rw-r--r-- | sys/kern/vfs_lookup.c | 159 |
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; } |