Add ext2 VFS implementation
diff --git a/.dir-locals.el b/.dir-locals.el
index 2648a7e..14d2bd3 100644
--- a/.dir-locals.el
+++ b/.dir-locals.el
@@ -1,3 +1,5 @@
 ((nil . ((eval
-		  . (setq-local flycheck-clang-args
-						(jmk-arguments-for (expand-file-name buffer-file-name)))))))
+		  . (setq-local flycheck-clang-include-path
+						(jmk-includes-for (expand-file-name buffer-file-name))
+						flycheck-clang-args
+						(jmk-other-flags-for (expand-file-name buffer-file-name)))))))
diff --git a/include/kernel/dri/fs/ext2/ext2.h b/include/kernel/dri/fs/ext2/ext2.h
index 5b587c9..9b6d5ba 100644
--- a/include/kernel/dri/fs/ext2/ext2.h
+++ b/include/kernel/dri/fs/ext2/ext2.h
@@ -264,6 +264,11 @@
 	EXT2_FT_SYMLINK,
 };
 
+inline uint ext2_block_size(struct ext2_superblock *sb)
+{
+	return 1024 << sb->block_size_shift;
+}
+
 /// Read a file system block (0-indexed), if necessary multiple disk
 /// blocks will be read automatically
 void ext2_read_block(struct ext2_superblock *sb, void *buffer,
@@ -300,9 +305,19 @@
 					struct ext2_inode *inode);
 
 /// Load a block group descriptor for a certain block group
-struct ext2_block_group_descriptor ext2_load_block_group_descriptor(
+struct ext2_block_group_descriptor ext2_load_bgd(
 	struct ext2_superblock *sb, uint block_group);
 
+/// Write a block group descriptor for a certain block group
+void ext2_write_bgd(struct ext2_superblock *sb, uint block_group,
+					struct ext2_block_group_descriptor *d);
+
+/// Load or write a BGD
+void ext2_load_or_write_bgd(struct ext2_superblock *sb,
+							uint block_group,
+							struct ext2_block_group_descriptor *d,
+							bool set);
+
 /// List the contents of a directory dir. Calls `cb` for each item. If
 /// `dir` is not a directory, returns false. Otherwise returns true.
 /// if cb returns true, ls will continue. Otherwise it will stop.
@@ -324,7 +339,9 @@
 bool ext2_write_inode_block(struct ext2_superblock *sb, struct ext2_inode *dir,
 							void *buffer, uint block);
 
-ssize_t ext2_read_inode(struct ext2_superblock *sb, struct ext2_inode *inode, void *buffer, ssize_t size);
+ssize_t ext2_read_inode(struct ext2_superblock *sb,
+						struct ext2_inode *inode, void *buffer,
+						ssize_t size);
 
 /**
  * @brief Set a block in a bitmap
diff --git a/include/kernel/dri/fs/ext2/ext2_vfs.h b/include/kernel/dri/fs/ext2/ext2_vfs.h
new file mode 100644
index 0000000..856e9e2
--- /dev/null
+++ b/include/kernel/dri/fs/ext2/ext2_vfs.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include "ext2.h"
+#include <vfs.h>
+
+// VFS specific EXT2 functions
+
+/// ext2 inode -> vfs node
+struct fs_node *ext2_inode2vfs(struct ext2_superblock *sb, uint inode,
+							   char *name, uint name_len);
diff --git a/include/kernel/vfs.h b/include/kernel/vfs.h
index e7cf8d9..d9f36a6 100644
--- a/include/kernel/vfs.h
+++ b/include/kernel/vfs.h
@@ -4,8 +4,12 @@
 
 struct fs_vtable;
 
+#define FS_MAX_NAME_LEN 128
+
 struct fs_node
 {
+	/** length of file name */
+	uint name_len;
 	/** file name */
 	char name[128];
 	/** identifier */
@@ -21,7 +25,11 @@
 	/** size in bytes */
 	size_t size;
 	/** reserved for driver */
-	uint dri_res;
+	union
+	{
+		uint dri_res_i;
+		void *dri_res_p;
+	};
 
 	struct fs_vtable *vtable;
 
@@ -33,6 +41,7 @@
 {
 	// EXT2 supports up to 256 byte file names, so we will do the same
 	char name[256];
+	uint name_len;
 	uint inode;
 };
 
@@ -42,7 +51,7 @@
 typedef void (*fs_close_t)(struct fs_node *node);
 
 typedef bool (*fs_readdir_t)(struct fs_node *node, uint index, struct fs_dirent *dirent);
-typedef struct fs_node *(*fs_finddir_t)(struct fs_node *node, char *name);
+typedef struct fs_node *(*fs_finddir_t)(struct fs_node *node, char *name, uint name_len);
 
 struct fs_vtable
 {
@@ -78,7 +87,7 @@
 void fs_close(struct fs_node *node);
 
 bool fs_readdir(struct fs_node *node, uint index, struct fs_dirent *out);
-struct fs_node *fs_finddir(struct fs_node *node, char *name);
+struct fs_node *fs_finddir(struct fs_node *node, char *name, uint name_len);
 
 /* Returns the following error codes:
  * 0: success
diff --git a/share/jmk/dir-locals.el b/share/jmk/dir-locals.el
index 2648a7e..14d2bd3 100644
--- a/share/jmk/dir-locals.el
+++ b/share/jmk/dir-locals.el
@@ -1,3 +1,5 @@
 ((nil . ((eval
-		  . (setq-local flycheck-clang-args
-						(jmk-arguments-for (expand-file-name buffer-file-name)))))))
+		  . (setq-local flycheck-clang-include-path
+						(jmk-includes-for (expand-file-name buffer-file-name))
+						flycheck-clang-args
+						(jmk-other-flags-for (expand-file-name buffer-file-name)))))))
diff --git a/share/jmk/jmk-flycheck.el b/share/jmk/jmk-flycheck.el
index 8de4949..a1ab539 100644
--- a/share/jmk/jmk-flycheck.el
+++ b/share/jmk/jmk-flycheck.el
@@ -35,3 +35,11 @@
 	(message "includes: %s" names)
 	names))
 
+(defun jmk-other-flags-for (p)
+  (let* ((args (jmk-arguments-for p))
+		 (not-includes (cl-remove-if-not (lambda (arg)
+										   (string-prefix-p "-I" arg))
+					   args))
+		 (stripped (mapcar #'string-trim not-includes)))
+	stripped))
+
diff --git a/src/kernel/dri/fs/ext2/Jmk b/src/kernel/dri/fs/ext2/Jmk
index aac4c5c..8ad9344 100644
--- a/src/kernel/dri/fs/ext2/Jmk
+++ b/src/kernel/dri/fs/ext2/Jmk
@@ -1,3 +1,5 @@
+# -*- mode:makefile -*-
+
 init(ext2, ext2.a)
 
 preset(freestanding)
@@ -10,7 +12,7 @@
 
 CFLAGS += -I$(ROOT)/include/kernel
 
-OBJECTS = ext2.o
+OBJECTS = ext2.o ext2_vfs.o
 
 type(static_lib)
 
diff --git a/src/kernel/dri/fs/ext2/ext2.c b/src/kernel/dri/fs/ext2/ext2.c
index 9ecbe51..9952948 100644
--- a/src/kernel/dri/fs/ext2/ext2.c
+++ b/src/kernel/dri/fs/ext2/ext2.c
@@ -19,11 +19,6 @@
 };
 #undef F
 
-inline uint ext2_block_size(struct ext2_superblock *sb)
-{
-	return 1024 << sb->block_size_shift;
-}
-
 void ext2_read_block(struct ext2_superblock *sb, void *buffer, uint block)
 {
 	uint block_size = ext2_block_size(sb) / 512;
@@ -68,9 +63,26 @@
 	}
 }
 
-struct ext2_block_group_descriptor ext2_load_block_group_descriptor(
+void ext2_write_bgd(struct ext2_superblock *sb, uint block_group,
+					struct ext2_block_group_descriptor *d)
+{
+	ext2_load_or_write_bgd(sb, block_group, d, true);
+}
+
+struct ext2_block_group_descriptor ext2_load_bgd(
 	struct ext2_superblock *sb, uint block_group)
 {
+	struct ext2_block_group_descriptor bgd;
+	ext2_load_or_write_bgd(sb, block_group, &bgd, false);
+
+	return bgd;
+}
+
+void ext2_load_or_write_bgd(struct ext2_superblock *sb,
+							uint block_group,
+							struct ext2_block_group_descriptor *d,
+							bool set)
+{
 	/**
 	 * The BGDT (not to be confused with the GDT) is located the block after the
 	 * superblock. On any block size EXCEPT 1024 (the minimum, remember that the
@@ -95,9 +107,17 @@
 
 	uint lba = (block_size / 512) * bgdt_block + hd_page;
 
-	ata_pio_read_sectors(&descriptors, lba, 1);
+	ata_pio_read_sectors(descriptors, lba, 1);
 
-	return descriptors[bgd_offset];
+	if (set)
+	{
+		descriptors[bgd_offset] = *d;
+		ata_pio_write_sectors(lba, 1, (ushort *)descriptors);
+	}
+	else
+	{
+		*d = descriptors[bgd_offset];
+	}
 }
 
 static bool print_entry(uint inode, const char *name, uint l, void *sb)
@@ -194,7 +214,7 @@
 
 	// Load this from the block group descriptor table
 	struct ext2_block_group_descriptor descriptor =
-		ext2_load_block_group_descriptor(sb, block_group);
+		ext2_load_bgd(sb, block_group);
 
 	// We need to figure out what FS block the inode is on, we know how many
 	// inodes there are total in this BGD and the number per page, so this is
@@ -579,7 +599,7 @@
 	for (int bg_num = 0; bg_num < num_block_groups; bg_num++)
 	{
 		struct ext2_block_group_descriptor bgd =
-			ext2_load_block_group_descriptor(sb, 0);
+			ext2_load_bgd(sb, 0);
 
 		const uint block_size = ext2_block_size(sb);
 		// + 1 because we need to round up (ie 1025 for 1024 size blocks will
@@ -609,7 +629,7 @@
 	for (int bg_num = 0; bg_num < num_block_groups; bg_num++)
 	{
 		struct ext2_block_group_descriptor bgd =
-			ext2_load_block_group_descriptor(sb, 0);
+			ext2_load_bgd(sb, 0);
 
 		const uint block_size = ext2_block_size(sb);
 		// + 1 because we need to round up (ie 1025 for 1024 size blocks will
@@ -706,3 +726,25 @@
 
 	return d.inode;
 }
+
+uint ext2_alloc_new_block(struct ext2_superblock *sb,
+						  uint block_group)
+{
+	struct ext2_block_group_descriptor bgd =
+		ext2_load_bgd(sb, block_group);
+
+	if (bgd.unallocated_blocks == 0)
+		// TODO: handle out of blocks
+		return ext2_alloc_new_block(sb, block_group + 1);
+
+	// We can safely pass ~0 here as long as the FS is well formed
+	// because we know there is at least 1 free block
+	uint block = ext2_first_zero_bit(sb, bgd.block_bitmap, ~0, 0);
+
+	ext2_set_in_bitmap(sb, bgd.block_bitmap, block, true);
+	bgd.unallocated_blocks--;
+
+	ext2_write_bgd(sb, block_group, &bgd);
+
+	return block;
+}
diff --git a/src/kernel/dri/fs/ext2/ext2_vfs.c b/src/kernel/dri/fs/ext2/ext2_vfs.c
new file mode 100644
index 0000000..a612a4d
--- /dev/null
+++ b/src/kernel/dri/fs/ext2/ext2_vfs.c
@@ -0,0 +1,281 @@
+#include <dri/fs/ext2/ext2.h>
+#include <dri/fs/ext2/ext2_vfs.h>
+#include <sync.h>
+#include <alloc.h>
+#include <log.h>
+#include <io.h>
+
+struct ext2_fs_dirent
+{
+	struct fs_node *node;
+	char name[256];
+	uint name_len;
+	uint inode;
+
+	struct ext2_fs_dirent *next;
+};
+
+struct ext2_fs_data
+{
+	struct ext2_superblock *sb;
+
+	/// Cached inode
+	struct ext2_inode *in;
+
+	/// Reference count for the inode cache. Once this hits 0 we free
+	/// in. Only used for **files**, directories **always** cache
+	/// their inode.
+	uint rc;
+
+	semaphore_t lock;
+
+	/// Entries (if this is a directory)
+	struct ext2_fs_dirent *dirent;
+};
+
+uint ext2_file_read(struct fs_node *node, size_t ofs, size_t size,
+					uchar *buffer)
+{
+	struct ext2_fs_data *d = node->dri_res_p;
+	const uint block_size = ext2_block_size(d->sb);
+
+	// temporary buffer
+	uchar *tmp = malloc(block_size);
+
+	uint num_read = 0;
+
+	if (!buffer)
+	{
+		kprintf(WARN "ext2_file_read out of memory!\n");
+		return 0;
+	}
+
+	uint block_start = ofs / block_size;
+	uint block_offset = ofs % block_size;
+	int to_read;
+
+	do
+	{
+		// How much from this block to read
+		to_read = MIN(block_size - block_offset, size);
+		ext2_read_inode_block(d->sb, d->in, tmp, block_start);
+
+		// Copy that to the user buffer
+		memcpy(buffer, tmp + block_offset, to_read);
+		buffer += to_read;
+		size -= to_read;
+		num_read += to_read;
+
+		// Move to the next block
+		block_start++;
+		block_offset = 0;
+	}
+	while (to_read > 0);
+
+	free(tmp);
+	return num_read;
+}
+
+void ext2_file_open(struct fs_node *node)
+{
+	struct ext2_fs_data *d = node->dri_res_p;
+
+	sm_wait(d->lock);
+
+	if (d->rc++ == 0)
+	{
+		d->in = malloc(sizeof(struct ext2_inode));
+
+		if (d->in == NULL)
+		{
+			// lol
+			kpanic("ext2_file_open: ENOMEM");
+		}
+
+		if (!ext2_find_inode(d->sb, node->inode, d->in))
+		{
+			kprintf(ERROR "ext2_file_open: can't find inode %d\n",
+					node->inode);
+
+			free(d->in);
+			goto done;
+		}
+	}
+
+done:
+	sm_signal(d->lock);
+}
+
+void ext2_file_close(struct fs_node *node)
+{
+	struct ext2_fs_data *d = node->dri_res_p;
+
+	sm_wait(d->lock);
+
+	if (--d->rc == 0)
+	{
+		free(d->in);
+		d->in = NULL;
+	}
+
+	sm_signal(d->lock);
+}
+
+struct fs_vtable ext2_file_vfs_vt =
+{
+	.read = ext2_file_read,
+	.write = NULL,
+	.open = ext2_file_open,
+	.close = ext2_file_close,
+	.readdir = NULL,
+	.finddir = NULL,
+};
+
+bool ext2_dir_readdir(struct fs_node *node, uint index, struct fs_dirent *dirent)
+{
+	struct ext2_fs_data *d = node->dri_res_p;
+	struct ext2_fs_dirent *dent = d->dirent;
+
+	uint i;
+
+	for (i = 0; i < index && dent; i++, dent = dent->next)
+	{
+	}
+
+	if (i == index)
+	{
+		memcpy(dirent->name, dent->name, 256);
+		dirent->name_len = dent->name_len;
+		dirent->inode = dent->inode;
+
+		return true;
+	}
+
+	return false;
+}
+
+struct fs_node *ext2_dir_finddir(struct fs_node *node, char *name, uint name_len)
+{
+	name_len = MIN(name_len, 256);
+	struct ext2_fs_data *d = node->dri_res_p;
+	struct ext2_fs_dirent *dent = d->dirent;
+
+	for (; dent; dent = dent->next)
+	{
+		if (strncmp(dent->name, name, name_len) == 0)
+		{
+			return dent->node;
+		}
+	}
+
+	return NULL;
+}
+
+struct fs_vtable ext2_dir_vfs_vt =
+{
+	.read = NULL,
+	.write = NULL,
+	.open = NULL,
+	.close = NULL,
+	.readdir = ext2_dir_readdir,
+	.finddir = ext2_dir_finddir,
+};
+
+struct ext2_fs_dirent_to_fs_data
+{
+	struct ext2_fs_data *d;
+	struct ext2_fs_dirent *last;
+	struct ext2_superblock *sb;
+};
+
+static bool ext2_dirent_to_fs_node_cb(uint inode, const char *name,
+							   uint name_len, void *data)
+{
+	struct ext2_fs_dirent_to_fs_data *d = data;
+
+	struct ext2_fs_dirent *dent =
+		malloc(sizeof(struct ext2_fs_dirent));
+
+	dent->node = ext2_inode2vfs(d->sb, inode, (char *)name, name_len);
+	dent->name_len = name_len;
+	memcpy(dent->name, name, MIN(name_len, 256));
+	dent->inode = inode;
+
+	if (d->last)
+	{
+		d->last->next = dent;
+		d->last = dent;
+	}
+	else
+	{
+		d->last = d->d->dirent = dent;
+	}
+
+	return true;
+}
+
+struct fs_node *ext2_inode2vfs(struct ext2_superblock *sb, uint inode,
+							   char *name, uint name_len)
+{
+	struct ext2_inode in;
+
+	if (!ext2_find_inode(sb, inode, &in))
+	{
+		// Something has gone terribly wrong!
+		return NULL;
+	}
+
+	struct ext2_fs_data *d = malloc(sizeof(struct ext2_fs_data));
+
+	d->sb = sb;
+	d->rc = 0;
+	d->lock = sm_new();
+	d->in = NULL;
+	d->dirent = NULL;
+
+	struct fs_node *n = malloc(sizeof(struct fs_node));
+
+	n->name_len = MIN(name_len, FS_MAX_NAME_LEN);
+	memcpy(n->name, name, n->name_len);
+	n->inode = inode;
+	n->dri_res_p = d;
+
+	n->mask = in.mode & 0xfff;
+	n->gid = in.gid;
+	n->uid = in.uid;
+	n->size = in.size;
+
+	switch (in.mode & EXT2_F_TYPE)
+	{
+	case EXT2_S_IFREG:
+	{
+		n->flags = FS_FILE;
+		n->vtable = &ext2_file_vfs_vt;
+
+		break;
+	}
+
+	case EXT2_S_IFDIR:
+	{
+		n->flags = FS_DIRECTORY;
+		n->vtable = &ext2_dir_vfs_vt;
+
+		struct ext2_fs_dirent_to_fs_data data;
+		data.d = d;
+		data.last = NULL;
+
+		ext2_dir_ls(sb, &in, ext2_dirent_to_fs_node_cb, &data);
+
+		break;
+	}
+
+	default:
+	{
+		kprintf(ERROR "ext2_inode2vfs: unimplemented for dir type%d\n",
+				EXT2_S_TO_FT(in.mode));
+		kpanic("Unimplemented");
+	}
+	}
+
+	return n;
+}
diff --git a/src/kernel/vfs.c b/src/kernel/vfs.c
index aef955b..e3c06a0 100644
--- a/src/kernel/vfs.c
+++ b/src/kernel/vfs.c
@@ -45,13 +45,13 @@
 	return node->vtable->readdir(node, index, out);
 }
 
-struct fs_node *fs_finddir(struct fs_node *node, char *name)
+struct fs_node *fs_finddir(struct fs_node *node, char *name, uint name_len)
 {
 	if (!node || !node->vtable || !node->vtable->finddir ||
 		(node->flags & 7) != FS_DIRECTORY)
 		return NULL;
 
-	return node->vtable->finddir(node, name);
+	return node->vtable->finddir(node, name, name_len);
 }
 
 bool root_readdir(struct fs_node *node, uint index, struct fs_dirent *out)
@@ -68,9 +68,9 @@
 		return false;
 }
 
-struct fs_node *root_finddir(struct fs_node *node, char *name)
+struct fs_node *root_finddir(struct fs_node *node, char *name, uint name_len)
 {
-	if (!strcmp(name, "dev"))
+	if (!strncmp(name, "dev", name_len))
 	{
 		return &dev;
 	}
@@ -90,12 +90,12 @@
 		return false;
 }
 
-struct fs_node *dev_finddir(struct fs_node *node, char *name)
+struct fs_node *dev_finddir(struct fs_node *node, char *name, uint name_len)
 {
 	if (node != &dev)
 		return NULL;
 
-	if (!strcmp(name, "initrd"))
+	if (!strncmp(name, "initrd", MIN(name_len, FS_MAX_NAME_LEN)))
 	{
 		return &initrd;
 	}
diff --git a/src/kernel/vfs_initrd.c b/src/kernel/vfs_initrd.c
index bca0b50..30d690e 100644
--- a/src/kernel/vfs_initrd.c
+++ b/src/kernel/vfs_initrd.c
@@ -31,15 +31,20 @@
 	return true;
 }
 
-struct fs_node *initrd_finddir(struct fs_node *node, char *name)
+struct fs_node *initrd_finddir(struct fs_node *node, char *name,
+							   uint name_len)
 {
 	if (node != &real_initrd)
 		return NULL;
 
 	for (int i = 0; i < num_initrd_files; i++)
 	{
-		if (strcmp(entries[i].name, name) == 0)
+		if (strncmp(entries[i].name, name,
+					MIN(FS_MAX_NAME_LEN, name_len))
+			== 0)
+		{
 			return &initrd_content[i];
+		}
 	}
 	return NULL;
 }
@@ -58,7 +63,7 @@
 		return 0;
 
 	size = MIN(size, node->size - offset);
-	memcpy(buffer, entries[node->dri_res].data, size);
+	memcpy(buffer, entries[node->dri_res_i].data, size);
 
 	return size;
 }
@@ -104,7 +109,7 @@
 		entries[i].size = f->size;
 
 		initrd_content[i] = (struct fs_node){
-			.dri_res = i,
+			.dri_res_i = i,
 			.flags = FS_FILE,
 			.mask = 0b101001001,
 			.uid = 0,