Multiuser, Capabilities and Access Controls (#54)
[lunaix-os.git] / lunaix-os / kernel / fs / vfs.c
index 1696d6e961ace4b3ac092809db67cd9dddd42183..32014d34a871e2783ff696ef0bffdce663b4386e 100644 (file)
 
 #include <usr/lunaix/dirent_defs.h>
 
 
 #include <usr/lunaix/dirent_defs.h>
 
+#define INODE_ACCESSED  0
+#define INODE_MODIFY    1
+
 static struct cake_pile* dnode_pile;
 static struct cake_pile* inode_pile;
 static struct cake_pile* file_pile;
 static struct cake_pile* superblock_pile;
 static struct cake_pile* fd_pile;
 
 static struct cake_pile* dnode_pile;
 static struct cake_pile* inode_pile;
 static struct cake_pile* file_pile;
 static struct cake_pile* superblock_pile;
 static struct cake_pile* fd_pile;
 
-struct v_dnode* vfs_sysroot;
+struct v_dnode* vfs_sysroot = NULL;
 
 struct lru_zone *dnode_lru, *inode_lru;
 
 
 struct lru_zone *dnode_lru, *inode_lru;
 
@@ -152,6 +155,20 @@ vfs_dcache_lookup(struct v_dnode* parent, struct hstr* str)
     return NULL;
 }
 
     return NULL;
 }
 
+static void
+__vfs_touch_inode(struct v_inode* inode, const int type)
+{
+    if (type == INODE_MODIFY) {
+        inode->mtime = clock_unixtime();
+    }
+
+    else if (type == INODE_ACCESSED) {
+        inode->atime = clock_unixtime();
+    }
+
+    lru_use_one(inode_lru, &inode->lru);
+}
+
 void
 vfs_dcache_add(struct v_dnode* parent, struct v_dnode* dnode)
 {
 void
 vfs_dcache_add(struct v_dnode* parent, struct v_dnode* dnode)
 {
@@ -192,12 +209,12 @@ vfs_dcache_rehash(struct v_dnode* new_parent, struct v_dnode* dnode)
 int
 vfs_open(struct v_dnode* dnode, struct v_file** file)
 {
 int
 vfs_open(struct v_dnode* dnode, struct v_file** file)
 {
-    if (!dnode->inode || !dnode->inode->ops->open) {
+    struct v_inode* inode = dnode->inode;
+    
+    if (!inode || !inode->ops->open) {
         return ENOTSUP;
     }
 
         return ENOTSUP;
     }
 
-    struct v_inode* inode = dnode->inode;
-
     lock_inode(inode);
 
     struct v_file* vfile = cake_grab(file_pile);
     lock_inode(inode);
 
     struct v_file* vfile = cake_grab(file_pile);
@@ -696,6 +713,31 @@ done:
     return errno;
 }
 
     return errno;
 }
 
+
+static bool
+__check_unlinkable(struct v_dnode* dnode)
+{
+    int acl;
+    bool wr_self, wr_parent;
+    struct v_dnode* parent;
+
+    parent = dnode->parent;
+    acl = dnode->inode->acl;
+
+    wr_self = check_allow_write(dnode->inode);
+    wr_parent = check_allow_write(parent->inode);
+
+    if (!fsacl_test(acl, svtx)) {
+        return wr_self;
+    }
+
+    if (current_euid() == dnode->inode->uid) {
+        return true;
+    }
+
+    return wr_self && wr_parent;
+}
+
 int
 vfs_do_open(const char* path, int options)
 {
 int
 vfs_do_open(const char* path, int options)
 {
@@ -814,6 +856,11 @@ __DEFINE_LXSYSCALL2(int, sys_readdir, int, fd, struct lx_dirent*, dent)
         goto unlock;
     }
 
         goto unlock;
     }
 
+    if (!check_allow_read(inode)) {
+        errno = EPERM;
+        goto unlock;
+    }
+
     struct dir_context dctx = (struct dir_context) {
         .cb_data = dent,
         .read_complete_callback = __vfs_readdir_callback
     struct dir_context dctx = (struct dir_context) {
         .cb_data = dent,
         .read_complete_callback = __vfs_readdir_callback
@@ -836,6 +883,8 @@ __DEFINE_LXSYSCALL3(int, read, int, fd, void*, buf, size_t, count)
 {
     int errno = 0;
     struct v_fd* fd_s;
 {
     int errno = 0;
     struct v_fd* fd_s;
+    struct v_inode* inode;
+
     if ((errno = vfs_getfd(fd, &fd_s))) {
         goto done;
     }
     if ((errno = vfs_getfd(fd, &fd_s))) {
         goto done;
     }
@@ -846,23 +895,29 @@ __DEFINE_LXSYSCALL3(int, read, int, fd, void*, buf, size_t, count)
         goto done;
     }
 
         goto done;
     }
 
-    lock_inode(file->inode);
+    if (!check_allow_read(file->inode)) {
+        errno = EPERM;
+        goto done;
+    }
 
 
-    file->inode->atime = clock_unixtime();
+    inode = file->inode;
+    lock_inode(inode);
 
 
-    if (check_seqdev_node(file->inode) || (fd_s->flags & FO_DIRECT)) {
-        errno = file->ops->read(file->inode, buf, count, file->f_pos);
+    __vfs_touch_inode(inode, INODE_ACCESSED);
+
+    if (check_seqdev_node(inode) || (fd_s->flags & FO_DIRECT)) {
+        errno = file->ops->read(inode, buf, count, file->f_pos);
     } else {
     } else {
-        errno = pcache_read(file->inode, buf, count, file->f_pos);
+        errno = pcache_read(inode, buf, count, file->f_pos);
     }
 
     if (errno > 0) {
         file->f_pos += errno;
     }
 
     if (errno > 0) {
         file->f_pos += errno;
-        unlock_inode(file->inode);
+        unlock_inode(inode);
         return errno;
     }
 
         return errno;
     }
 
-    unlock_inode(file->inode);
+    unlock_inode(inode);
 
 done:
     return DO_STATUS(errno);
 
 done:
     return DO_STATUS(errno);
@@ -891,7 +946,7 @@ __DEFINE_LXSYSCALL3(int, write, int, fd, void*, buf, size_t, count)
     inode = file->inode;
     lock_inode(inode);
 
     inode = file->inode;
     lock_inode(inode);
 
-    inode->mtime = clock_unixtime();
+    __vfs_touch_inode(inode, INODE_MODIFY);
     if ((fd_s->flags & O_APPEND)) {
         file->f_pos = inode->fsize;
     }
     if ((fd_s->flags & O_APPEND)) {
         file->f_pos = inode->fsize;
     }
@@ -932,6 +987,11 @@ __DEFINE_LXSYSCALL3(int, lseek, int, fd, int, offset, int, options)
         goto done;
     }
 
         goto done;
     }
 
+    if (!check_allow_read(inode)) {
+        errno = EPERM;
+        goto done;
+    }
+
     lock_inode(inode);
 
     int overflow = 0;
     lock_inode(inode);
 
     int overflow = 0;
@@ -1012,6 +1072,10 @@ vfs_readlink(struct v_dnode* dnode, char* buf, size_t size)
         return ENOTSUP;
     }
 
         return ENOTSUP;
     }
 
+    if (!check_allow_read(inode)) {
+        return EPERM;
+    }
+
     lock_inode(inode);
 
     int errno = inode->ops->read_symlink(inode, &link);
     lock_inode(inode);
 
     int errno = inode->ops->read_symlink(inode, &link);
@@ -1118,6 +1182,11 @@ __DEFINE_LXSYSCALL1(int, rmdir, const char*, pathname)
 
     lock_dnode(dnode);
 
 
     lock_dnode(dnode);
 
+    if (!__check_unlinkable(dnode)) {
+        errno = EPERM;
+        goto done;
+    } 
+
     if ((errno = vfs_check_writable(dnode))) {
         goto done;
     }
     if ((errno = vfs_check_writable(dnode))) {
         goto done;
     }
@@ -1214,7 +1283,7 @@ done:
     return DO_STATUS(errno);
 }
 
     return DO_STATUS(errno);
 }
 
-int
+static int
 __vfs_do_unlink(struct v_dnode* dnode)
 {
     int errno;
 __vfs_do_unlink(struct v_dnode* dnode)
 {
     int errno;
@@ -1224,6 +1293,10 @@ __vfs_do_unlink(struct v_dnode* dnode)
         return EBUSY;
     }
 
         return EBUSY;
     }
 
+    if (!__check_unlinkable(dnode)) {
+        return EPERM;
+    } 
+
     if ((errno = vfs_check_writable(dnode))) {
         return errno;
     }
     if ((errno = vfs_check_writable(dnode))) {
         return errno;
     }
@@ -1434,16 +1507,11 @@ done:
     return DO_STATUS(errno);
 }
 
     return DO_STATUS(errno);
 }
 
-int
-vfs_do_chdir(struct proc_info* proc, struct v_dnode* dnode)
+static int
+vfs_do_chdir_nolock(struct proc_info* proc, struct v_dnode* dnode)
 {
 {
-    int errno = 0;
-
-    lock_dnode(dnode);
-
     if (!check_directory_node(dnode->inode)) {
     if (!check_directory_node(dnode->inode)) {
-        errno = ENOTDIR;
-        goto done;
+        return ENOTDIR;
     }
 
     if (proc->cwd) {
     }
 
     if (proc->cwd) {
@@ -1453,9 +1521,20 @@ vfs_do_chdir(struct proc_info* proc, struct v_dnode* dnode)
     vfs_ref_dnode(dnode);
     proc->cwd = dnode;
 
     vfs_ref_dnode(dnode);
     proc->cwd = dnode;
 
+    return 0;
+}
+
+static int
+vfs_do_chdir(struct proc_info* proc, struct v_dnode* dnode)
+{
+    int errno = 0;
+
+    lock_dnode(dnode);
+
+    errno = vfs_do_chdir_nolock(proc, dnode);
+
     unlock_dnode(dnode);
 
     unlock_dnode(dnode);
 
-done:
     return errno;
 }
 
     return errno;
 }
 
@@ -1489,6 +1568,31 @@ done:
     return DO_STATUS(errno);
 }
 
     return DO_STATUS(errno);
 }
 
+
+__DEFINE_LXSYSCALL1(int, chroot, const char*, path)
+{
+    int errno;
+    struct v_dnode* dnode;
+    if ((errno = vfs_walk_proc(path, &dnode, NULL, 0))) {
+        return errno;
+    }
+
+    lock_dnode(dnode);
+
+    errno = vfs_do_chdir_nolock(__current, dnode);
+    if (errno) {
+        unlock_dnode(dnode);
+        goto done;
+    }
+
+    __current->root = dnode;
+    
+    unlock_dnode(dnode);
+
+done:
+    return DO_STATUS(errno);
+}
+
 __DEFINE_LXSYSCALL2(char*, getcwd, char*, buf, size_t, size)
 {
     int errno = 0;
 __DEFINE_LXSYSCALL2(char*, getcwd, char*, buf, size_t, size)
 {
     int errno = 0;
@@ -1629,12 +1733,21 @@ __DEFINE_LXSYSCALL2(int, fstat, int, fd, struct file_stat*, stat)
     struct v_inode* vino = fds->file->inode;
     struct device* fdev = vino->sb->dev;
 
     struct v_inode* vino = fds->file->inode;
     struct device* fdev = vino->sb->dev;
 
-    *stat = (struct file_stat){.st_ino = vino->id,
-                               .st_blocks = vino->lb_usage,
-                               .st_size = vino->fsize,
-                               .mode = vino->itype,
-                               .st_ioblksize = PAGE_SIZE,
-                               .st_blksize = vino->sb->blksize};
+    stat->st_ino     = vino->id;
+    stat->st_blocks  = vino->lb_usage;
+    stat->st_size    = vino->fsize;
+    stat->st_blksize = vino->sb->blksize;
+    stat->st_nlink   = vino->link_count;
+    stat->st_uid     = vino->uid;
+    stat->st_gid     = vino->gid;
+
+    stat->st_ctim    = vino->ctime;
+    stat->st_atim    = vino->atime;
+    stat->st_mtim    = vino->mtime;
+
+    stat->st_mode    = (vino->itype << 16) | vino->acl;
+
+    stat->st_ioblksize = PAGE_SIZE;
 
     if (check_device_node(vino)) {
         struct device* rdev = resolve_device(vino->data);
 
     if (check_device_node(vino)) {
         struct device* rdev = resolve_device(vino->data);
@@ -1654,6 +1767,108 @@ __DEFINE_LXSYSCALL2(int, fstat, int, fd, struct file_stat*, stat)
                                .index = dev_uid(fdev) };
     }
 
                                .index = dev_uid(fdev) };
     }
 
+done:
+    return DO_STATUS(errno);
+}
+
+__DEFINE_LXSYSCALL4(int, fchmodat, int, fd, 
+                    const char*, path, int, mode, int, flags)
+{
+    int errno;
+    struct v_dnode *dnode;
+    struct v_inode* inode;
+
+    errno = vfs_walkat(fd, path, flags, &dnode);
+    if (errno) {
+        goto done;
+    }
+
+    errno = vfs_check_writable(dnode);
+    if (errno) {
+        return errno;
+    }
+
+    inode = dnode->inode;
+    lock_inode(inode);
+
+    if (!current_is_root()) {
+        mode = mode & FSACL_RWXMASK;
+    }
+
+    inode->acl = mode;
+    __vfs_touch_inode(inode, INODE_MODIFY);
+
+    unlock_inode(inode);
+
+done:
+    return DO_STATUS(errno);
+}
+
+__DEFINE_LXSYSCALL5(int, fchownat, int, fd, 
+                    const char*, path, uid_t, uid, gid_t, gid, int, flags)
+{
+    int errno;
+    struct v_dnode *dnode;
+    struct v_inode *inode;
+
+    errno = vfs_walkat(fd, path, flags, &dnode);
+    if (errno) {
+        goto done;
+    }
+
+    errno = vfs_check_writable(dnode);
+    if (errno) {
+        return errno;
+    }
+
+    inode = dnode->inode;
+    lock_inode(inode);
+
+    inode->uid = uid;
+    inode->gid = gid;
+    __vfs_touch_inode(inode, INODE_MODIFY);
+
+    unlock_inode(inode);
+
+done:
+    return DO_STATUS(errno);
+}
+
+__DEFINE_LXSYSCALL4(int, faccessat, int, fd, 
+                    const char*, path, int, amode, int, flags)
+{
+    int errno, acl;
+    struct v_dnode *dnode;
+    struct v_inode *inode;
+    struct user_scope* uscope;
+
+    uid_t tuid;
+    gid_t tgid;
+
+    errno = vfs_walkat(fd, path, flags, &dnode);
+    if (errno) {
+        goto done;
+    }
+
+    if ((flags & AT_EACCESS)) {
+        tuid = current_euid();
+        tgid = current_egid();
+    }
+    else {
+        uscope = current_user_scope();
+        tuid = uscope->ruid;
+        tgid = uscope->rgid;
+    }
+
+    inode = dnode->inode;
+
+    acl  = inode->acl;
+    acl &= amode;
+    acl &= check_acl_between(inode->uid, inode->gid, tuid, tgid);
+    if (!acl) {
+        errno = EACCESS;
+    }
+
 done:
     return DO_STATUS(errno);
 }
\ No newline at end of file
 done:
     return DO_STATUS(errno);
 }
\ No newline at end of file