BarryServer : Git

All the code for all my projects
// BarryServer : Git / Orion / commit / d41a53cbc7d055b1c00cf0a339dbed6925f4f02c / vfs / vfs.c

// Related

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(&current->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(&current->fs->cwdCustody);
+	copy_custody_chain(&tmp, &current->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(&current->fs->cwdCustody, buf, size);
+}