BarryServer : Git

All the code for all my projects
// BarryServer : Git / Orion / blob / 1628fcfdfdf2978ed9ccac96ee7d13bb3dc43a01 / vfs / vfs.c

// Related

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(&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 */
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(&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);
}