Nucleus
Barry FS Object wrapper functions 77a8df8 (3 years, 3 months ago)
diff --git a/vfs/direntry.c b/vfs/direntry.c
index a8a0110..7a0b559 100644
--- a/vfs/direntry.c
+++ b/vfs/direntry.c
@@ -16,6 +16,7 @@ static void direntry_delete(Object *);
/* Directory Entry object type */
ObjectType dirEntryType = {
+ .name = "DIRECTORY ENTRY",
.size = sizeof(DirEntry),
.delete = direntry_delete,
};
@@ -44,9 +45,11 @@ direntry_delete(Object *obj)
static int
compare_direntry_name(void *addr, void *data)
{
- DirEntry *entry = (void *) addr;
+ DirEntry *entry = addr;
struct FindData *find = data;
uint32_t hash = name_hash(find->name);
+ if (__builtin_expect(!(entry->hash), 0))
+ entry->hash = name_hash(entry->name);
if (entry->hash != hash)
return 0;
if (strcmp(entry->name, find->name))
@@ -59,13 +62,11 @@ compare_direntry_name(void *addr, void *data)
DirEntry *
find_direntry(ObjectList *list, const char *name)
{
- lock(list);
struct FindData data = {
.name = name,
.result = NULL,
};
iterate(list, compare_direntry_name, &data);
- unlock(list);
return data.result;
}
diff --git a/vfs/file.c b/vfs/file.c
index 8aef18f..f73c8e1 100644
--- a/vfs/file.c
+++ b/vfs/file.c
@@ -7,6 +7,7 @@
* generic version.
*/
+#include <errno.h>
#include <nucleus/object.h>
#include <nucleus/vfs.h>
@@ -14,6 +15,7 @@ static void file_delete(Object *);
/* File object type */
ObjectType fileType = {
+ .name = "FILE",
.size = sizeof(File),
.delete = file_delete,
};
@@ -28,3 +30,36 @@ file_delete(Object *obj)
if (file->path)
destroy_list(file->path);
}
+
+/* Open a file */
+int
+file_open(File *file)
+{
+ if (!file->ops || !file->ops->open)
+ return -EINVAL;
+ return file->ops->open(file);
+}
+
+/* Read a file */
+size_t
+file_read(File *file, char *buf, size_t size)
+{
+ size_t count = 0;
+ if (!file->ops || !file->ops->read)
+ return count;
+ count = file->ops->read(file, buf, size, file->pos);
+ file->pos += count;
+ return count;
+}
+
+/* Write a file */
+size_t
+file_write(File *file, char *buf, size_t size)
+{
+ size_t count = 0;
+ if (!file->ops || !file->ops->write)
+ return count;
+ count = file->ops->write(file, buf, size, file->pos);
+ file->pos += count;
+ return count;
+}
diff --git a/vfs/fstype.c b/vfs/fstype.c
index 73d28c8..9043ba4 100644
--- a/vfs/fstype.c
+++ b/vfs/fstype.c
@@ -11,6 +11,7 @@
/* Structure for a File System Type */
struct FSType {
+ Object obj;
const char *name;
Inode *(*mount)(FSType *, int, const char *, void *);
};
@@ -23,6 +24,7 @@ struct FindData {
/* File System Type object type */
ObjectType fstypeType = {
+ .name = "FILE SYSTEM TYPE",
.size = sizeof(FSType),
};
diff --git a/vfs/inode.c b/vfs/inode.c
index 6f49c67..c64542d 100644
--- a/vfs/inode.c
+++ b/vfs/inode.c
@@ -7,8 +7,12 @@
* operation can be called on the particular inode.
*/
+#include <string.h>
+#include <sys/stat.h>
+#include <errno.h>
#include <nucleus/object.h>
#include <nucleus/memory.h>
+#include <nucleus/task.h>
#include <nucleus/vfs.h>
static void inode_new(Object *);
@@ -16,6 +20,7 @@ static void inode_delete(Object *);
/* Inode object type */
ObjectType inodeType = {
+ .name = "INODE",
.size = sizeof(Inode),
.new = inode_new,
.delete = inode_delete,
@@ -26,6 +31,7 @@ static void
inode_new(Object *obj)
{
Inode *inode = (void *) obj;
+ inode->dirEntries = create_list(&dirEntryType);
inode->pages = create_list(&pageType);
}
@@ -36,9 +42,90 @@ inode_delete(Object *obj)
Inode *inode = (void *) obj;
/* Remove inode from the SuperBlock's list */
-// if (inode->super)
+ if (inode->super) {
// super_remove_inode(inode->super, inode);
+ put(inode->super);
+ }
/* Clean cache */
destroy_list(inode->pages);
}
+
+/* Check if a process has permission to access an inode */
+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;
+}
+
+/* Create an inode wrapper */
+int
+inode_create(Inode *inode, DirEntry *entry, mode_t mode)
+{
+ if (!inode->ops || !inode->ops->create)
+ return -EINVAL;
+ lock(inode);
+ int err = inode->ops->create(inode, entry, mode);
+ if (!err) {
+ add(inode->dirEntries, entry);
+ entry->inode = super_alloc_inode(inode->super);
+ entry->inode->mode = mode | S_IFREG;
+ }
+ unlock(inode);
+ return err;
+}
+
+/* Find a named entry in a directory inode */
+DirEntry *
+inode_lookup(Inode *inode, const char *name)
+{
+ if (!S_ISDIR(inode->mode))
+ return NULL;
+ lock(inode);
+
+ /* Check cache first */
+ DirEntry *entry = find_direntry(inode->dirEntries, name);
+ if (entry) {
+ get(entry);
+ if (entry->inode)
+ goto end;
+ }
+ /* Try file system lookup */
+ if (!inode->ops || !inode->ops->lookup) {
+ entry = NULL;
+ goto end;
+ }
+ Inode *child = inode->ops->lookup(inode, name);
+
+ /* The file doesn't exist */
+ if (!child) {
+ if (entry)
+ remove(inode->dirEntries, entry);
+ entry = NULL;
+ goto end;
+ }
+
+ /* Fill in DirEntry */
+ if (!entry) {
+ entry = new(&dirEntryType);
+ strncpy(entry->name, name, NAME_MAX);
+ entry->super = inode->super;
+ add(inode->dirEntries, entry);
+ }
+ entry->inode = get(child);
+
+end:
+ unlock(inode);
+ return entry;
+}
diff --git a/vfs/mount.c b/vfs/mount.c
index 472b30f..53bdab2 100644
--- a/vfs/mount.c
+++ b/vfs/mount.c
@@ -3,7 +3,9 @@
* mount system call.
*/
+#include <string.h>
#include <errno.h>
+#include <nucleus/memory.h>
#include <nucleus/task.h>
#include <nucleus/vfs.h>
#include "namespace.h"
@@ -16,6 +18,13 @@ 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;
+
Inode *mnt = NULL;
int res = mount_fstype(type, flags, src, data, &mnt);
if (res < 0)
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;
+}
diff --git a/vfs/superblock.c b/vfs/superblock.c
index 657ddc7..4d90e3d 100644
--- a/vfs/superblock.c
+++ b/vfs/superblock.c
@@ -17,11 +17,12 @@ struct FindData {
/* Super Block object type */
ObjectType superBlockType = {
+ .name = "SUPER BLOCK",
.size = sizeof(SuperBlock),
};
/* Find an Inode by ino value */
-int
+static int
compare_inode_ino(void *addr, void *data)
{
Inode *inode = addr;