Nucleus
Barry FS Object wrapper functions 77a8df8 (3 years, 3 months ago)
diff --git a/vfs/open.c b/vfs/open.c
new file mode 100644
index 0000000..cf03a24
--- /dev/null
+++ b/vfs/open.c
@@ -0,0 +1,189 @@
+/*
+ * 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 <string.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.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;
+ ObjectList *custody;
+
+ /* Resolve to absolute/relative root */
+ if (*p == '/') {
+ inode = current->fs->root;
+ while (*++p == '/');
+ custody = create_list(&dirEntryType);
+ } else {
+ inode = 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)
+ inode = current->fs->root;
+ else
+ put(entry);
+ }
+ } else {
+ entry = inode_lookup(inode, prev);
+ if (!entry) {
+ inode = NULL;
+ goto end;
+ }
+ add(custody, entry);
+ put(entry);
+ }
+
+ if (entry)
+ inode = entry->inode;
+
+ while (*++curr == '/');
+ prev = curr;
+ }
+
+ /* Base name */
+ if (!strcmp(prev, ".") || !strcmp(prev, "")) {
+ if (!S_ISDIR(inode->mode)) {
+ inode = NULL;
+ entry = NULL;
+ }
+ } else if (!strcmp(prev, "..")) {
+ if (inode != current->fs->root) {
+ entry = pop_from_end(custody);
+ if (!entry)
+ 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) {
+ inode = entry->inode;
+ if (inode)
+ get(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 (!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);
+ 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;
+}