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;
}