Orion
Barry Importing existing Orion kernel d41a53c (2 years, 4 months ago)diff --git a/vfs/vfs.c b/vfs/vfs.c new file mode 100644 index 0000000..68f4b34 --- /dev/null +++ b/vfs/vfs.c @@ -0,0 +1,680 @@ +/* + * This file controls the Virtual File System for the Kernel. It implements a + * generic File System, which can hook other File Systems. This forms a key + * part of the Kernel, and is used for loading programs and some higher-level + * IPC. This system will dispatch messages to the actual File System tasks in + * user-space. + */ + +#include <stdio.h> +#include <stdint.h> +#include <stddef.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <sys/stat.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include "tmpfs/fs.h" +#include "devfs/fs.h" +#include "procfs/fs.h" +#include "ext2fs/fs.h" +#include "vfs.h" +#include "cache.h" +#include "inode.h" +#include "../mem/heap.h" +#include "../task/task.h" + +FileSystemType *fsTypes; + +/* Register a File System Type */ +void +register_fstype(FileSystemType *fstype) +{ + if (!fsTypes) { + fsTypes = fstype; + return; + } + FileSystemType *prev; + for (prev = fsTypes; prev->next; prev = prev->next); + prev->next = fstype; +} + +/* Initialise the Virtual File System */ +void +init_vfs(void) +{ + register_fstype(&tmpfsType); + register_fstype(&devfsType); + register_fstype(&procfsType); + register_fstype(&ext2fsType); + + mount("tmpfs", NULL, "TmpFS", 0, NULL); + mkdir("dev", 0); + mount("devfs", "/dev", "DevFS", 0, NULL); + mkdir("proc", 0); + mount("procfs", "/proc", "ProcFS", 0, NULL); +} + +/* Check if a process has permission for a file */ +int +permission(Inode *inode, int mask) +{ + if (!inode) + return 0; + + int mode = inode->mode; + + if (current->euid == inode->uid) + mode >>= 6; + else if (current->egid == inode->gid) + mode >>= 3; + + if (((mode & mask & 0007) == mask) || super_user()) + return 1; + return 0; +} + +/* Lookup a file path */ +Inode * +lookup(const char *path, CustodyChain *newchain) +{ + if (!current->fs->root) + return NULL; + + char buf[PATH_MAX], *p = buf; + strncpy(buf, path, PATH_MAX); + Inode *ino; + DirEntry *de = NULL; + CustodyChain *chain = create_custody_chain(); + + /* Resolve to absolute/relative root */ + if (*p == '/') { + ino = current->fs->root; + while (*++p == '/'); + } else { + ino = current->fs->cwd; + copy_custody_chain(¤t->fs->cwdCustody, chain); + } + + /* Drop through directories */ + char *curr, *prev = p; + for (curr = p; *curr; curr++) { + if (*curr != '/') + continue; + *curr = '\0'; + if (!strcmp(prev, ".")) { + de = NULL; + } else if (!strcmp(prev, "..")) { + de = NULL; + if (ino != current->fs->root) { + de = remove_custody(chain); + if (!de) + ino = current->fs->root; + } + } else { + de = inode_lookup(ino, prev); + if (!de) { + ino = NULL; + goto end; + } + add_custody(chain, de); + entry_put(de); + } + if (de) { + ino = de->inode; + if (de->mnt) + ino = de->mnt; + } + while (*++curr == '/'); + prev = curr; + } + /* Basename */ + if (!strcmp(prev, ".") || !strcmp(prev, "")) { + if (!S_ISDIR(ino->mode)) { + ino = NULL; + de = NULL; + } + } else if (!strcmp(prev, "..")) { + if (ino != current->fs->root) { + de = remove_custody(chain); + if (!de) + ino = current->fs->root; + } + } else { + de = inode_lookup(ino, prev); + if (!de) { + de = kmalloc(sizeof(DirEntry)); + init_lock(&de->lock); + strncpy(de->name, prev, NAME_MAX); + de->super = ino->super; + de->usage = 1; + } + add_custody(chain, de); + entry_put(de); + } + if (de) { + ino = de->inode; + if (de->mnt) + ino = de->mnt; + if (ino) + inode_get(ino); + } + + /* Copy chain */ + if (newchain) + copy_custody_chain(chain, newchain); +end: + destroy_custody_chain(chain); + return ino; +} + +/* Mount a file system */ +int +mount(const char *src, const char *target, const char *type, + unsigned long flags, void *data) +{ + if (!verify_access(src, strnlen(src, PATH_MAX), PROT_READ)) + return -EFAULT; + if (!verify_access(target, strnlen(target, PATH_MAX), PROT_READ)) + return -EFAULT; + if (!verify_access(type, strnlen(type, 16), PROT_READ)) + return -EFAULT; + + FileSystemType *fsType; + for (fsType = fsTypes; fsType; fsType = fsType->next) + if (!strcmp(fsType->name, type)) + break; + if (!fsType) + return -ENODEV; + + Inode *mnt = fsType->mount(fsType, flags, src, data); + if ((int) mnt < 0) + return (int) mnt; + if (!target && !current->fs->root) { + current->fs->root = inode_get(mnt); + current->fs->cwd = inode_get(mnt); + return 0; + } + /* Find target's DirEntry */ + CustodyChain tmp; + init_custody_chain(&tmp); + Inode *root = lookup(target, &tmp); + if (!root) { + clean_custody_chain(&tmp); + return -ENOENT; + } + if (!S_ISDIR(root->mode)) { + inode_put(root); + clean_custody_chain(&tmp); + return -ENOTDIR; + } + DirEntry *de = entry_get(tmp.end->entry); + de->mnt = inode_get(mnt); + inode_put(root); + clean_custody_chain(&tmp); + return 0; +} + +/* Open a file */ +int +open(const char *name, int flags, ...) +{ + if (!verify_access(name, strnlen(name, PATH_MAX), PROT_READ)) + return -EFAULT; + + /* Allocate file descriptor */ + int fd; + for (fd = 0; fd < NFILES; fd++) + if (!current->files->fd[fd]) + break; + if (fd == NFILES) + return -EMFILE; + + /* Find inode */ + CustodyChain chain; + init_custody_chain(&chain); + Inode *ino = lookup(name, &chain); + va_list args; + va_start(args, flags); + if (!ino) { + if (!(flags & O_CREATE)) { + clean_custody_chain(&chain); + return -ENOENT; + } + /* Create file */ + if (!chain.size) { + clean_custody_chain(&chain); + return -ENOENT; + } + if (chain.end->prev->entry) { + ino = chain.end->prev->entry->inode; + if (chain.end->prev->entry->mnt) + ino = chain.end->prev->entry->mnt; + } else { + ino = current->fs->root; + } + if (!permission(ino, MAY_WRITE)) { + clean_custody_chain(&chain); + return -EACCES; + } + inode_create(ino, chain.end->entry, va_arg(args, mode_t)); + ino = lookup(name, NULL); + } + va_end(args); + + /* Check permissions */ + int perm = 0; + if ((flags & O_ACCMODE) == O_RDONLY) + perm = MAY_READ; + else if ((flags & O_ACCMODE) == O_WRONLY) + perm = MAY_WRITE; + else if ((flags & O_ACCMODE) == O_RDWR) + perm = MAY_READ | MAY_WRITE; + if (!permission(ino, perm)) { + inode_put(ino); + clean_custody_chain(&chain); + return -EACCES; + } + + /* Open file */ + File *file = kmalloc(sizeof(File)); + file->inode = inode_get(ino); + file->uid = ino->uid; + file->gid = ino->gid; + file->flags = flags; + file->mode = ino->mode; + file->pos = 0; + init_lock(&file->lock); + file->ops = ino->fileOps; + file->path = create_custody_chain(); + file->usage = 1; + + copy_custody_chain(&chain, file->path); + clean_custody_chain(&chain); + + int err = file_open(file); + if (err) { + file_put(file); + inode_put(ino); + return err; + } + + current->files->fd[fd] = file; + inode_put(ino); + return fd; +} + +/* Close a file */ +int +close(int fd) +{ + if (fd < 0 || fd >= NFILES) + return -EBADF; + File *file = current->files->fd[fd]; + current->files->fd[fd] = NULL; + if (!file) + return -EBADF; + + file_put(file); + return 0; +} + +/* Read a file */ +size_t +read(int fd, void *buf, size_t count) +{ + if (!verify_access(buf, count, PROT_WRITE)) + return -EFAULT; + if (fd < 0 || fd >= NFILES) + return -EBADF; + File *file = current->files->fd[fd]; + if (!file) + return -EBADF; + + if (S_ISDIR(file->mode)) + return -EISDIR; + + return file_read(file, buf, count); +} + +/* Write a file */ +size_t +write(int fd, void *buf, size_t count) +{ + if (!verify_access(buf, count, PROT_READ)) + return -EFAULT; + if (fd < 0 || fd >= NFILES) + return -EBADF; + File *file = current->files->fd[fd]; + if (!file) + return -EBADF; + + if (S_ISDIR(file->mode)) + return -EISDIR; + + return file_write(file, buf, count); +} + +/* I/O Control */ +int +ioctl(int fd, unsigned long request, ...) +{ + if (fd < 0 || fd >= NFILES) + return -EBADF; + File *file = current->files->fd[fd]; + if (!file) + return -EBADF; + va_list args; + va_start(args, request); + uintptr_t argp = va_arg(args, uintptr_t); + va_end(args); + return file_ioctl(file, request, argp); +} + +/* Move a file's position */ +off_t +lseek(int fd, off_t offset, int whence) +{ + if (fd < 0 || fd >= NFILES) + return -EBADF; + File *file = current->files->fd[fd]; + if (!file) + return -EBADF; + acquire(&file->lock); + + switch (whence) { + case SEEK_SET: + file->pos = offset; + break; + case SEEK_CUR: + file->pos += offset; + break; + case SEEK_END: + file->pos = file->inode->size + offset; + break; + default: + release(&file->lock); + return -EINVAL; + } + release(&file->lock); + return file->pos; +} + +/* Get file status */ +int +stat(const char *pathname, struct stat *statbuf) +{ + if (!verify_access(pathname, strnlen(pathname, PATH_MAX), PROT_READ)) + return -EFAULT; + if (!verify_access(statbuf, sizeof(struct stat), PROT_WRITE)) + return -EFAULT; + Inode *inode = lookup(pathname, NULL); + if (!inode) + return -ENOENT; + if (statbuf) { + statbuf->inode = inode->ino; + statbuf->mode = inode->mode; + statbuf->nlink = inode->nlink; + statbuf->uid = inode->uid; + statbuf->gid = inode->gid; + statbuf->size = inode->size; + } + inode_put(inode); + return 0; +} + +/* Get file status by file descriptor */ +int +fstat(int fd, struct stat *statbuf) +{ + if (!verify_access(statbuf, sizeof(struct stat), PROT_WRITE)) + return -EFAULT; + if (fd < 0 || fd >= NFILES) + return -EBADF; + File *file = current->files->fd[fd]; + if (!file) + return -EBADF; + Inode *inode = file->inode; + statbuf->inode = inode->ino; + statbuf->mode = inode->mode; + statbuf->size = inode->size; + return 0; +} + +/* Get directory entries */ +size_t +getdents(int fd, void *buf, size_t count) +{ + if (!verify_access(buf, count, PROT_WRITE)) + return -EFAULT; + if (count < sizeof(DirEnt)) + return -EINVAL; + + int err; + size_t i, size = 0; + DirEnt *dent = buf; + File *file = current->files->fd[fd]; + if (!file) + return -EBADF; + if (!S_ISDIR(file->inode->mode)) + return -ENOTDIR; + + /* Read as many entries as possible */ + for (i = 0; i < count / sizeof(DirEnt); i++) { + err = file_readdir(file, dent, i); + if (err < 0) + goto out; + size += sizeof(DirEnt) + dent->namelen; + *((char *) buf + size - 1) = '\0'; + dent = (void *) ((char *) buf + size); + } +out: + if (!i) + return err; + return size; +} + +/* Make a directory */ +int +mkdir(const char *pathname, mode_t mode) +{ + if (!verify_access(pathname, strnlen(pathname, PATH_MAX), PROT_READ)) + return -EFAULT; + int err; + CustodyChain chain; + init_custody_chain(&chain); + Inode *inode = lookup(pathname, &chain); + if (inode) { + err = -EEXIST; + goto end; + } + if (!chain.size) { + err = -ENOENT; + goto end; + } + /* Check write permission */ + if (chain.end->prev->entry) { + inode = chain.end->prev->entry->inode; + if (chain.end->prev->entry->mnt) + inode = chain.end->prev->entry->mnt; + } else { + inode = current->fs->root; + } + if (!permission(inode, MAY_WRITE)) { + err = -EACCES; + goto end; + } + + /* Create directory */ + err = inode_mkdir(inode, entry_get(chain.end->entry), mode); +end: + clean_custody_chain(&chain); +// if (inode) +// inode_put(inode); + return err; +} + +/* Remove a directory */ +int +rmdir(const char *pathname) +{ + return 0; +} + +/* Make a special node */ +int +mknod(const char *pathname, mode_t mode, dev_t dev) +{ + if (!verify_access(pathname, strnlen(pathname, PATH_MAX), PROT_READ)) + return -EFAULT; + int err; + CustodyChain chain; + init_custody_chain(&chain); + Inode *inode = lookup(pathname, &chain); + if (inode) { + err = -EEXIST; + goto end; + } + if (!chain.size) { + err = -ENOENT; + goto end; + } + /* Check write permission */ + if (chain.end->prev->entry) { + inode = chain.end->prev->entry->inode; + if (chain.end->prev->entry->mnt) + inode = chain.end->prev->entry->mnt; + } else { + inode = current->fs->root; + } + if (!permission(inode, MAY_WRITE)) { + err = -EACCES; + goto end; + } + + /* Create node */ + err = inode_mknod(inode, entry_get(chain.end->entry), mode, dev); +end: + clean_custody_chain(&chain); +// if (inode) +// inode_put(inode); + return err; +} + +/* Rename/move a file */ +int +rename(const char *oldpath, const char *newpath) +{ + return 0; +} + +/* Duplicate a file descriptor */ +int +dup(int oldfd) +{ + /* Check file descriptor */ + if (oldfd < 0 || oldfd >= NFILES) + return -EBADF; + if (!current->files->fd[oldfd]) + return -EBADF; + + /* Allocate file descriptor */ + int fd; + for (fd = 0; fd < NFILES; fd++) + if (!current->files->fd[fd]) + break; + if (fd == NFILES) + return -EMFILE; + dup2(oldfd, fd); +} + +/* Duplicate a file descriptor to a new file descriptor */ +int +dup2(int oldfd, int newfd) +{ + /* Check file descriptors */ + if (oldfd < 0 || oldfd >= NFILES) + return -EBADF; + if (!current->files->fd[oldfd]) + return -EBADF; + if (newfd < 0 || newfd >= NFILES) + return -EBADF; + + if (newfd == oldfd) + return newfd; + + /* Close old file in newfd */ + if (current->files->fd[newfd]) + close(newfd); + current->files->fd[newfd] = file_get(current->files->fd[oldfd]); + return newfd; +} + +/* Check if a file descriptor refers to a terminal */ +int +isatty(int fd) +{ + if (fd < 0 || fd >= NFILES) + return -EBADF; + File *file = current->files->fd[fd]; + if (!file) + return -EBADF; + if (S_ISCHR(file->mode)) + return 1; + return 0; +} + +/* Change the current working directory */ +int +chdir(const char *path) +{ + if (!verify_access(path, strnlen(path, PATH_MAX), PROT_READ)) + return -EFAULT; + CustodyChain tmp; + init_custody_chain(&tmp); + Inode *node = lookup(path, &tmp); + if (!node) + return -ENOENT; + if (!S_ISDIR(node->mode)) + return -ENOTDIR; + if (!permission(node, MAY_READ)) + return -EACCES; + inode_put(current->fs->cwd); + current->fs->cwd = inode_get(node); + while (current->fs->cwdCustody.size) + remove_custody(¤t->fs->cwdCustody); + copy_custody_chain(&tmp, ¤t->fs->cwdCustody); + clean_custody_chain(&tmp); +// inode_put(node); + return 0; +} + +/* Change the current root */ +int +chroot(const char *path) +{ + if (!verify_access(path, strnlen(path, PATH_MAX), PROT_READ)) + return -EFAULT; + Inode *node = lookup(path, NULL); + if (!node) + return -ENOENT; + if (!S_ISDIR(node->mode)) + return -ENOTDIR; + if (!permission(node, MAY_READ)) + return -EACCES; + inode_put(current->fs->root); + current->fs->root = inode_get(node); +// inode_put(node); + return 0; +} + +/* Get the current working directory's path */ +char * +getcwd(char *buf, size_t size) +{ + if (!verify_access(buf, size, PROT_WRITE)) + return (char *) -EFAULT; + return custody_path(¤t->fs->cwdCustody, buf, size); +}