BarryServer : Git

All the code for all my projects
// BarryServer : Git / Orion / commit / d41a53cbc7d055b1c00cf0a339dbed6925f4f02c / vfs

// Related

Orion

Barry Importing existing Orion kernel d41a53c (2 years, 4 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(&current->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(&current->fs->cwdCustody);
+	copy_custody_chain(&tmp, &current->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(&current->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