Orion
Barry Keyboard/Mouse drivers + POSIX names for structs 1628fcf (2 years, 4 months ago)/* * 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 */ int 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; int sz = (int) file_read(file, buf, count); return sz; } /* Write a file */ int 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; int sz = (int) file_write(file, buf, count); return sz; } /* 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->st_dev = inode->dev; statbuf->st_ino = inode->ino; statbuf->st_mode = inode->mode; statbuf->st_nlink = inode->nlink; statbuf->st_uid = inode->uid; statbuf->st_gid = inode->gid; statbuf->st_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; if (statbuf) { statbuf->st_dev = inode->dev; statbuf->st_ino = inode->ino; statbuf->st_mode = inode->mode; statbuf->st_nlink = inode->nlink; statbuf->st_uid = inode->uid; statbuf->st_gid = inode->gid; statbuf->st_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->d_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); }