Multiuser, Capabilities and Access Controls (#54)
[lunaix-os.git] / lunaix-os / kernel / fs / path_walk.c
index d376c798b778660328f07951257c6acfd9c537cd..246e90327cc3219af3f03d418c0fba366dc4aea6 100644 (file)
@@ -3,9 +3,12 @@
 #include <lunaix/process.h>
 #include <lunaix/spike.h>
 
+#include <usr/lunaix/fcntl_defs.h>
+
 #include <klibc/string.h>
 
 #define VFS_SYMLINK_DEPTH 16
+#define VFS_SYMLINK_MAXLEN 512
 
 extern struct lru_zone *dnode_lru, *inode_lru;
 
@@ -28,11 +31,16 @@ __vfs_walk(struct v_dnode* start,
     if (path[0] == VFS_PATH_DELIM || !start) {
         if ((walk_options & VFS_WALK_FSRELATIVE) && start) {
             start = start->super_block->root;
-        } else {
+        } 
+        else if (unlikely(!__current)) {
             start = vfs_sysroot;
-            if (!vfs_sysroot->mnt) {
-                panick("vfs: no root");
-            }
+        }
+        else {
+            start = __current->root ?: vfs_sysroot;
+        }
+
+        if (unlikely(!start || !start->mnt)) {
+            fail("vfs: no root");
         }
 
         if (path[0] == VFS_PATH_DELIM) {
@@ -49,9 +57,12 @@ __vfs_walk(struct v_dnode* start,
     struct hstr name = HSTR(fname_buffer, 0);
 
     char current = path[i++], lookahead;
-    while (current) {
+    while (current) 
+    {
         lookahead = path[i++];
-        if (current != VFS_PATH_DELIM) {
+
+        if (current != VFS_PATH_DELIM) 
+        {
             if (j >= VFS_NAME_MAXLEN - 1) {
                 return ENAMETOOLONG;
             }
@@ -82,13 +93,21 @@ __vfs_walk(struct v_dnode* start,
 
         lock_dnode(current_level);
 
+        if (!check_allow_execute(current_inode)) {
+            errno = EACCESS;
+            unlock_dnode(current_level);
+            goto error;
+        }
+
         dnode = vfs_dcache_lookup(current_level, &name);
 
-        if (!dnode) {
+        if (!dnode) 
+        {
             dnode = vfs_d_alloc(current_level, &name);
 
             if (!dnode) {
                 errno = ENOMEM;
+                unlock_dnode(current_level);
                 goto error;
             }
 
@@ -119,21 +138,29 @@ __vfs_walk(struct v_dnode* start,
         current_level = dnode;
         current_inode = current_level->inode;
 
-        if ((current_inode->itype & F_MSLNK) &&
-            !(walk_options & VFS_WALK_NOFOLLOW)) {
+        assert(current_inode);
+        
+        if (check_symlink_node(current_inode) &&
+            !(walk_options & VFS_WALK_NOFOLLOW)) 
+        {
             const char* link;
+            struct v_inode_ops* iops;
+
+            iops = current_inode->ops;
 
-            if (!current_inode->ops->read_symlink) {
+            if (!iops->read_symlink) {
                 errno = ENOTSUP;
                 goto error;
             }
 
             lock_inode(current_inode);
-            if ((errno =
-                   current_inode->ops->read_symlink(current_inode, &link))) {
+
+            errno = iops->read_symlink(current_inode, &link);
+            if ((errno < 0)) {
                 unlock_inode(current_inode);
                 goto error;
             }
+
             unlock_inode(current_inode);
 
             errno = __vfs_walk(current_level->parent,
@@ -161,6 +188,7 @@ __vfs_walk(struct v_dnode* start,
 
 cleanup:
     vfs_d_free(dnode);
+
 error:
     *dentry = NULL;
     return errno;
@@ -173,6 +201,11 @@ vfs_walk(struct v_dnode* start,
          struct hstr* component,
          int options)
 {
+    if (!path) {
+        *dentry = NULL;
+        return 0;
+    }
+
     // allocate a file name stack for path walking and recursion to resolve
     // symlink
     char* name_buffer = valloc(2048);
@@ -191,4 +224,36 @@ vfs_walk_proc(const char* path,
               int options)
 {
     return vfs_walk(__current->cwd, path, dentry, component, options);
+}
+
+int
+vfs_walkat(int fd, const char* path, int at_opts, struct v_dnode** dnode_out)
+{
+    int errno, options = 0;
+    struct v_dnode *root_dnode;
+    struct v_fd* _fd;
+
+    if ((at_opts & AT_FDCWD)) {
+        root_dnode = __current->cwd;
+    }
+    else 
+    {
+        errno = vfs_getfd(fd, &_fd);
+        if (errno) {
+            return errno;
+        }
+
+        root_dnode = _fd->file->dnode;
+    }
+
+    if ((at_opts & AT_SYMLINK_NOFOLLOW)) {
+        options |= VFS_WALK_NOFOLLOW;
+    }
+
+    errno = vfs_walk(root_dnode, path, dnode_out, NULL, options);
+    if (errno) {
+        return errno;
+    }
+
+    return 0;
 }
\ No newline at end of file