Orion
Barry Importing existing Orion kernel d41a53c (2 years, 9 months ago)diff --git a/vfs/cache.c b/vfs/cache.c
new file mode 100644
index 0000000..54a7495
--- /dev/null
+++ b/vfs/cache.c
@@ -0,0 +1,307 @@
+/*
+ * This file handles the VFS cache. This includes the VFS tree cache and all
+ * the functions for managing the associated structures. One such structure
+ * is the Inode's list of Directory Entries. Every DirEntry has a name, but
+ * also has a hash. This is for fast comparisons - when iterating the list to
+ * search for a name, first check if the hash matches. This saves a lot of time
+ * rather than actually comparing the names. Entries with hash collisions are
+ * placed adjacent in the list.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include "vfs.h"
+#include "inode.h"
+#include "cache.h"
+#include "../mem/heap.h"
+#include "../mem/paging.h"
+#include "../mem/frame.h"
+#include "../task/task.h"
+
+/* Hash a file name */
+static uint32_t
+name_hash(const char *name)
+{
+ uint32_t digest = 5381;
+ int c;
+ while (c = *name++)
+ digest = ((digest << 5) + digest) ^ c; /* digest*33 ^ c */
+ return digest;
+}
+
+/* Reap any unused branches of the cache tree */
+void
+cache_reaper(void)
+{
+ /*
+ * The Cache Reaper attempts to release all the directory entries
+ * belonging to the root of the current task's file system context.
+ * This will in theory release all the associated inodes. If those
+ * inodes are only held in the cache (not in use, and therefore not
+ * referenced by anything else), they'll be freed and will in turn have
+ * their directory entries released. This recursive process will prune
+ * most of the VFS cache, leaving only in-use inodes.
+ */
+ if (!current)
+ return;
+ if (!current->fs)
+ return;
+ if (!current->fs->root)
+ return;
+ entry_clean(current->fs->root);
+ current->fs->root->dirEntries = NULL;
+}
+
+/* Get a DirEntry */
+DirEntry *
+entry_get(DirEntry *entry)
+{
+ entry->usage++;
+ return entry;
+}
+
+/* Put a DirEntry */
+void
+entry_put(DirEntry *entry)
+{
+ if (--entry->usage)
+ return;
+ if (entry->inode)
+ inode_put(entry->inode);
+ kfree(entry);
+}
+
+/* Search for a Directory Entry in a list by name */
+DirEntry *
+entry_find(Inode *inode, const char *name)
+{
+ if (!inode)
+ return NULL;
+ acquire(&inode->lock);
+ uint32_t search = name_hash(name);
+ DirEntry *item = inode->dirEntries;
+ /* Look for matching hash */
+ while (item) {
+ if (item->hash == search)
+ break;
+ item = item->next;
+ }
+ if (!item) {
+ release(&inode->lock);
+ return NULL;
+ }
+ /* Check all collisions */
+ while (item && item->hash == search) {
+ if (!strcmp(item->name, name))
+ break;
+ item = item->next;
+ }
+ release(&inode->lock);
+ if (!item || item->hash != search)
+ return NULL;
+ return item;
+}
+
+/* Add a Directory Entry to a list */
+void
+entry_add(Inode *inode, DirEntry *insert)
+{
+ if (!inode)
+ return;
+ acquire(&inode->lock);
+ insert->hash = name_hash(insert->name);
+ if (!inode->dirEntries) {
+ inode->dirEntries = entry_get(insert);
+ release(&inode->lock);
+ return;
+ }
+
+ /* Try to find a collision */
+ DirEntry *collision = inode->dirEntries;
+ while (collision->next) {
+ if (collision->hash == insert->hash)
+ break;
+ collision = collision->next;
+ }
+ /* Add into list */
+ insert->next = collision->next;
+ collision->next = entry_get(insert);
+ release(&inode->lock);
+}
+
+/* Remove a Directory Entry from a list */
+void
+entry_remove(Inode *inode, const char *name)
+{
+ if (!inode)
+ return;
+ acquire(&inode->lock);
+ uint32_t search = name_hash(name);
+ DirEntry *item = inode->dirEntries, *prev = NULL;
+ /* Look for matching hash */
+ while (item) {
+ if (item->hash == search)
+ break;
+ prev = item;
+ item = item->next;
+ }
+ if (!item) {
+ release(&inode->lock);
+ return;
+ }
+ /* Check all collisions */
+ while (item && item->hash == search) {
+ if (!strcmp(item->name, name))
+ break;
+ prev = item;
+ item = item->next;
+ }
+ if (!item || item->hash != search) {
+ release(&inode->lock);
+ return;
+ }
+
+ /* Link over item */
+ if (prev)
+ prev->next = item->next;
+ else
+ inode->dirEntries = item;
+ entry_put(item);
+ release(&inode->lock);
+}
+
+/* Clean a list of Directory Entries */
+void
+entry_clean(Inode *inode)
+{
+ if (!inode)
+ return;
+ if (!inode->dirEntries)
+ return;
+ acquire(&inode->lock);
+ DirEntry *de, *den;
+ for (de = inode->dirEntries; de; de = den) {
+ den = de->next;
+ entry_put(de);
+ }
+ release(&inode->lock);
+}
+
+/* Get a Page */
+Page *
+page_get(Page *page)
+{
+ page->usage++;
+ return page;
+}
+
+/* Put a Page */
+void
+page_put(Page *page)
+{
+ if (--page->usage)
+ return;
+ free_frame(PG_ADDR(page->frame));
+ kfree(page);
+}
+
+/* Find a Page by offset */
+Page *
+page_find(Inode *inode, off_t offset)
+{
+ if (!inode)
+ return NULL;
+ acquire(&inode->lock);
+ PageCache *cache;
+ for (cache = inode->pages; cache; cache = cache->next)
+ if (cache->page->offset == PG_ADDR(offset))
+ break;
+ release(&inode->lock);
+ if (!cache)
+ return NULL;
+ return cache->page;
+}
+
+/* Add a page to a list */
+void
+page_add(Inode *inode, Page *page)
+{
+ if (!inode)
+ return;
+ acquire(&inode->lock);
+ PageCache *cache;
+ if (!inode->pages) {
+ inode->pages = kmalloc(sizeof(PageCache));
+ cache = inode->pages;
+ } else {
+ for (cache = inode->pages; cache->next; cache = cache->next);
+ cache->next = kmalloc(sizeof(PageCache));
+ cache = cache->next;
+ }
+ cache->page = page_get(page);
+ release(&inode->lock);
+}
+
+/* Create and add a Page to a list */
+Page *
+page_create(Inode *inode, page_t frame, off_t offset)
+{
+ if (!inode)
+ return NULL;
+ acquire(&inode->lock);
+ Page *prev, *page = kmalloc(sizeof(Page));
+ page->frame = PG_ADDR(frame);
+ page->offset = PG_ADDR(offset);
+ page_add(inode, page);
+ release(&inode->lock);
+ return page;
+}
+
+/* Remove a Page from a list */
+void
+page_remove(Inode *inode, Page *page)
+{
+ if (!inode)
+ return;
+ if (!inode->pages)
+ return;
+ acquire(&inode->lock);
+
+ PageCache *cache, *item;
+ if (inode->pages->page == page) {
+ item = inode->pages;
+ inode->pages = item->next;
+ } else {
+ for (cache = inode->pages;
+ cache->next; cache = cache->next)
+ if (cache->next->page == page)
+ break;
+ if (!cache->next) {
+ release(&inode->lock);
+ return;
+ }
+ item = cache->next;
+ cache->next = item->next;
+ }
+ page_put(page);
+ kfree(item);
+ release(&inode->lock);
+}
+
+/* Clean a list of Pages */
+void
+page_clean(Inode *inode)
+{
+ if (!inode)
+ return;
+ if (!inode->pages)
+ return;
+ acquire(&inode->lock);
+ PageCache *c, *cn;
+ for (c = inode->pages; c; c = cn) {
+ page_put(c->page);
+ cn = c->next;
+ kfree(c);
+ }
+ release(&inode->lock);
+}
diff --git a/vfs/cache.h b/vfs/cache.h
new file mode 100644
index 0000000..158d2ab
--- /dev/null
+++ b/vfs/cache.h
@@ -0,0 +1,22 @@
+#ifndef KERNEL_VFS_CACHE_H
+#define KERNEL_VFS_CACHE_H
+
+#include "vfs.h"
+
+void cache_reaper(void);
+
+DirEntry *entry_get(DirEntry *entry);
+void entry_put(DirEntry *entry);
+DirEntry *entry_find(Inode *inode, const char *name);
+void entry_add(Inode *inode, DirEntry *insert);
+void entry_remove(Inode *inode, const char *name);
+void entry_clean(Inode *inode);
+
+Page *page_get(Page *page);
+Page *page_find(Inode *inode, off_t offset);
+void page_add(Inode *inode, Page *page);
+Page *page_create(Inode *inode, page_t frame, off_t offset);
+void page_remove(Inode *inode, Page *page);
+void page_clean(Inode *inode);
+
+#endif
diff --git a/vfs/custody.c b/vfs/custody.c
new file mode 100644
index 0000000..1200f59
--- /dev/null
+++ b/vfs/custody.c
@@ -0,0 +1,117 @@
+/*
+ * This file handles chains of custody for files. It implements a generic set
+ * of operations for acting on CustodyChain objects. A CustodyChain holds a
+ * linked list of Custody objects, each of which refers to a directory entry.
+ * With this chain, the VFS is able to find what path was used to access a
+ * resources and go back up the chain (for implementing ..) and avoid going
+ * beyond the root.
+ */
+
+#include <string.h>
+#include "vfs.h"
+#include "cache.h"
+#include "../mem/heap.h"
+
+/* Initialise a custody chain */
+void
+init_custody_chain(CustodyChain *chain)
+{
+ Custody *root = kmalloc(sizeof(Custody));
+ chain->start = chain->end = root;
+ chain->size = 0;
+ init_lock(&chain->lock);
+ root->prev = root->next = NULL;
+ root->chain = chain;
+ root->entry = NULL;
+}
+
+/* Create a new custody chain */
+CustodyChain *
+create_custody_chain(void)
+{
+ CustodyChain *chain = kmalloc(sizeof(CustodyChain));
+ init_custody_chain(chain);
+ return chain;
+}
+
+/* Clean a custody chain */
+void
+clean_custody_chain(CustodyChain *chain)
+{
+ acquire(&chain->lock);
+ while (chain->size)
+ remove_custody(chain);
+ kfree(chain->start);
+ release(&chain->lock);
+}
+
+/* Destroy a custody chain */
+void
+destroy_custody_chain(CustodyChain *chain)
+{
+ clean_custody_chain(chain);
+ kfree(chain);
+}
+
+/* Copy custody chain */
+void
+copy_custody_chain(CustodyChain *chain, CustodyChain *newchain)
+{
+ acquire(&chain->lock);
+ acquire(&newchain->lock);
+ Custody *c;
+ for (c = chain->start->next; c; c = c->next)
+ add_custody(newchain, c->entry);
+ release(&newchain->lock);
+ release(&chain->lock);
+}
+
+/* Add entry */
+void
+add_custody(CustodyChain *chain, DirEntry *entry)
+{
+ acquire(&chain->lock);
+ chain->end->next = kmalloc(sizeof(Custody));
+ chain->end->next->prev= chain->end;
+ chain->end = chain->end->next;
+ chain->end->chain = chain;
+ chain->end->entry = entry_get(entry);
+ chain->size++;
+ release(&chain->lock);
+}
+
+/* Remove an entry */
+DirEntry *
+remove_custody(CustodyChain *chain)
+{
+ if (!chain->size)
+ return NULL;
+ acquire(&chain->lock);
+ entry_put(chain->end->entry);
+ chain->end = chain->end->prev;
+ kfree(chain->end->next);
+ chain->end->next = NULL;
+ chain->size--;
+ release(&chain->lock);
+ return chain->end->entry;
+}
+
+/* Convert a chain to a path string */
+char *
+custody_path(CustodyChain *chain, char *buf, size_t size)
+{
+ char *ptr = buf;
+ Custody *curr;
+ if (!chain->start->next) {
+ *ptr++ = '/';
+ *ptr = '\0';
+ return buf;
+ }
+ for (curr = chain->start->next; curr; curr = curr->next) {
+ *ptr++ = '/';
+ strcpy(ptr, curr->entry->name);
+ ptr += strlen(curr->entry->name);
+ }
+ *ptr = '\0';
+ return buf;
+}
diff --git a/vfs/devfs/file.c b/vfs/devfs/file.c
new file mode 100644
index 0000000..d2da4e4
--- /dev/null
+++ b/vfs/devfs/file.c
@@ -0,0 +1,84 @@
+/*
+ * This file controls access to DevFS Files. It contains the functions called
+ * by the VFS for any operation on a File struct belonging to DevFS.
+ */
+
+#include <stddef.h>
+#include <string.h>
+#include <errno.h>
+#include <dirent.h>
+#include "fs.h"
+#include "../vfs.h"
+#include "../cache.h"
+#include "../../mem/paging.h"
+#include "../../mem/frame.h"
+#include "../../drivers/drivers.h"
+
+int devfs_open(File *file);
+int devfs_readdir(File *file, DirEnt *dent, off_t index);
+
+FileOps devfsFileOps = {
+ .open = devfs_open,
+ .readdir = devfs_readdir,
+};
+
+/* Open a device node */
+int
+devfs_open(File *file)
+{
+ if (S_ISREG(file->inode->mode) || S_ISDIR(file->inode->mode))
+ return 0;
+
+ /* Use major number to find relevant driver */
+ Driver *driver;
+ for (driver = drivers; driver; driver = driver->next)
+ if (driver->major == MAJOR(file->inode->dev))
+ break;
+ if (!driver)
+ return -ENXIO;
+
+ file->ops = driver->ops;
+ if (!file->ops)
+ return 0;
+ if (!file->ops->open)
+ return 0;
+ return file->ops->open(file);
+ /* For checking the minor internally */
+}
+
+/* Read a directory entry */
+int
+devfs_readdir(File *file, DirEnt *dent, off_t index)
+{
+ DirEntry *de;
+
+ if (!index--) {
+ dent->ino = file->inode->ino;
+ dent->type = DT_DIR;
+ dent->namelen = 2;
+ strncpy(dent->name, ".", dent->namelen);
+ return 0;
+ }
+
+ for (de = file->inode->dirEntries; de && index; de = de->next, index--);
+ if (!de)
+ return -ENOENT;
+ dent->ino = de->inode->ino;
+ if (S_ISBLK(de->inode->mode))
+ dent->type = DT_BLK;
+ if (S_ISCHR(de->inode->mode))
+ dent->type = DT_CHR;
+ if (S_ISDIR(de->inode->mode))
+ dent->type = DT_DIR;
+ if (S_ISFIFO(de->inode->mode))
+ dent->type = DT_FIFO;
+ if (S_ISLNK(de->inode->mode))
+ dent->type = DT_LNK;
+ if (S_ISREG(de->inode->mode))
+ dent->type = DT_REG;
+ if (S_ISSOCK(de->inode->mode))
+ dent->type = DT_SOCK;
+ dent->namelen = strnlen(de->name, NAME_MAX) + 1;
+ strncpy(dent->name, de->name, NAME_MAX);
+ return 0;
+}
diff --git a/vfs/devfs/fs.h b/vfs/devfs/fs.h
new file mode 100644
index 0000000..f972b67
--- /dev/null
+++ b/vfs/devfs/fs.h
@@ -0,0 +1,13 @@
+#ifndef KERNEL_VFS_DEVFS_H
+#define KERNEL_VFS_DEVFS_H
+
+#include "../vfs.h"
+
+/* Operations */
+extern SuperOps devfsSuperOps;
+extern InodeOps devfsInodeOps;
+extern FileOps devfsFileOps;
+
+extern FileSystemType devfsType;
+
+#endif
diff --git a/vfs/devfs/inode.c b/vfs/devfs/inode.c
new file mode 100644
index 0000000..2208263
--- /dev/null
+++ b/vfs/devfs/inode.c
@@ -0,0 +1,69 @@
+/*
+ * This file contains the functions dealing with DevFS inodes. The VFS will
+ * call these when it performs operations on DevFS Inodes, or is dealing with
+ * the DevFS hierarchy.
+ */
+
+#include <string.h>
+#include "fs.h"
+#include "../vfs.h"
+#include "../../mem/heap.h"
+
+int devfs_create(Inode *inode, DirEntry *entry, mode_t mode);
+Inode *devfs_lookup(Inode *inode, const char *name);
+int devfs_mkdir(Inode *inode, DirEntry *entry, mode_t mode);
+int devfs_rmdir(Inode *inode, DirEntry *entry);
+int devfs_mknod(Inode *inode, DirEntry *entry, mode_t mode, dev_t dev);
+int devfs_rename(Inode *si, DirEntry *sde, Inode *di, DirEntry *dde);
+
+InodeOps devfsInodeOps = {
+ .create = devfs_create,
+ .lookup = devfs_lookup,
+ .mkdir = devfs_mkdir,
+ .rmdir = devfs_rmdir,
+ .mknod = devfs_mknod,
+ .rename = devfs_rename,
+};
+
+/* Create a file */
+int
+devfs_create(Inode *inode, DirEntry *entry, mode_t mode)
+{
+ return 0;
+}
+
+
+/* Look up a file */
+Inode *
+devfs_lookup(Inode *inode, const char *name)
+{
+ return NULL;
+}
+
+/* Make a directory */
+int
+devfs_mkdir(Inode *inode, DirEntry *entry, mode_t mode)
+{
+ return 0;
+}
+
+/* Remove a directory */
+int
+devfs_rmdir(Inode *inode, DirEntry *entry)
+{
+ return 0;
+}
+
+/* Make a node */
+int
+devfs_mknod(Inode *inode, DirEntry *entry, mode_t mode, dev_t dev)
+{
+ return 0;
+}
+
+/* Rename/mode a directory entry */
+int
+devfs_rename(Inode *si, DirEntry *sde, Inode *di, DirEntry *dde)
+{
+ return 0;
+}
diff --git a/vfs/devfs/super.c b/vfs/devfs/super.c
new file mode 100644
index 0000000..e57d60b
--- /dev/null
+++ b/vfs/devfs/super.c
@@ -0,0 +1,85 @@
+/*
+ * This file controls the superblock for DevFS. It supports mounting new DevFS
+ * filesystems. The VFS will use the calls here when dealing directly with the
+ * filesystem structure.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "fs.h"
+#include "../vfs.h"
+#include "../super.h"
+#include "../inode.h"
+#include "../../mem/heap.h"
+
+Inode *devfs_mount(FileSystemType *type, int flags,
+ const char *dev, void *data);
+Inode *devfs_alloc_inode(SuperBlock *sb);
+
+FileSystemType devfsType = {
+ .name = "DevFS",
+ .mount = devfs_mount,
+// .kill_sb = devfs_kill_sb,
+};
+
+SuperOps devfsSuperOps = {
+ .alloc_inode = devfs_alloc_inode,
+// .free_inode = devfs_free_inode,
+// .write_inode = devfs_write_inode,
+// .delete_inode = devfs_delete_inode,
+};
+
+Inode devfsRoot = {
+ .mode = S_IFDIR | 0755,
+ .ops = &devfsInodeOps,
+ .fileOps = &devfsFileOps,
+ .size = 4096,
+};
+
+/* Mount a DevFS instance */
+Inode *
+devfs_mount(FileSystemType *type, int flags, const char *dev, void *data)
+{
+ if (type != &devfsType)
+ return NULL;
+
+ SuperBlock *super = kmalloc(sizeof(SuperBlock));
+
+ super->type = type;
+ super->ops = &devfsSuperOps;
+ init_lock(&super->lock);
+
+ /*
+ * DevFS is special - it has a globally unique root. This means that no
+ * matter where a DevFS instance is mounted, it will be mounted with
+ * this inode, meaning the contents are the same. It basically causes
+ * the mountpoint to act as a hard-link. This means you can mount a new
+ * DevFS instance in a chroot() environment, and not have to remake all
+ * the device nodes.
+ */
+ Inode *inode = &devfsRoot;
+ inode->nlink++;
+ inode->super = super;
+ /* FIXME: need an inode per super, or only one super */
+ super->root = inode;
+ /* Never free */
+ if (!inode->usage)
+ inode_get(inode);
+
+ return inode;
+}
+
+/* Allocate an inode */
+Inode *
+devfs_alloc_inode(SuperBlock *sb)
+{
+ Inode *inode = kmalloc(sizeof(Inode));
+ init_lock(&inode->lock);
+ inode->ops = &devfsInodeOps;
+ inode->fileOps = &devfsFileOps;
+ inode->super = sb;
+ inode->nlink = 1;
+ return inode_get(inode); /* This ensures that the inode is never free */
+}
diff --git a/vfs/ext2fs/block.c b/vfs/ext2fs/block.c
new file mode 100644
index 0000000..28cf480
--- /dev/null
+++ b/vfs/ext2fs/block.c
@@ -0,0 +1,32 @@
+/*
+ * This file contains the Ext2 block implementation. It reads a backing file
+ * from the super block in the intervals specified by the superblock. It also
+ * contains functions for reading blocks from files.
+ */
+
+#include <stdint.h>
+#include "fs.h"
+
+/* Read a block of data */
+void
+ext2_read_block(SuperBlock *super, uint32_t index, char *buf)
+{
+ Ext2Super *rsuper = super->data;
+
+ super->back->pos = index * (1024 << rsuper->blockSize);
+ file_read(super->back, buf, 1024 << rsuper->blockSize);
+}
+
+/* Get the data block address from inode block index */
+uint32_t
+ext2_get_data_addr(SuperBlock *super, Ext2Inode *node, uint32_t index)
+{
+ uint32_t tmp;
+ char block[4096];
+
+ /* Main blocks */
+ if (index < 12)
+ return node->directBlock[index];
+ index -= 12;
+ return 0;
+}
diff --git a/vfs/ext2fs/file.c b/vfs/ext2fs/file.c
new file mode 100644
index 0000000..d0caa35
--- /dev/null
+++ b/vfs/ext2fs/file.c
@@ -0,0 +1,86 @@
+/*
+ * This file controls access to Ext2FS Files. It contains the functions called
+ * by the VFS for any operation on a Ext2FS File struct.
+ */
+
+#include <stddef.h>
+#include <string.h>
+#include <dirent.h>
+#include <errno.h>
+#include "fs.h"
+#include "../vfs.h"
+
+size_t ext2fs_read(File *file, char *buf, size_t size, off_t offset);
+int ext2fs_readdir(File *file, DirEnt *dent, off_t index);
+int ext2fs_open(File *file);
+
+FileOps ext2fsFileOps = {
+ .read = ext2fs_read,
+ .readdir = ext2fs_readdir,
+ .open = ext2fs_open,
+};
+
+/* Read a file */
+size_t
+ext2fs_read(File *file, char *buf, size_t size, off_t offset)
+{
+ Ext2Inode inode;
+ uint16_t min;
+ size_t count = 0, i = offset / 4096;
+ uint32_t blk;
+ char ebuf[4096];
+ ext2_read_inode(file->inode->super, file->inode->ino, &inode);
+ if (offset > inode.lsize)
+ return 0;
+ if (size + offset > inode.lsize)
+ size = inode.lsize - offset;
+ while (size) {
+ min = (size > 0x1000) ? 0x1000 : size;
+ blk = ext2_get_data_addr(file->inode->super, &inode, i);
+ ext2_read_block(file->inode->super, blk, ebuf);
+ memcpy(buf + count, ebuf + (offset % 4096), min);
+ size -= min;
+ count += min;
+ i++;
+ }
+ if (count >= 0x1000)
+ return count;
+}
+
+/* Read a directory entry */
+int
+ext2fs_readdir(File *file, DirEnt *dent, off_t index)
+{
+ char buf[4096];
+ uint32_t block, blk = 0;
+ Ext2DirEntry *de;
+ Ext2Inode inode;
+ ext2_read_inode(file->inode->super, file->inode->ino, &inode);
+ for (blk = 0; blk < 0xFFFF; blk++) {
+ block = ext2_get_data_addr(file->inode->super, &inode, blk);
+ if (!block)
+ return -ENOENT;
+ ext2_read_block(file->inode->super, block, buf);
+ for (de = (Ext2DirEntry *) buf;
+ index && de < (Ext2DirEntry *) (buf + 4096);
+ de = (void *) ((char *) de + de->size), index--);
+ if (de >= (Ext2DirEntry *) (buf + 4096))
+ return -ENOENT;
+ if (!index)
+ break;
+ }
+ if (!de->ino)
+ return -ENOENT;
+ dent->ino = de->ino;
+ dent->type = de->type;
+ dent->namelen = de->nameLen + 1;
+ strncpy(dent->name, de->name, de->size);
+ return 0;
+}
+
+/* Open a file */
+int
+ext2fs_open(File *file)
+{
+ return 0;
+}
diff --git a/vfs/ext2fs/fs.h b/vfs/ext2fs/fs.h
new file mode 100644
index 0000000..0e5cadb
--- /dev/null
+++ b/vfs/ext2fs/fs.h
@@ -0,0 +1,116 @@
+#ifndef KERNEL_VFS_EXT2FS_H
+#define KERNEL_VFS_EXT2FS_H
+
+#include "../vfs.h"
+
+typedef struct Ext2Super Ext2Super;
+typedef struct Ext2BlockGroupDesc Ext2BlockGroupDesc;
+typedef struct Ext2Inode Ext2Inode;
+typedef struct Ext2DirEntry Ext2DirEntry;
+
+/* Structure of the Ext2 SuperBlock */
+struct Ext2Super {
+ uint32_t numInodes;
+ uint32_t numBlocks;
+ uint32_t reservedBlocks;
+ uint32_t unallocBlocks;
+ uint32_t unallocInodes;
+ uint32_t superBlock;
+ uint32_t blockSize;
+ uint32_t fragSize;
+ uint32_t blocksPerGroup;
+ uint32_t fragsPerGroup;
+ uint32_t inodesPerGroup;
+ uint32_t lastMountTime;
+ uint32_t lastWriteTime;
+ uint16_t lastCheck;
+ uint16_t mustCheck;
+ uint16_t signature;
+ uint16_t state;
+ uint16_t error;
+ uint16_t verMinor;
+ uint32_t lastCheckTime;
+ uint32_t checkInterval;
+ uint32_t creator;
+ uint32_t verMajor;
+ uint16_t uid, gid;
+ /* Extended fields */
+ uint32_t firstAvailInode;
+ uint16_t inodeSize;
+ uint16_t blockGroup;
+ uint32_t optionalFeatures;
+ uint32_t requiredFeatures;
+ uint32_t writableFeatures;
+ char fsId[16];
+ char volumeName[16];
+ char lastPath[64];
+ uint32_t compression;
+ uint8_t preallocFileBlocks;
+ uint8_t preallocDirBlocks;
+ uint16_t unused;
+ char journalId[16];
+ uint32_t journalInode;
+ uint32_t journalDev;
+ uint32_t orphanHead;
+} __attribute__((packed));
+
+/* Structure of the Ext2 Block Group Descriptor */
+struct Ext2BlockGroupDesc {
+ uint32_t blockUsage;
+ uint32_t inodeUsage;
+ uint32_t inodeTable;
+ uint16_t unallocBlocks;
+ uint16_t unallocInodes;
+ uint16_t numDirs;
+} __attribute__((packed));
+
+/* Structure of the Ext2 Inode */
+struct Ext2Inode {
+ uint16_t type;
+ uint16_t uid;
+ uint32_t lsize;
+ uint32_t lastAccessTime;
+ uint32_t creationTime;
+ uint32_t lastWriteTime;
+ uint32_t deletionTime;
+ uint16_t gid;
+ uint16_t numHardLinks;
+ uint32_t numSectors;
+ uint32_t flags;
+ uint32_t osA;
+ uint32_t directBlock[12];
+ uint32_t singleBlock;
+ uint32_t doubleBlock;
+ uint32_t tripleBlock;
+ uint32_t gen;
+ uint32_t attr;
+ union {
+ uint32_t usize;
+ uint32_t dirACL;
+ };
+ uint32_t fragment;
+ uint32_t osB[3];
+} __attribute__((packed));
+
+/* Structure of the Ext2 Directory Entry */
+struct Ext2DirEntry {
+ uint32_t ino;
+ uint16_t size;
+ uint8_t nameLen;
+ uint8_t type;
+ char name[];
+} __attribute__((packed));
+
+/* Operations */
+extern SuperOps ext2fsSuperOps;
+extern InodeOps ext2fsInodeOps;
+extern FileOps ext2fsFileOps;
+
+extern FileSystemType ext2fsType;
+
+void ext2_read_vnode(SuperBlock *super, uint32_t index, Inode *res);
+void ext2_read_inode(SuperBlock *super, uint32_t index, Ext2Inode *res);
+void ext2_read_block(SuperBlock *super, uint32_t index, char *buf);
+uint32_t ext2_get_data_addr(SuperBlock *super, Ext2Inode *node, uint32_t index);
+
+#endif
diff --git a/vfs/ext2fs/inode.c b/vfs/ext2fs/inode.c
new file mode 100644
index 0000000..3437220
--- /dev/null
+++ b/vfs/ext2fs/inode.c
@@ -0,0 +1,94 @@
+/*
+ * This file contains the Ext2 inode implementation. It contains the functions
+ * for the inode operations, as well as several internal operations relating to
+ * inodes required for the full Ext2 implementation.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <fcntl.h>
+#include "fs.h"
+#include "../vfs.h"
+#include "../super.h"
+#include "../../mem/heap.h"
+
+Inode *ext2fs_lookup(Inode *inode, const char *name);
+
+InodeOps ext2fsInodeOps = {
+ .lookup = ext2fs_lookup,
+};
+
+/* Read an Ext2 inode as a VFS inode */
+void
+ext2_read_vnode(SuperBlock *super, uint32_t index, Inode *res)
+{
+ Ext2Inode inode;
+ ext2_read_inode(super, index, &inode);
+
+ res->ino = index;
+ res->usage = 0;
+ res->uid = inode.uid;
+ res->gid = inode.gid;
+ res->mode = inode.type;
+ res->nlink = inode.numHardLinks;
+ res->size = inode.lsize;
+ res->ops = &ext2fsInodeOps;
+ res->fileOps = &ext2fsFileOps;
+ res->super = super;
+}
+
+/* Read an Ext2 inode */
+void
+ext2_read_inode(SuperBlock *super, uint32_t index, Ext2Inode *res)
+{
+ Ext2Super *rsuper = super->data;
+
+ /* Find the Block Group Descriptor */
+ Ext2BlockGroupDesc bgd;
+ uint32_t groups = rsuper->numBlocks / rsuper->blocksPerGroup;
+ uint32_t group = (index - 1) / rsuper->inodesPerGroup;
+ index = (index - 1) % rsuper->inodesPerGroup;
+ if (rsuper->numBlocks % rsuper->blocksPerGroup)
+ groups++;
+ super->back->pos = (rsuper->blockSize ? 1 : 2)
+ * (1024 << rsuper->blockSize);
+ super->back->pos += sizeof(Ext2BlockGroupDesc) * group;
+ file_read(super->back, (char *) &bgd, sizeof(Ext2BlockGroupDesc));
+ /* Read Inode */
+ super->back->pos = bgd.inodeTable * (1024 << rsuper->blockSize);
+ super->back->pos += rsuper->inodeSize * index;
+ file_read(super->back, (char *) res, sizeof(Ext2Inode));
+}
+
+/* Look up a file */
+Inode *
+ext2fs_lookup(Inode *vnode, const char *name)
+{
+ char buf[4096];
+ uint32_t block, blk = 0;
+ Ext2DirEntry *de;
+ Ext2Inode inode;
+ ext2_read_inode(vnode->super, vnode->ino, &inode);
+ for (blk = 0; blk < 0xFFFF; blk++) {
+ block = ext2_get_data_addr(vnode->super, &inode, blk);
+ if (!block)
+ return NULL;
+ ext2_read_block(vnode->super, block, buf);
+ for (de = (Ext2DirEntry *) buf;
+ strncmp(de->name, name, de->nameLen)
+ && de < (Ext2DirEntry *) (buf + 4096);
+ de = (void *) ((char *) de + de->size));
+ if (de >= (Ext2DirEntry *) (buf + 4096))
+ return NULL;
+ if (!strncmp(de->name, name, de->nameLen))
+ break;
+ }
+ if (!de->ino)
+ return NULL;
+ Inode *res = super_find_inode(vnode->super, de->ino);
+ if (res)
+ return res;
+ res = kmalloc(sizeof(Inode));
+ ext2_read_vnode(vnode->super, de->ino, res);
+ return res;
+}
diff --git a/vfs/ext2fs/super.c b/vfs/ext2fs/super.c
new file mode 100644
index 0000000..98ca800
--- /dev/null
+++ b/vfs/ext2fs/super.c
@@ -0,0 +1,89 @@
+/*
+ * This file controls the superblock for Ext2FS. It supports mounting new
+ * Ext2FS filesystems. The VFS will use the calls here when dealing directly
+ * with the filesystem structure.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/mount.h>
+#include "fs.h"
+#include "../vfs.h"
+#include "../inode.h"
+#include "../super.h"
+#include "../../mem/heap.h"
+#include "../../task/task.h"
+#include "../../screen.h"
+
+Inode *ext2fs_mount(FileSystemType *type, int flags,
+ const char *dev, void *data);
+Inode *ext2fs_alloc_inode(SuperBlock *sb);
+
+FileSystemType ext2fsType = {
+ .name = "Ext2FS",
+ .mount = ext2fs_mount,
+};
+
+SuperOps ext2fsSuperOps = {
+ .alloc_inode = ext2fs_alloc_inode,
+};
+
+/* Mount a Ext2FS instance */
+Inode *
+ext2fs_mount(FileSystemType *type, int flags, const char *dev, void *data)
+{
+ if (type != &ext2fsType)
+ return NULL;
+
+ int fd = open(dev, O_RDONLY);
+ if (fd < 0)
+ return (void *) fd;
+ File *back = file_get(current->files->fd[fd]);
+ close(fd);
+ if (!S_ISBLK(back->mode)) {
+ file_put(back);
+ return (void *) -ENOTBLK;
+ }
+
+ Ext2Super *rsuper = kmalloc(sizeof(Ext2Super));
+ back->pos = 1024;
+ file_read(back, (char *) rsuper, sizeof(Ext2Super));
+ if (rsuper->signature != 0xEF53) {
+ kprintf("Error while mounting %s: Not an Ext2FS drive", dev);
+ file_put(back);
+ kfree(rsuper);
+ return (void *) -EINVAL;
+ }
+ if (rsuper->requiredFeatures & 0x01) {
+ kprintf("Cannot mount %s: Compression required", dev);
+ file_put(back);
+ kfree(rsuper);
+ return (void *) -EINVAL;
+ }
+ if ((rsuper->writableFeatures & 0x02) && !(flags & MS_RDONLY)) {
+ kprintf("Cannot mount %s: 64-bit File System required", dev);
+ file_put(back);
+ kfree(rsuper);
+ return (void *) -EINVAL;
+ }
+
+ SuperBlock *super = kmalloc(sizeof(SuperBlock));
+ super->type = type;
+ super->ops = &ext2fsSuperOps;
+ init_lock(&super->lock);
+ super->back = back;
+ super->data = rsuper;
+ super->root = kmalloc(sizeof(Inode));
+ ext2_read_vnode(super, 2, super->root);
+
+ return super->root;
+}
+
+/* Allocate an inode */
+Inode *
+ext2fs_alloc_inode(SuperBlock *sb)
+{
+ return NULL;
+}
diff --git a/vfs/file.c b/vfs/file.c
new file mode 100644
index 0000000..d3dfdc6
--- /dev/null
+++ b/vfs/file.c
@@ -0,0 +1,181 @@
+/*
+ * This file handles the file operations. Most of the functions here are just
+ * wrapper around the file operations. They generally check if the operation
+ * exists and use it if it does. If it doesn't they may implement a generic
+ * action, or just simply return. They also perform the necessary validation to
+ * ensure the operation can be called on the particular file.
+ */
+
+#include <string.h>
+#include <errno.h>
+#include "vfs.h"
+#include "inode.h"
+#include "cache.h"
+#include "../mem/heap.h"
+#include "../mem/frame.h"
+#include "../screen.h"
+
+/* Get a File */
+File *
+file_get(File *file)
+{
+ file->usage++;
+ return file;
+}
+
+/* Put a File */
+void
+file_put(File *file)
+{
+ ASSERT(file->usage);
+ if (--file->usage)
+ return;
+ if (file->inode)
+ inode_put(file->inode);
+ if (file->path)
+ destroy_custody_chain(file->path);
+ kfree(file);
+}
+
+/* Read a file */
+size_t
+file_read(File *file, char *buf, size_t size)
+{
+ size_t count = 0;
+ if (!S_ISREG(file->inode->mode)) {
+ if (!file->ops)
+ return count;
+ if (!file->ops->read)
+ return count;
+ count = file->ops->read(file, buf, size, file->pos);
+ file->pos += count;
+ return count;
+ }
+ if (file->pos > file->inode->size)
+ return count;
+ if (size + file->pos > file->inode->size)
+ size = file->inode->size - file->pos;
+
+ uint16_t min, offset = file->pos & 0xFFF;
+ Page *page;
+ page_t oldPage;
+ acquire(&file->lock);
+ while (size) {
+ min = (size > 0x1000) ? 0x1000 : size;
+ page = page_find(file->inode, file->pos + (count & ~0xFFF));
+ if (!page) {
+ if (!file->ops)
+ goto end;
+ if (!file->ops->read)
+ goto end;
+ file->ops->read(file, buf + count, min,
+ file->pos + count);
+ } else {
+ acquire(&quickPageLock);
+ oldPage = quick_page(page->frame);
+ memcpy(buf + count, QUICK_PAGE + offset, min);
+ quick_page(oldPage);
+ release(&quickPageLock);
+ }
+ offset = 0;
+end:
+ size -= min;
+ count += min;
+ }
+ file->pos += count;
+ release(&file->lock);
+ return count;
+}
+
+/* Write a file */
+size_t
+file_write(File *file, char *buf, size_t size)
+{
+ size_t count = 0;
+ if (!S_ISREG(file->inode->mode)) {
+ if (!file->ops)
+ return count;
+ if (!file->ops->write)
+ return count;
+ count = file->ops->write(file, buf, size, file->pos);
+ file->pos += count;
+ return count;
+ }
+ if (size + file->pos > file->inode->size)
+ file->inode->size = size + file->pos;
+
+ uint16_t min, offset = file->pos & 0xFFF;
+ Page *page;
+ page_t oldPage;
+ acquire(&file->lock);
+ while (size) {
+ min = (size > 0x1000) ? 0x1000 : size;
+ page = page_find(file->inode, file->pos + (count & ~0xFFF));
+ if (!page) {
+ if (!file->ops)
+ goto end;
+ if (!file->ops->write)
+ goto end;
+ file->ops->write(file, buf + count, min,
+ file->pos + count);
+ } else {
+ acquire(&quickPageLock);
+ oldPage = quick_page(page->frame);
+ memcpy(QUICK_PAGE + offset, buf + count, min);
+ quick_page(oldPage);
+ release(&quickPageLock);
+ }
+ offset = 0;
+end:
+ size -= min;
+ count += min;
+ }
+ file->pos += count;
+ release(&file->lock);
+ return count;
+}
+
+/* I/O Control */
+int
+file_ioctl(File *file, unsigned long request, uintptr_t argp)
+{
+ if (!file->ops)
+ return -EINVAL;
+ if (!file->ops->ioctl)
+ return -ENOTTY;
+ return file->ops->ioctl(file, request, argp);
+}
+
+/* Read a directory entry (DirEnt) from a directory */
+int
+file_readdir(File *file, DirEnt *dent, off_t index)
+{
+ if (!file->ops)
+ return -EINVAL;
+ if (!file->ops->readdir)
+ return -EINVAL;
+ return file->ops->readdir(file, dent, index);
+}
+
+/* Open a file */
+int
+file_open(File *file)
+{
+ if (!file->ops)
+ return -EINVAL;
+ if (!file->ops->open)
+ return -EINVAL;
+ return file->ops->open(file);
+}
+
+/* Map a file into memory */
+void
+file_mmap(File *file, void *addr, size_t len, off_t offset)
+{
+ if (!file->ops)
+ return;
+ if (file->ops->mmap)
+ return file->ops->mmap(file, addr, len, offset);
+ if (S_ISREG(file->mode))
+ file->ops->read(file, addr, len, offset);
+}
diff --git a/vfs/inode.c b/vfs/inode.c
new file mode 100644
index 0000000..34b5c65
--- /dev/null
+++ b/vfs/inode.c
@@ -0,0 +1,204 @@
+/*
+ * This file deals with inode operations. Most of the functions here are just
+ * wrappers for the inode operations. They generally check if the operation
+ * exists and use it if it does. If it doesn't they may implement a generic
+ * action, or just simply return. They also perform the necessary validation to
+ * ensure the operation can be called on the particular inode.
+ */
+
+#include <string.h>
+#include <errno.h>
+#include "vfs.h"
+#include "cache.h"
+#include "inode.h"
+#include "super.h"
+#include "../mem/heap.h"
+#include "../screen.h"
+
+/* Get an Inode */
+Inode *
+inode_get(Inode *inode)
+{
+ /* Add the inode to the Inode List */
+ if (!inode->usage && inode->super) {
+ if (!inode->super->inodes) {
+ inode->super->inodes = inode;
+ } else {
+ inode->lnext = inode->super->inodes;
+ inode->super->inodes = inode;
+ }
+ inode->super->cachedInodes++;
+ }
+
+ inode->usage++;
+ return inode;
+}
+
+/* Put an Inode */
+void
+inode_put(Inode *inode)
+{
+ ASSERT(inode->usage);
+ if (--inode->usage)
+ return;
+
+ /* Remove the inode from the Inode List */
+ Inode *search, *prev = NULL;
+ if (inode->super) {
+ for (search = inode->super->inodes; search;
+ prev = search, search = search->lnext)
+ if (search == inode)
+ break;
+ if (search) {
+ if (prev)
+ prev->lnext = search->lnext;
+ else
+ inode->super->inodes = search->lnext;
+ }
+ }
+
+ /* Clean up */
+ if (S_ISDIR(inode->mode))
+ entry_clean(inode);
+ else
+ page_clean(inode);
+
+ kfree(inode);
+}
+
+/* Create an inode */
+int
+inode_create(Inode *inode, DirEntry *entry, mode_t mode)
+{
+ if (!inode->ops)
+ return -EINVAL;
+ if (!inode->ops->create)
+ return -EINVAL;
+ acquire(&inode->lock);
+ int err = inode->ops->create(inode, entry, mode);
+ if (!err) {
+ entry_add(inode, entry);
+ entry->inode = super_alloc_inode(inode->super);
+ entry->inode->mode = mode | S_IFREG;
+ }
+ release(&inode->lock);
+ return err;
+}
+
+/* Find a child inode in a directory inode */
+DirEntry *
+inode_lookup(Inode *inode, const char *name)
+{
+ if (!S_ISDIR(inode->mode))
+ return NULL;
+
+ acquire(&inode->lock);
+
+ /* Check cache first */
+ DirEntry *de = entry_find(inode, name);
+ if (de) {
+ entry_get(de);
+ if (de->inode) {
+ release(&inode->lock);
+ return de;
+ }
+ }
+ /* Try file system lookup */
+ if (!inode->ops) {
+ release(&inode->lock);
+ return NULL;
+ }
+ if (!inode->ops->lookup) {
+ release(&inode->lock);
+ return NULL;
+ }
+ Inode *child = inode->ops->lookup(inode, name);
+
+ /* The file doesn't exist */
+ if (!child) {
+ if (de)
+ entry_remove(inode, name);
+ release(&inode->lock);
+ return NULL;
+ }
+
+ /* Fill in DirEntry */
+ if (!de) {
+ de = kmalloc(sizeof(DirEntry));
+ strncpy(de->name, name, NAME_MAX);
+ de->super = inode->super;
+ entry_add(inode, de);
+ }
+ de->inode = inode_get(child);
+ de->mnt = NULL;
+ release(&inode->lock);
+
+ return entry_get(de);
+}
+
+/* Make a directory */
+int
+inode_mkdir(Inode *inode, DirEntry *entry, mode_t mode)
+{
+ if (!inode->ops)
+ return -EINVAL;
+ if (!inode->ops->mkdir)
+ return -EINVAL;
+ acquire(&inode->lock);
+ int err = inode->ops->mkdir(inode, entry, mode);
+ if (!err) {
+ entry_add(inode, entry);
+ entry->inode = super_alloc_inode(inode->super);
+ entry->inode->mode = mode | S_IFDIR;
+ }
+ release(&inode->lock);
+ return err;
+}
+
+/* Remove a directory */
+int
+inode_rmdir(Inode *inode, DirEntry *entry)
+{
+ if (!inode->ops)
+ return -EINVAL;
+ if (!inode->ops->rmdir)
+ return -EINVAL;
+ acquire(&inode->lock);
+ int err = inode->ops->rmdir(inode, entry);
+ if (!err)
+ entry_remove(inode, entry->name);
+ release(&inode->lock);
+ return err;
+}
+
+/* Make a node */
+int
+inode_mknod(Inode *inode, DirEntry *entry, mode_t mode, dev_t dev)
+{
+ if (!inode->ops)
+ return -EINVAL;
+ if (!inode->ops->mknod)
+ return -EINVAL;
+ acquire(&inode->lock);
+
+ int err = inode->ops->mknod(inode, entry, mode, dev);
+ if (!err) {
+ entry_add(inode, entry);
+ entry->inode = super_alloc_inode(inode->super);
+ entry->inode->mode = mode; /* FIXME */
+ entry->inode->dev = dev;
+ }
+ release(&inode->lock);
+ return err;
+}
+
+/* Rename/mode a directory entry */
+int
+inode_rename(Inode *si, DirEntry *sde, Inode *di, DirEntry *dde)
+{
+ if (!si->ops)
+ return -EINVAL;
+ if (!si->ops->rename)
+ return -EINVAL;
+ return 0;
+}
diff --git a/vfs/inode.h b/vfs/inode.h
new file mode 100644
index 0000000..adf5b3b
--- /dev/null
+++ b/vfs/inode.h
@@ -0,0 +1,15 @@
+#ifndef KERNEL_VFS_INODE_H
+#define KERNEL_VFS_INODE_H
+
+#include "vfs.h"
+
+Inode *inode_get(Inode *inode);
+void inode_put(Inode *inode);
+int inode_create(Inode *inode, DirEntry *entry, mode_t mode);
+DirEntry *inode_lookup(Inode *inode, const char *name);
+int inode_mkdir(Inode *inode, DirEntry *entry, mode_t mode);
+int inode_rmdir(Inode *inode, DirEntry *entry);
+int inode_mknod(Inode *inode, DirEntry *entry, mode_t mode, dev_t dev);
+int inode_rename(Inode *si, DirEntry *sde, Inode *di, DirEntry *dde);
+
+#endif
diff --git a/vfs/procfs/file.c b/vfs/procfs/file.c
new file mode 100644
index 0000000..b292cf4
--- /dev/null
+++ b/vfs/procfs/file.c
@@ -0,0 +1,126 @@
+/*
+ * This file controls access to ProcFS Files. It contains the functions called
+ * by the VFS for any operation on a File struct belonging to ProcFS.
+ */
+
+#include <stddef.h>
+#include <string.h>
+#include <errno.h>
+#include "fs.h"
+#include "../vfs.h"
+#include "../cache.h"
+#include "../../mem/paging.h"
+#include "../../mem/frame.h"
+
+size_t procfs_read(File *file, char *buf, size_t size, off_t offset);
+size_t procfs_write(File *file, char *buf, size_t size, off_t offset);
+int procfs_readdir(File *file, DirEnt *dent, off_t index);
+int procfs_open(File *file);
+
+FileOps procfsFileOps = {
+ .read = procfs_read,
+ .write = procfs_write,
+ .readdir = procfs_readdir,
+ .open = procfs_open,
+};
+
+/* Read a file */
+size_t
+procfs_read(File *file, char *buf, size_t size, off_t offset)
+{
+ if (offset > file->inode->size)
+ return 0;
+ if (size + offset > file->inode->size)
+ size = file->inode->size - offset;
+
+ uint16_t min;
+ size_t count = 0;
+ Page *page;
+ page_t oldPage;
+ while (size) {
+ min = (size > 0x1000) ? 0x1000 : size;
+ page = page_find(file->inode, offset);
+ if (!page)
+ break;
+ acquire(&quickPageLock);
+ oldPage = quick_page(page->frame);
+ memcpy(buf + count, QUICK_PAGE, min);
+ quick_page(oldPage);
+ release(&quickPageLock);
+ size -= min;
+ count += min;
+ }
+ return count;
+}
+
+/* Write a file */
+size_t
+procfs_write(File *file, char *buf, size_t size, off_t offset)
+{
+ if (size + offset > file->inode->size)
+ file->inode->size = size + offset;
+
+ uint16_t min;
+ size_t count = 0;
+ Page *page;
+ page_t oldPage;
+ while (size) {
+ min = (size > 0x1000) ? 0x1000 : size;
+ page = page_find(file->inode, offset);
+ if (!page)
+ page = page_create(file->inode, alloc_frames(1),
+ offset);
+ acquire(&quickPageLock);
+ oldPage = quick_page(page->frame);
+ memcpy(QUICK_PAGE, buf + count, min);
+ quick_page(oldPage);
+ release(&quickPageLock);
+ size -= min;
+ count += min;
+ }
+ return count;
+}
+
+/* Read a directory entry */
+int
+procfs_readdir(File *file, DirEnt *dent, off_t index)
+{
+ DirEntry *de;
+
+ if (!index--) {
+ dent->ino = file->inode->ino;
+ dent->type = DT_DIR;
+ dent->namelen = 2;
+ strncpy(dent->name, ".", dent->namelen);
+ return 0;
+ }
+
+ for (de = file->inode->dirEntries; de && index; de = de->next, index--);
+ if (!de)
+ return -ENOENT;
+ dent->ino = de->inode->ino;
+ if (S_ISBLK(de->inode->mode))
+ dent->type = DT_BLK;
+ if (S_ISCHR(de->inode->mode))
+ dent->type = DT_CHR;
+ if (S_ISDIR(de->inode->mode))
+ dent->type = DT_DIR;
+ if (S_ISFIFO(de->inode->mode))
+ dent->type = DT_FIFO;
+ if (S_ISLNK(de->inode->mode))
+ dent->type = DT_LNK;
+ if (S_ISREG(de->inode->mode))
+ dent->type = DT_REG;
+ if (S_ISSOCK(de->inode->mode))
+ dent->type = DT_SOCK;
+ dent->namelen = strnlen(de->name, NAME_MAX) + 1;
+ strncpy(dent->name, de->name, NAME_MAX);
+ return 0;
+}
+
+/* Open a file */
+int
+procfs_open(File *file)
+{
+ return 0;
+}
diff --git a/vfs/procfs/fs.h b/vfs/procfs/fs.h
new file mode 100644
index 0000000..346c1b3
--- /dev/null
+++ b/vfs/procfs/fs.h
@@ -0,0 +1,13 @@
+#ifndef KERNEL_VFS_PROCFS_H
+#define KERNEL_VFS_PROCFS_H
+
+#include "../vfs.h"
+
+/* Operations */
+extern SuperOps procfsSuperOps;
+extern InodeOps procfsInodeOps;
+extern FileOps procfsFileOps;
+
+extern FileSystemType procfsType;
+
+#endif
diff --git a/vfs/procfs/inode.c b/vfs/procfs/inode.c
new file mode 100644
index 0000000..a81c3fc
--- /dev/null
+++ b/vfs/procfs/inode.c
@@ -0,0 +1,69 @@
+/*
+ * This file contains the functions dealing with ProcFS inodes. The VFS will
+ * call these when it performs operations on ProcFS Inodes, or is dealing with
+ * the ProcFS hierarchy.
+ */
+
+#include <string.h>
+#include "fs.h"
+#include "../vfs.h"
+#include "../../mem/heap.h"
+
+int procfs_create(Inode *inode, DirEntry *entry, mode_t mode);
+Inode *procfs_lookup(Inode *inode, const char *name);
+int procfs_mkdir(Inode *inode, DirEntry *entry, mode_t mode);
+int procfs_rmdir(Inode *inode, DirEntry *entry);
+int procfs_mknod(Inode *inode, DirEntry *entry, mode_t mode, dev_t dev);
+int procfs_rename(Inode *si, DirEntry *sde, Inode *di, DirEntry *dde);
+
+InodeOps procfsInodeOps = {
+ .create = procfs_create,
+ .lookup = procfs_lookup,
+ .mkdir = procfs_mkdir,
+ .rmdir = procfs_rmdir,
+ .mknod = procfs_mknod,
+ .rename = procfs_rename,
+};
+
+/* Create a file */
+int
+procfs_create(Inode *inode, DirEntry *entry, mode_t mode)
+{
+ return 0;
+}
+
+
+/* Look up a file */
+Inode *
+procfs_lookup(Inode *inode, const char *name)
+{
+ return NULL;
+}
+
+/* Make a directory */
+int
+procfs_mkdir(Inode *inode, DirEntry *entry, mode_t mode)
+{
+ return 0;
+}
+
+/* Remove a directory */
+int
+procfs_rmdir(Inode *inode, DirEntry *entry)
+{
+ return 0;
+}
+
+/* Make a node */
+int
+procfs_mknod(Inode *inode, DirEntry *entry, mode_t mode, dev_t dev)
+{
+ return 0;
+}
+
+/* Rename/mode a directory entry */
+int
+procfs_rename(Inode *si, DirEntry *sde, Inode *di, DirEntry *dde)
+{
+ return 0;
+}
diff --git a/vfs/procfs/super.c b/vfs/procfs/super.c
new file mode 100644
index 0000000..dcfa871
--- /dev/null
+++ b/vfs/procfs/super.c
@@ -0,0 +1,77 @@
+/*
+ * This file controls the superblock for ProcFS. It supports mounting new ProcFS
+ * filesystems. The VFS will use the calls here when dealing directly with the
+ * filesystem structure.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "fs.h"
+#include "../vfs.h"
+#include "../super.h"
+#include "../inode.h"
+#include "../../mem/heap.h"
+
+Inode *procfs_mount(FileSystemType *type, int flags,
+ const char *dev, void *data);
+Inode *procfs_alloc_inode(SuperBlock *sb);
+
+FileSystemType procfsType = {
+ .name = "ProcFS",
+ .mount = procfs_mount,
+// .kill_sb = procfs_kill_sb,
+};
+
+SuperOps procfsSuperOps = {
+ .alloc_inode = procfs_alloc_inode,
+// .free_inode = procfs_free_inode,
+// .write_inode = procfs_write_inode,
+// .delete_inode = procfs_delete_inode,
+};
+
+Inode procfsRoot = {
+ .mode = S_IFDIR | 0755,
+ .ops = &procfsInodeOps,
+ .fileOps = &procfsFileOps,
+ .size = 4096,
+};
+
+/* Mount a ProcFS instance */
+Inode *
+procfs_mount(FileSystemType *type, int flags, const char *dev, void *data)
+{
+ if (type != &procfsType)
+ return NULL;
+
+ SuperBlock *super = kmalloc(sizeof(SuperBlock));
+
+ super->type = type;
+ super->ops = &procfsSuperOps;
+ init_lock(&super->lock);
+
+ Inode *inode = &procfsRoot;
+ inode->nlink++;
+ inode->super = super;
+ /* FIXME: need an inode per super, or only one super */
+ super->root = inode;
+ /* Never free */
+ if (!inode->usage)
+ inode_get(inode);
+
+ return inode;
+}
+
+/* Allocate an inode */
+Inode *
+procfs_alloc_inode(SuperBlock *sb)
+{
+ Inode *inode = kmalloc(sizeof(Inode));
+ init_lock(&inode->lock);
+ inode->ops = &procfsInodeOps;
+ inode->fileOps = &procfsFileOps;
+ inode->super = sb;
+ inode->nlink = 1;
+ return inode_get(inode); /* This ensures that the inode is never free */
+}
diff --git a/vfs/super.c b/vfs/super.c
new file mode 100644
index 0000000..5201110
--- /dev/null
+++ b/vfs/super.c
@@ -0,0 +1,42 @@
+/*
+ * This file contains the wrapper functions for the Super Block Operations. It
+ * just calls the relevant internal functions for the file system, since most of
+ * them need to know driver specific values, like where to find the file system
+ * operations. There usually isn't a generic way to handle the functions.
+ */
+
+#include <stdint.h>
+#include "vfs.h"
+
+/* Allocate an inode */
+Inode *
+super_alloc_inode(SuperBlock *sb)
+{
+ if (!sb->ops)
+ return NULL;
+ if (!sb->ops->alloc_inode)
+ return NULL;
+ return sb->ops->alloc_inode(sb);
+}
+
+/* Search for an Inode in a Super Block's Inode list */
+Inode *
+super_find_inode(SuperBlock *sb, ino_t ino)
+{
+ Inode *inode;
+ for (inode = sb->inodes; inode; inode = inode->lnext)
+ if (inode->ino == ino)
+ break;
+ return inode;
+}
+
+/* Write an inode to disk */
+int
+super_write_inode(SuperBlock *sb, Inode *inode)
+{
+ if (!sb->ops)
+ return 0;
+ if (!sb->ops->write_inode)
+ return 0;
+ return sb->ops->write_inode(inode);
+}
diff --git a/vfs/super.h b/vfs/super.h
new file mode 100644
index 0000000..917b7c1
--- /dev/null
+++ b/vfs/super.h
@@ -0,0 +1,10 @@
+#ifndef KERNEL_VFS_SUPER_H
+#define KERNEL_VFS_SUPER_H
+
+#include "vfs.h"
+
+Inode *super_alloc_inode(SuperBlock *sb);
+Inode *super_find_inode(SuperBlock *sb, ino_t ino);
+int super_write_inode(SuperBlock *sb, Inode *inode);
+
+#endif
diff --git a/vfs/tmpfs/file.c b/vfs/tmpfs/file.c
new file mode 100644
index 0000000..252c88f
--- /dev/null
+++ b/vfs/tmpfs/file.c
@@ -0,0 +1,126 @@
+/*
+ * This file controls access to TmpFS Files. It contains the functions called
+ * by the VFS for any operation on a File struct belonging to TmpFS.
+ */
+
+#include <stddef.h>
+#include <string.h>
+#include <errno.h>
+#include "fs.h"
+#include "../vfs.h"
+#include "../cache.h"
+#include "../../mem/paging.h"
+#include "../../mem/frame.h"
+
+size_t tmpfs_read(File *file, char *buf, size_t size, off_t offset);
+size_t tmpfs_write(File *file, char *buf, size_t size, off_t offset);
+int tmpfs_readdir(File *file, DirEnt *dent, off_t index);
+int tmpfs_open(File *file);
+
+FileOps tmpfsFileOps = {
+ .read = tmpfs_read,
+ .write = tmpfs_write,
+ .readdir = tmpfs_readdir,
+ .open = tmpfs_open,
+};
+
+/* Read a file */
+size_t
+tmpfs_read(File *file, char *buf, size_t size, off_t offset)
+{
+ if (offset > file->inode->size)
+ return 0;
+ if (size + offset > file->inode->size)
+ size = file->inode->size - offset;
+
+ uint16_t min;
+ size_t count = 0;
+ Page *page;
+ page_t oldPage;
+ while (size) {
+ min = (size > 0x1000) ? 0x1000 : size;
+ page = page_find(file->inode, offset);
+ if (!page)
+ break;
+ acquire(&quickPageLock);
+ oldPage = quick_page(page->frame);
+ memcpy(buf + count, QUICK_PAGE, min);
+ quick_page(oldPage);
+ release(&quickPageLock);
+ size -= min;
+ count += min;
+ }
+ return count;
+}
+
+/* Write a file */
+size_t
+tmpfs_write(File *file, char *buf, size_t size, off_t offset)
+{
+ if (size + offset > file->inode->size)
+ file->inode->size = size + offset;
+
+ uint16_t min;
+ size_t count = 0;
+ Page *page;
+ page_t oldPage;
+ while (size) {
+ min = (size > 0x1000) ? 0x1000 : size;
+ page = page_find(file->inode, offset);
+ if (!page)
+ page = page_create(file->inode, alloc_frames(1),
+ offset);
+ acquire(&quickPageLock);
+ oldPage = quick_page(page->frame);
+ memcpy(QUICK_PAGE, buf + count, min);
+ quick_page(oldPage);
+ release(&quickPageLock);
+ size -= min;
+ count += min;
+ }
+ return count;
+}
+
+/* Read a directory entry */
+int
+tmpfs_readdir(File *file, DirEnt *dent, off_t index)
+{
+ DirEntry *de;
+
+ if (!index--) {
+ dent->ino = file->inode->ino;
+ dent->type = DT_DIR;
+ dent->namelen = 2;
+ strncpy(dent->name, ".", dent->namelen);
+ return 0;
+ }
+
+ for (de = file->inode->dirEntries; de && index; de = de->next, index--);
+ if (!de)
+ return -ENOENT;
+ dent->ino = de->inode->ino;
+ if (S_ISBLK(de->inode->mode))
+ dent->type = DT_BLK;
+ if (S_ISCHR(de->inode->mode))
+ dent->type = DT_CHR;
+ if (S_ISDIR(de->inode->mode))
+ dent->type = DT_DIR;
+ if (S_ISFIFO(de->inode->mode))
+ dent->type = DT_FIFO;
+ if (S_ISLNK(de->inode->mode))
+ dent->type = DT_LNK;
+ if (S_ISREG(de->inode->mode))
+ dent->type = DT_REG;
+ if (S_ISSOCK(de->inode->mode))
+ dent->type = DT_SOCK;
+ dent->namelen = strnlen(de->name, NAME_MAX) + 1;
+ strncpy(dent->name, de->name, NAME_MAX);
+ return 0;
+}
+
+/* Open a file */
+int
+tmpfs_open(File *file)
+{
+ return 0;
+}
diff --git a/vfs/tmpfs/fs.h b/vfs/tmpfs/fs.h
new file mode 100644
index 0000000..104f64d
--- /dev/null
+++ b/vfs/tmpfs/fs.h
@@ -0,0 +1,13 @@
+#ifndef KERNEL_VFS_TMPFS_H
+#define KERNEL_VFS_TMPFS_H
+
+#include "../vfs.h"
+
+/* Operations */
+extern SuperOps tmpfsSuperOps;
+extern InodeOps tmpfsInodeOps;
+extern FileOps tmpfsFileOps;
+
+extern FileSystemType tmpfsType;
+
+#endif
diff --git a/vfs/tmpfs/inode.c b/vfs/tmpfs/inode.c
new file mode 100644
index 0000000..5da5408
--- /dev/null
+++ b/vfs/tmpfs/inode.c
@@ -0,0 +1,69 @@
+/*
+ * This file contains the functions dealing with TmpFS inodes. The VFS will
+ * call these when it performs operations on TmpFS Inodes, or is dealing with
+ * the TmpFS hierarchy.
+ */
+
+#include <string.h>
+#include "fs.h"
+#include "../vfs.h"
+#include "../../mem/heap.h"
+
+int tmpfs_create(Inode *inode, DirEntry *entry, mode_t mode);
+Inode *tmpfs_lookup(Inode *inode, const char *name);
+int tmpfs_mkdir(Inode *inode, DirEntry *entry, mode_t mode);
+int tmpfs_rmdir(Inode *inode, DirEntry *entry);
+int tmpfs_mknod(Inode *inode, DirEntry *entry, mode_t mode, dev_t dev);
+int tmpfs_rename(Inode *si, DirEntry *sde, Inode *di, DirEntry *dde);
+
+InodeOps tmpfsInodeOps = {
+ .create = tmpfs_create,
+ .lookup = tmpfs_lookup,
+ .mkdir = tmpfs_mkdir,
+ .rmdir = tmpfs_rmdir,
+ .mknod = tmpfs_mknod,
+ .rename = tmpfs_rename,
+};
+
+/* Create a file */
+int
+tmpfs_create(Inode *inode, DirEntry *entry, mode_t mode)
+{
+ return 0;
+}
+
+
+/* Look up a file */
+Inode *
+tmpfs_lookup(Inode *inode, const char *name)
+{
+ return NULL;
+}
+
+/* Make a directory */
+int
+tmpfs_mkdir(Inode *inode, DirEntry *entry, mode_t mode)
+{
+ return 0;
+}
+
+/* Remove a directory */
+int
+tmpfs_rmdir(Inode *inode, DirEntry *entry)
+{
+ return 0;
+}
+
+/* Make a node */
+int
+tmpfs_mknod(Inode *inode, DirEntry *entry, mode_t mode, dev_t dev)
+{
+ return 0;
+}
+
+/* Rename/mode a directory entry */
+int
+tmpfs_rename(Inode *si, DirEntry *sde, Inode *di, DirEntry *dde)
+{
+ return 0;
+}
diff --git a/vfs/tmpfs/super.c b/vfs/tmpfs/super.c
new file mode 100644
index 0000000..ede1961
--- /dev/null
+++ b/vfs/tmpfs/super.c
@@ -0,0 +1,65 @@
+/*
+ * This file controls the superblock for TmpFS. It supports mounting new TmpFS
+ * filesystems. The VFS will use the calls here when dealing directly with the
+ * filesystem structure.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "fs.h"
+#include "../vfs.h"
+#include "../super.h"
+#include "../inode.h"
+#include "../../mem/heap.h"
+
+Inode *tmpfs_mount(FileSystemType *type, int flags,
+ const char *dev, void *data);
+Inode *tmpfs_alloc_inode(SuperBlock *sb);
+
+FileSystemType tmpfsType = {
+ .name = "TmpFS",
+ .mount = tmpfs_mount,
+// .kill_sb = tmpfs_kill_sb,
+};
+
+SuperOps tmpfsSuperOps = {
+ .alloc_inode = tmpfs_alloc_inode,
+// .free_inode = tmpfs_free_inode,
+// .write_inode = tmpfs_write_inode,
+// .delete_inode = tmpfs_delete_inode,
+};
+
+/* Mount a TmpFS instance */
+Inode *
+tmpfs_mount(FileSystemType *type, int flags, const char *dev, void *data)
+{
+ if (type != &tmpfsType)
+ return NULL;
+
+ SuperBlock *super = kmalloc(sizeof(SuperBlock));
+
+ super->type = type;
+ super->ops = &tmpfsSuperOps;
+ init_lock(&super->lock);
+
+ Inode *inode = super_alloc_inode(super);
+ inode->mode = S_IFDIR | 0755;
+ super->root = inode;
+
+ return inode;
+}
+
+/* Allocate an inode */
+Inode *
+tmpfs_alloc_inode(SuperBlock *sb)
+{
+ Inode *inode = kmalloc(sizeof(Inode));
+ init_lock(&inode->lock);
+ inode->ops = &tmpfsInodeOps;
+ inode->fileOps = &tmpfsFileOps;
+ inode->super = sb;
+ inode->nlink = 1;
+ return inode_get(inode); /* This ensures that the inode is never free */
+}
diff --git a/vfs/vfs.c b/vfs/vfs.c
new file mode 100644
index 0000000..68f4b34
--- /dev/null
+++ b/vfs/vfs.c
@@ -0,0 +1,680 @@
+/*
+ * 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(¤t->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 */
+size_t
+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;
+
+ return file_read(file, buf, count);
+}
+
+/* Write a file */
+size_t
+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;
+
+ return file_write(file, buf, count);
+}
+
+/* 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->inode = inode->ino;
+ statbuf->mode = inode->mode;
+ statbuf->nlink = inode->nlink;
+ statbuf->uid = inode->uid;
+ statbuf->gid = inode->gid;
+ statbuf->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;
+ statbuf->inode = inode->ino;
+ statbuf->mode = inode->mode;
+ statbuf->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->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(¤t->fs->cwdCustody);
+ copy_custody_chain(&tmp, ¤t->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(¤t->fs->cwdCustody, buf, size);
+}
diff --git a/vfs/vfs.h b/vfs/vfs.h
new file mode 100644
index 0000000..ece5679
--- /dev/null
+++ b/vfs/vfs.h
@@ -0,0 +1,202 @@
+#ifndef KERNEL_VFS_H
+#define KERNEL_VFS_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include "../spinlock.h"
+#include "../mem/paging.h"
+
+#define NFILES 32
+#define NAME_MAX 255
+#define PATH_MAX 1024
+
+#define MAY_READ 4
+#define MAY_WRITE 2
+#define MAY_EXECUTE 1
+
+typedef struct FileSystemType FileSystemType;
+typedef struct SuperBlock SuperBlock;
+typedef struct SuperOps SuperOps;
+typedef struct Inode Inode;
+typedef struct InodeOps InodeOps;
+typedef struct File File;
+typedef struct FileOps FileOps;
+typedef struct DirEntry DirEntry;
+typedef struct Page Page;
+typedef struct PageCache PageCache;
+typedef struct Custody Custody;
+typedef struct CustodyChain CustodyChain;
+
+/* Structure for a File System Type */
+struct FileSystemType {
+ const char *name;
+ Inode *(*mount)(FileSystemType *, int, const char *, void *);
+ void (*kill_sb)(SuperBlock *);
+ pid_t owner;
+ FileSystemType *next;
+};
+
+/* Structure for a Super Block */
+struct SuperBlock {
+ FileSystemType *type;
+ SuperOps *ops;
+ Spinlock lock;
+ Inode *root;
+ Inode *inodes; /* List of cached inodes */
+ size_t cachedInodes;
+ File *back;
+ void *data;
+};
+struct SuperOps {
+ Inode *(*alloc_inode)(SuperBlock *);
+ int (*write_inode)(Inode *);
+ void (*delete_inode)(Inode *);
+};
+
+/* Structure for an Inode */
+struct Inode {
+ ino_t ino;
+ refcount_t usage;
+ unsigned short uid, gid;
+ mode_t mode;
+ nlink_t nlink;
+ uint32_t flags;
+ size_t size;
+ dev_t dev;
+ Spinlock lock;
+ InodeOps *ops;
+ FileOps *fileOps;
+ SuperBlock *super;
+ union {
+ DirEntry *dirEntries; /* List of Directory Entries */
+ PageCache *pages; /* List of Pages */
+ };
+ Inode *lnext; /* Next inode in super list */
+};
+struct InodeOps {
+ int (*create)(Inode *, DirEntry *, mode_t);
+ Inode *(*lookup)(Inode *, const char *);
+ int (*mkdir)(Inode *, DirEntry *, mode_t);
+ int (*rmdir)(Inode *, DirEntry *);
+ int (*mknod)(Inode *, DirEntry *, mode_t, dev_t);
+ int (*rename) (Inode *, DirEntry *, Inode *, DirEntry *);
+};
+
+/* Structure for an open File */
+struct File {
+ Inode *inode;
+ unsigned short uid, gid;
+ int flags;
+ mode_t mode;
+ off_t pos;
+ Spinlock lock;
+ FileOps *ops;
+ CustodyChain *path;
+ refcount_t usage;
+};
+struct FileOps {
+ off_t (*lseek)(File *, off_t, int);
+ size_t (*read)(File *, char *, size_t, off_t);
+ size_t (*write)(File *, char *, size_t, off_t);
+ int (*ioctl)(File *, unsigned long, uintptr_t);
+ int (*readdir)(File *, DirEnt *, off_t);
+ int (*open)(File *);
+ int (*flush)(File *);
+ int (*release)(File *);
+ void (*mmap)(File *, void *, size_t, off_t);
+};
+
+/* Structure for a Directory Entry */
+struct DirEntry {
+ DirEntry *next;
+ Inode *inode, *mnt;
+ uint32_t hash;
+ char name[NAME_MAX];
+ Spinlock lock;
+ SuperBlock *super;
+ refcount_t usage;
+ DirEntry *lnext; /* Next directory entry in global list */
+};
+
+/* Structure for a Page in a block cache */
+struct Page {
+ page_t frame;
+ off_t offset;
+ refcount_t usage;
+};
+struct PageCache {
+ Page *page;
+ PageCache *next;
+};
+
+/* Custody Chains */
+struct Custody {
+ Custody *prev, *next;
+ CustodyChain *chain;
+ DirEntry *entry;
+};
+struct CustodyChain {
+ Custody *start, *end;
+ size_t size;
+ Spinlock lock;
+};
+
+/* File System Namespace */
+typedef struct FileSystem {
+ Inode *cwd;
+ CustodyChain cwdCustody;
+ Inode *root;
+ refcount_t usage;
+} FileSystem;
+
+/* Files Namespace */
+typedef struct Files {
+ File *fd[NFILES];
+ refcount_t usage;
+} Files;
+
+void init_vfs(void);
+void register_fstype(FileSystemType *fstype);
+Inode *lookup(const char *name, CustodyChain *chain);
+
+int open(const char *name, int flags, ...); /* mode_t mode */
+int close(int fd);
+size_t read(int fd, void *buf, size_t count);
+size_t write(int fd, void *buf, size_t count);
+int ioctl(int fd, unsigned long request, ...);
+off_t lseek(int fd, off_t offset, int whence);
+int stat(const char *pathname, struct stat *statbuf);
+size_t getdents(int fd, void *buf, size_t count);
+int mkdir(const char *pathname, mode_t mode);
+int rmdir(const char *pathname);
+int mknod(const char *pathname, mode_t mode, dev_t dev);
+int rename(const char *oldpath, const char *newpath);
+int dup(int oldfd);
+int dup2(int oldfd, int newfd);
+int mount(const char *src, const char *target, const char *type,
+ unsigned long flags, void *data);
+int chdir(const char *path);
+int chroot(const char *path);
+char *getcwd(char *buf, size_t size);
+
+void init_custody_chain(CustodyChain *chain);
+CustodyChain *create_custody_chain(void);
+void clean_custody_chain(CustodyChain *chain);
+void destroy_custody_chain(CustodyChain *chain);
+void copy_custody_chain(CustodyChain *chain, CustodyChain *newchain);
+void add_custody(CustodyChain *chain, DirEntry *entry);
+DirEntry *remove_custody(CustodyChain *chain);
+char *custody_path(CustodyChain *chain, char *buf, size_t size);
+
+File *file_get(File *file);
+void file_put(File *file);
+size_t file_read(File *file, char *buf, size_t size);
+size_t file_write(File *file, char *buf, size_t size);
+int file_ioctl(File *file, unsigned long reqeust, uintptr_t argp);
+int file_readdir(File *file, DirEnt *dent, off_t index);
+int file_open(File *file);
+void file_mmap(File *file, void *buf, size_t len, off_t offset);
+
+#endif