aboutsummaryrefslogtreecommitdiff
path: root/kernel/fs/namev.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/fs/namev.c')
-rw-r--r--kernel/fs/namev.c164
1 files changed, 159 insertions, 5 deletions
diff --git a/kernel/fs/namev.c b/kernel/fs/namev.c
index e8b01e8..343f02c 100644
--- a/kernel/fs/namev.c
+++ b/kernel/fs/namev.c
@@ -76,8 +76,27 @@ long namev_is_descendant(vnode_t *a, vnode_t *b)
long namev_lookup(vnode_t *dir, const char *name, size_t namelen,
vnode_t **res_vnode)
{
- NOT_YET_IMPLEMENTED("VFS: namev_lookup");
- return 0;
+ // // NOT_YET_IMPLEMENTED("VFS: namev_lookup");
+
+ // KASSERT(NULL != dir);
+ // KASSERT(NULL != name);
+ // KASSERT(NULL != res_vnode);
+
+ // if (namelen == 0)
+ // {
+ // return -EINVAL;
+ // }
+
+ if (
+ dir->vn_ops == NULL
+ || dir->vn_ops->lookup == NULL
+ || !S_ISDIR(dir->vn_mode)
+ )
+ {
+ return -ENOTDIR;
+ }
+
+ return dir->vn_ops->lookup(dir, name, namelen, res_vnode);
}
/*
@@ -187,10 +206,85 @@ static const char *namev_tokenize(const char **search, size_t *len)
long namev_dir(vnode_t *base, const char *path, vnode_t **res_vnode,
const char **name, size_t *namelen)
{
- NOT_YET_IMPLEMENTED("VFS: namev_dir");
+ // NOT_YET_IMPLEMENTED("VFS: namev_dir");
+
+ // Check if the pathname is nullish
+ if (path == NULL || *path == '\0')
+ {
+ return -EINVAL;
+ }
+
+ // Get a vnode based on the pathname
+ vnode_t *dir = NULL;
+ if (*path == '/')
+ {
+ dir = vfs_root_fs.fs_root;
+ }
+ else if (base == NULL)
+ {
+ dir = curproc->p_cwd;
+ }
+ else
+ {
+ dir = base;
+ }
+ // Refcount it
+ vref(dir);
+
+ // Tokenize the path
+ const char *token = NULL;
+ size_t len = 0;
+ while ((token = namev_tokenize(&path, &len)) && len > 0)
+ {
+ if (path == NULL || *path == '\0' || (*path == '/' && *(path + 1) == '\0'))
+ {
+ break;
+ }
+
+ // Check if the name is too long
+ if (len > NAME_LEN)
+ {
+ vput(&dir);
+ return -ENAMETOOLONG;
+ }
+
+ // Lookup the token
+ vnode_t *next = NULL;
+ vlock(dir);
+ long err = namev_lookup(dir, token, len, &next);
+ vunlock(dir);
+ if (err < 0)
+ {
+ vput(&dir);
+ return err;
+ }
+
+ *res_vnode = dir;
+ *name = token;
+ *namelen = len;
+
+ // Reduce refcount and move to the next token
+ if (next)
+ {
+ vput(&dir);
+ dir = next;
+ }
+ }
+
+ // Check if it's a directory
+ if (!S_ISDIR(dir->vn_mode))
+ {
+ vput(&dir);
+ return -ENOTDIR;
+ }
+
+ // Assign the basename
+ *res_vnode = dir;
+ *name = token;
+ *namelen = len;
+
return 0;
}
-
/*
* Open the file specified by `base` and `path`, or create it, if necessary.
* Return the file's vnode via `res_vnode`, which should be returned unlocked
@@ -217,7 +311,67 @@ long namev_dir(vnode_t *base, const char *path, vnode_t **res_vnode,
long namev_open(vnode_t *base, const char *path, int oflags, int mode,
devid_t devid, struct vnode **res_vnode)
{
- NOT_YET_IMPLEMENTED("VFS: namev_open");
+ // NOT_YET_IMPLEMENTED("VFS: namev_open");
+
+ // KASSERT(NULL != base);
+ KASSERT(NULL != path);
+ KASSERT(NULL != res_vnode);
+
+ const char *name = NULL;
+ vnode_t *dir = NULL;
+ size_t namelen = 0;
+
+ long err = namev_dir(base, path, &dir, &name, &namelen);
+ if (err < 0)
+ {
+ return err;
+ }
+ if (namelen > NAME_LEN)
+ {
+ vput(&dir);
+ return -ENAMETOOLONG;
+ }
+
+ vnode_t *res = NULL;
+ vlock(dir);
+ err = namev_lookup(dir, name, namelen, &res);
+ vunlock(dir);
+
+ if (err < 0)
+ {
+ if (!(oflags & O_CREAT))
+ {
+ vput(&dir);
+ return err;
+ }
+ else
+ {
+ vlock(dir);
+ err = dir->vn_ops->mknod(dir, name, namelen, mode, devid, &res);
+ vunlock(dir);
+
+ vput(&dir);
+ *res_vnode = res;
+ return err;
+ }
+ }
+
+ if (path[strlen(path) - 1] == '/' && !S_ISDIR(res->vn_mode))
+ {
+ vput(&dir);
+ vput(&res);
+ return -ENOTDIR;
+ }
+
+ if (S_ISDIR(res->vn_mode) && (oflags & O_CREAT))
+ {
+ vput(&dir);
+ vput(&res);
+ return -EISDIR; // TODO: check if this error
+ }
+
+ vput(&dir);
+ *res_vnode = res;
return 0;
}