BarryServer : Git

All the code for all my projects
// BarryServer : Git / Nucleus / blob / master / vfs / open.c

// Related

Nucleus

Barry System headers (remove libc dependency) 18495cf (3 years, 2 months ago)
/*
 * This file contains the open system call.  It handles creating missing files,
 * and installing the file descriptor.  Most of the actual code to find the
 * inode by name is in the lookup() routine.
 */

#include <stdarg.h>
#include <sys/errno.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <nucleus/lib.h>
#include <nucleus/memory.h>
#include <nucleus/object.h>
#include <nucleus/task.h>
#include <nucleus/vfs.h>
#include "namespace.h"

/* Lookup a file path */
Inode *
lookup(const char *path, ObjectList *newcustody)
{
	if (!current->fs->root)
		return NULL;

	char buf[PATH_MAX], *p = buf;
	strncpy(buf, path, PATH_MAX);
	Inode *inode;
	DirEntry *entry = NULL;
	ObjectList *custody;

	/* Resolve to absolute/relative root */
	if (*p == '/') {
		inode = get(current->fs->root);
		while (*++p == '/');
		custody = create_list(&dirEntryType, LIST_NORMAL);
	} else {
		inode = get(current->fs->cwd);
		custody = copy_list(current->fs->cwdPath);
	}

	/* Drop through directories */
	char *curr, *prev = p;
	for (curr = p; *curr; curr++) {
		if (*curr != '/')
			continue;
		*curr = '\0';

		/* Path part */
		if (!strcmp(prev, ".")) {
			entry = NULL;
		} else if (!strcmp(prev, "..")) {
			entry = NULL;
			if (inode != current->fs->root) {
				entry = pop_from_end(custody);
				if (!entry)
					swap_ref(inode, current->fs->root);
				else
					put(entry);
			}
		} else {
			entry = inode_lookup(inode, prev);
			if (!entry) {
				swap_ref(inode, NULL);
				goto end;
			}
			add(custody, entry);
			put(entry);
		}
		if (entry)
			swap_ref(inode, entry->inode);

		while (*++curr == '/');
		prev = curr;
	}

	/* Base name */
	if (!strcmp(prev, ".") || !strcmp(prev, "")) {
		if (!S_ISDIR(inode->mode)) {
			swap_ref(inode, NULL);
			swap_ref(entry, NULL);
		}
	} else if (!strcmp(prev, "..")) {
		if (inode != current->fs->root) {
			entry = pop_from_end(custody);
			if (!entry)
				swap_ref(inode, current->fs->root);
			else
				put(entry);
		}
	} else {
		entry = inode_lookup(inode, prev);
		if (!entry) {
			entry = new(&dirEntryType);
			strncpy(entry->name, prev, NAME_MAX);
			entry->super = inode->super;
		}
		add(custody, entry);
		put(entry);
	}
	if (entry)
		swap_ref(inode, entry->inode);

	/* Copy path */
	if (newcustody)
		concat_list(custody, newcustody);
end:
	destroy_list(custody);
	return inode;
}

/* Open a file */
int
open(const char *name, int flags, ...)
{
	if (!name)
		return -EFAULT;
	if (!verify_access(name, strnlen(name, PATH_MAX), PROT_READ))
		return -EFAULT;

	/* Allocate a file descriptor */
	int fd = allocate_fd();
	if (fd < 0)
		return -EMFILE;

	/* Find inode */
	ObjectList *custody = create_list(&dirEntryType, LIST_NORMAL);
	Inode *inode = lookup(name, custody);
	DirEntry *entry;
	va_list args;
	if (!inode) {
		if (!(flags & O_CREATE)) {
			destroy_list(custody);
			return -ENOENT;
		}
		/* Create file */
		if (!count(custody)) {
			destroy_list(custody);
			return -ENOENT;
		}
		entry = get_nth_item(custody, count(custody) - 2);
		if (entry)
			inode = entry->inode;
		else
			inode = current->fs->root;
		if (!permission(inode, PROT_WRITE)) {
			destroy_list(custody);
			return -EACCES;
		}
		va_start(args, flags);
		inode_create(inode, get_nth_item(custody, count(custody) - 1),
		             va_arg(args, mode_t));
		inode = lookup(name, NULL);
		va_end(args);
	}

	/* Check permissions */
	int perm = 0;
	if ((flags & O_ACCMODE) == O_RDONLY)
		perm = PROT_READ;
	else if ((flags & O_ACCMODE) == O_WRONLY)
		perm = PROT_WRITE;
	else if ((flags & O_ACCMODE) == O_RDWR)
		perm = PROT_READ | PROT_WRITE;
	if (!permission(inode, perm)) {
		put(inode);
		destroy_list(custody);
		return -EACCES;
	}

	/* Open file */
	File *file = new(&fileType);
	file->inode = get(inode);
	file->flags = flags;
	file->ops = inode->fileOps;
	file->path = custody;

	int err = file_open(file);
	if (err) {
		put(file);
		put(inode);
		return err;
	}

	install_fd(fd, file);
	put(inode);
	return fd;
}

/* Make a directory */
int
mkdir(const char *pathname, mode_t mode)
{
	if (!pathname)
		return -EFAULT;
	if (!verify_access(pathname, strnlen(pathname, PATH_MAX), PROT_READ))
		return -EFAULT;
	int err;
	ObjectList *custody = create_list(&dirEntryType, LIST_NORMAL);
	Inode *inode = lookup(pathname, custody);
	if (inode) {
		err = -EEXIST;
		goto end;
	}
	if (!count(custody)) {
		err = -ENOENT;
		goto end;
	}
	/* Check write permission */
	DirEntry *entry = get_nth_item(custody, count(custody) - 2);
	if (entry)
		inode = entry->inode;
	else
		inode = current->fs->root;
	if (!permission(inode, PROT_WRITE)) {
		err = -EACCES;
		goto end;
	}

	/* Create directory */
	err = inode_mkdir(inode, get_nth_item(custody, count(custody) - 1),
	                  mode);
end:
	destroy_list(custody);
	return err;
}

/* Make a special node */
int
mknod(const char *pathname, mode_t mode, dev_t dev)
{
	if (!pathname)
		return -EFAULT;
	if (!verify_access(pathname, strnlen(pathname, PATH_MAX), PROT_READ))
		return -EFAULT;
	int err;
	ObjectList *custody = create_list(&dirEntryType, LIST_NORMAL);
	Inode *inode = lookup(pathname, custody);
	if (inode) {
		err = -EEXIST;
		goto end;
	}
	if (!count(custody)) {
		err = -ENOENT;
		goto end;
	}
	/* Check write permission */
	DirEntry *entry = get_nth_item(custody, count(custody) - 2);
	if (entry)
		inode = entry->inode;
	else
		inode = current->fs->root;
	if (!permission(inode, PROT_WRITE)) {
		err = -EACCES;
		goto end;
	}

	/* Create node */
	err = inode_mknod(inode, get_nth_item(custody, count(custody) - 1),
	                  mode, dev);
end:
	destroy_list(custody);
	return err;
}