Add listing to EXT2
diff --git a/README.md b/README.md
index 774e14f..0db66bc 100644
--- a/README.md
+++ b/README.md
@@ -37,6 +37,9 @@
   - [x] Initial ramdisk
   - [ ] Filesystem drivers
     - [ ] EXT2 (in progress)
+      - [x] Read
+      - [ ] Write
+      - [ ] Consistency check (`fsck`)
     - [ ] FAT32
 - [ ] System call API
   - [ ] Filesystem API
diff --git a/include/kernel/dri/fs/ext2/ext2.h b/include/kernel/dri/fs/ext2/ext2.h
index 4205c4b..0ffeaac 100644
--- a/include/kernel/dri/fs/ext2/ext2.h
+++ b/include/kernel/dri/fs/ext2/ext2.h
@@ -4,55 +4,79 @@
 
 struct ext2_superblock
 {
-	// Total number of inodes
+	/// Total number of inodes
 	uint total_inodes;
-	// Total number of blocks
+
+	/// Total number of blocks
 	uint total_blocks;
-	// Number of blocks reserved for superuser
+
+	/// Number of blocks reserved for superuser
 	uint blocks_reserved_for_superuser;
-	// Number of unallocated blocks
+
+	/// Number of unallocated blocks
 	uint unallocated_blocks;
-	// Number of unallocated inodes
+
+	/// Number of unallocated inodes
 	uint unallocated_inodes;
-	// Block number containing the superblock
+
+	/// Block number containing the superblock
 	uint superblock_block_number;
-	// the number to shift 1024 to the left by to get the block size
+
+	/// the number to shift 1024 to the left by to get the block size
 	uint block_size_shift;
-	// the number to shift 1024 to the left by to get the fragment size
+
+	/// the number to shift 1024 to the left by to get the fragment size
 	uint fragment_size_shift;
-	// Number of blocks in one block group
+
+	/// Number of blocks in one block group
 	uint blocks_per_block_group;
-	// Number of fragments in one block group
+
+	/// Number of fragments in one block group
 	uint fragments_per_block_group;
-	// Number of inodse in one block group
+
+	/// Number of inodse in one block group
 	uint inodes_per_block_group;
-	// UNIX time of last mount
+
+	/// UNIX time of last mount
 	uint last_mount_time;
-	// UNIX time of last write
+
+	/// UNIX time of last write
 	uint last_write_time;
-	// Number of mounts since last consistency check
+
+	/// Number of mounts since last consistency check
 	ushort mounts_since_last_fsck;
-	// Number of mounts allowed between consistency checks
+
+	/// Number of mounts allowed between consistency checks
 	ushort mounts_allowed_before_fsck;
-	// EXT2 signature, should be 0xef53
+
+	/// EXT2 signature, should be 0xef53
 	ushort signature;
-	// File system state, see enum ext2_fs_state
+
+	/// File system state, see enum ext2_fs_state
 	ushort state;
-	// What to do in case of an error, see enum ext2_error_case
+
+	/// What to do in case of an error, see enum ext2_error_case
 	ushort error_case;
-	// Minor portion of version
+
+	/// Minor portion of version
 	ushort version_minor;
-	// UNIX time of last consistency check
+
+	/// UNIX time of last consistency check
 	uint last_fsck_time;
-	// Max time between consistency checks
+
+	/// Max time between consistency checks
 	uint fsck_time_interval;
-	// Operating system ID of creator
+
+	/// Operating system ID of creator
 	uint creator_os_id;
-	// Major portion of version
+
+	/// Major portion of version
 	uint version_major;
-	// User ID that can use reserved blocks
+
+	/// User ID that can use reserved blocks
 	ushort reserved_blocks_uid;
-	// Group ID that can use reserved blocks
+
+	/// Group ID that can use reserved blocks
 	ushort reserved_blocks_gid;
 };
 
@@ -80,6 +104,177 @@
 
 #define EXT2_SIGNATURE 0xef53
 
+struct ext2_block_group_descriptor
+{
+	/// Address of block usage bitmap
+	uint block_bitmap;                           // 4
+
+	/// Address of inode usage bitmap
+	uint inode_bitmap;                           // 8
+
+	/// Starting block address of inode table
+	uint inode_table_start_block;                // 12
+
+	/// Number of unallocated blocks in this group
+	ushort unallocated_blocks;                   // 14
+
+	/// Number of unallocated inodes in this group
+	ushort unallocated_inodes;                   // 16
+
+	/// Number of directories in this group
+	ushort num_dirs;                             // 18
+
+	ushort padding;
+
+	uchar reserved[12];                          // 32
+};
+
+struct ext2_inode
+{
+	/// The format and permissions of the file, see EXT2_S_*
+	ushort mode;
+	
+	/// The user id of the owner
+	ushort uid;
+
+	/// The lower 32 bits of the file size
+	uint size;
+
+	/// Last time this was accessed in UNIX time
+	uint access_time;
+
+	/// Time this was created in UNIX time
+	uint created_time;
+
+	/// Time this was last modified in UNIX time
+	uint modified_time;
+
+	/// Time this was deleted in UNIX time
+	uint deleted_time;
+
+	/// Owner group id
+	ushort gid;
+
+	/// Reference count for hard links
+	ushort links_count;
+
+	/// Number of 512 byte blocks storing this files data, NOT EXT2 blocks!
+	uint num_blocks;
+
+	/// Flags describing how this file can be accessed
+	uint flags;
+
+	/// First field reserved for the operating system
+	uint os_reserved_1;
+
+	union
+	{
+		/// The blocks of this file
+		uint blocks[15];
+
+		struct
+		{
+			/// The direct blocks
+			uint direct_blocks[12];
+
+			/// The single indirect block
+			uint ind_block;
+
+			/// The double indirect block
+			uint ind2_block;
+
+			/// The triple indirect block
+			uint ind3_block;
+		};
+	};
+
+	/// Used by NFS to version files
+	uint generation;
+
+	/// File attributes, unused
+	uint file_acl;
+
+	/// Directory attributes, unused
+	uint dir_acl;
+
+	/// Location of file fragment, unused
+	uint faddr;
+
+	/// Second field reserved for the operating system
+	uchar os_reserved_2[12];
+};
+
+enum
+{
+	// File types
+	EXT2_S_IFSOCK = 0xc000,
+	EXT2_S_IFLINK = 0xa000,
+	EXT2_S_IFREG = 0x8000,
+	EXT2_S_IFBLK = 0x6000,
+	EXT2_S_IFDIR = 0x4000,
+	EXT2_S_IFCHR = 0x2000,
+	EXT2_S_IFIFO = 0x1000,
+
+	/// Set UID
+	EXT2_S_ISUID = 0x0800,
+	/// Set GUID
+	EXT2_S_ISGID = 0x0400,
+	/// Sticky
+	EXT2_S_ISVTX = 0x0200,
+
+	// Normal UNIX file permissions
+	EXT2_S_IRUSR = 0x0100,
+	EXT2_S_IWUSR = 0x0080,
+	EXT2_S_IXUSR = 0x0040,
+	EXT2_S_IRGRP = 0x0020,
+	EXT2_S_IWGRP = 0x0010,
+	EXT2_S_IXGRP = 0x0008,
+	EXT2_S_IROTH = 0x0004,
+	EXT2_S_IWOTH = 0x0002,
+	EXT2_S_IXOTH = 0x0001,
+};
+
+struct ext2_dirent
+{
+	uint inode;
+	ushort rec_len;
+	uchar name_len;
+	uchar file_type;
+	char name[];
+};
+
+/// 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, uint block);
+
 struct ext2_superblock ext2_read_superblock();
+
+void ext2_write_superblock(struct ext2_superblock *sb);
+
+// Just for fun, corrupt the superblock (obviously don't actually do this)
+void ext2_corrupt_superblock_for_fun();
+
 void ext2_mount(struct fs_node *where);
+
 bool ext2_valid_filesystem();
+
+bool ext2_find_inode(struct ext2_superblock *sb, uint number, 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_superblock *sb, uint block_group);
+
+/// List the contents of a directory dir. Calls `cb` for each item. If `dir` is
+/// not a directory, returns false. Otherwise returns true.
+bool ext2_dir_ls(struct ext2_superblock *sb,
+				 struct ext2_inode *dir,
+				 void (*cb)(uint inode,
+							const char *name,
+							void *data),
+				 void *data);
+
+/// Read a data block for the inode, 0 being the first block of the file.
+bool ext2_read_inode_block(struct ext2_superblock *sb,
+						   struct ext2_inode *inode,
+						   void *buffer,
+						   uint block);
diff --git a/include/kernel/log.h b/include/kernel/log.h
index 3365474..a4ae42b 100644
--- a/include/kernel/log.h
+++ b/include/kernel/log.h
@@ -5,6 +5,7 @@
 #define RESET "\033[0m"
 #define INFO "[\033[96m INFO  " RESET "] "
 #define OKAY "[\033[92m  OK   " RESET "] "
+#define WARN "[\033[93m WARN  " RESET "] "
 #define ERROR "[\033[91m ERROR " RESET "] "
 #define DEBUG "[\033[93m DEBUG " RESET "] "
 
diff --git a/src/kernel/Jmk b/src/kernel/Jmk
index cbce0a2..05e2b8e 100644
--- a/src/kernel/Jmk
+++ b/src/kernel/Jmk
@@ -1,7 +1,8 @@
 init(kernel, kernel.elf)
 
 preset(freestanding)
-preset(optimize)
+# This makes debugging hard :(
+# preset(optimize)
 preset(debug)
 preset(32)
 preset(warn)
@@ -60,7 +61,7 @@
 type(custom_link)
 
 debug-wait: kernel.elf
-	qemu-system-i386 -s -S -kernel kernel.elf
+	qemu-system-i386 -s -S $(QEMUFLAGS) -kernel kernel.elf
 
 debug: kernel.elf
 	qemu-system-i386 -s -S $(QEMUFLAGS) -kernel kernel.elf &
diff --git a/src/kernel/dri/fs/ext2/ext2.c b/src/kernel/dri/fs/ext2/ext2.c
index 31d3b26..3a1e183 100644
--- a/src/kernel/dri/fs/ext2/ext2.c
+++ b/src/kernel/dri/fs/ext2/ext2.c
@@ -2,6 +2,20 @@
 #include <dri/fs/ext2/ext2.h>
 #include <kint.h>
 #include <log.h>
+#include <io.h>
+
+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;
+	uint block_start = block_size * block;
+
+	ata_pio_read_sectors(buffer, block_start, block_size);
+}
 
 struct ext2_superblock ext2_read_superblock()
 {
@@ -31,13 +45,8 @@
 	}
 }
 
-uint ext2_block_size(struct ext2_superblock *sb)
-{
-	return 1024 << sb->block_size_shift;
-}
-
-void ext2_load_block_group_descriptor_table(struct ext2_superblock *sb,
-											uint num_block_groups)
+struct ext2_block_group_descriptor ext2_load_block_group_descriptor(
+	struct ext2_superblock *sb, uint block_group)
 {
 	/**
 	 * The BGDT (not to be confused with the GDT) is located the block after the
@@ -51,7 +60,26 @@
 	if (block_size == 1024)
 		bgdt_block = 2;
 
-	kprintf(DEBUG "BGDT block = %d block size = %d\n", bgdt_block, block_size);
+	const uint bgd_size = sizeof(struct ext2_block_group_descriptor);
+
+	// Disk page that the BGD is on relative to the initial FILE SYSTEM block
+	uint hd_page = block_group / (512 / bgd_size);
+	// The offset from the beginning of that page the BGD is at
+	uint bgd_offset = block_group % (512 / bgd_size);
+
+	struct ext2_block_group_descriptor descriptors[512 / bgd_size];
+	kassert(sizeof(descriptors) == 512, "Wrong BGD size");
+
+	uint lba = (block_size / 512) * bgdt_block + hd_page;
+
+	ata_pio_read_sectors(&descriptors, lba, 1);
+
+	return descriptors[bgd_offset];
+}
+
+static void print_entry(uint inode, const char *name, void *data)
+{
+	kprintf("%d\t %s\n", inode, name);
 }
 
 void ext2_mount(struct fs_node *where)
@@ -59,9 +87,25 @@
 	struct ext2_superblock sb = ext2_read_superblock();
 
 	kprintf(DEBUG "EXT2 magic = 0x%x\n", sb.signature);
+	
+	// Read the root inode 2
+	struct ext2_inode root;
 
-	uint num_block_groups = ext2_num_block_groups(&sb);
-	ext2_load_block_group_descriptor_table(&sb, num_block_groups);
+	if (ext2_find_inode(&sb, 2, &root))
+	{
+		kprintf(OKAY "Found root inode 2 (size=0x%x, num_blocks=0x%x)\n", root.size, root.num_blocks);
+		// kprintf(DEBUG "Root.mode = 0x%x\n", root.mode & 0xf000);
+		kassert((root.mode & 0xf000) == EXT2_S_IFDIR, "Root (inode 2) is not a directory.");
+
+		kprintf("ls /\n");
+		kprintf("inode\t name\n");
+		kprintf("--------------------\n");
+		ext2_dir_ls(&sb, &root, print_entry, NULL);
+	}
+	else
+	{
+		kprintf(WARN "Failed to find root inode 2\n");
+	}
 }
 
 bool ext2_valid_filesystem()
@@ -72,3 +116,111 @@
 
 	return sb.signature == EXT2_SIGNATURE;
 }
+
+void ext2_write_superblock(struct ext2_superblock *sb)
+{
+	ushort *wp = (ushort *)sb;
+
+	ata_pio_write_sectors(2, 2, wp);
+}
+
+void ext2_corrupt_superblock_for_fun()
+{
+	struct ext2_superblock sb = ext2_read_superblock();
+	sb.signature = 0xDEAD;
+	ext2_write_superblock(&sb);
+}
+
+bool ext2_find_inode(struct ext2_superblock *sb, uint number, struct ext2_inode *inode)
+{
+	if (number == 0)
+		return false;
+
+	uint block_group = (number - 1) / sb->inodes_per_block_group;
+	uint local_index = (number - 1) % sb->inodes_per_block_group;
+
+	// Load this from the block group descriptor table
+	struct ext2_block_group_descriptor descriptor =
+		ext2_load_block_group_descriptor(sb, block_group);
+
+	kprintf(DEBUG "Descriptor inode_table = 0x%x\n", descriptor.inode_table_start_block);
+
+	// 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
+	// simple.
+
+	const uint block_size = ext2_block_size(sb);
+
+	const uint inodes_per_block = block_size / sizeof(struct ext2_inode);
+
+	uint inode_block = local_index / inodes_per_block;
+	uint inode_index = local_index % inodes_per_block;
+
+	struct ext2_inode inodes[block_size / sizeof(struct ext2_inode)];
+
+	ext2_read_block(sb, inodes, descriptor.inode_table_start_block +
+								inode_block);
+
+	*inode = inodes[inode_index];
+
+	return true;
+}
+
+bool ext2_dir_ls(struct ext2_superblock *sb,
+				 struct ext2_inode *dir,
+				 void (*cb)(uint inode,
+							const char *name,
+							void *data),
+				 void *data)
+{
+	if ((dir->mode & 0xf000) != EXT2_S_IFDIR)
+		return false;
+
+	for (int i = 0; i < dir->num_blocks; i++)
+	{
+		uchar buffer[ext2_block_size(sb)];
+		ext2_read_inode_block(sb, dir, buffer, i);
+
+		struct ext2_dirent *ent = (void *)buffer;
+
+		// While there are files in this block
+		while (ent < buffer + ext2_block_size(sb))
+		{
+			if (ent->inode == 0)
+				return true;
+
+			if (cb)
+			{
+				char name[257];
+
+				memcpy(name, ent->name, ent->name_len);
+				name[ent->name_len] = '\0';
+				
+				cb(ent->inode, name, data);
+			}
+
+			ent = (void *)(((uint)(void *)ent) + ent->rec_len);
+		}
+		// We ran out of files in this block, continue to the next one. This
+		// works because files cannot span blocks
+	}
+
+	return true;
+}
+
+bool ext2_read_inode_block(struct ext2_superblock *sb,
+						   struct ext2_inode *inode,
+						   void *buffer,
+						   uint block)
+{
+	if (block >= 12)
+	{
+		kprintf(ERROR "Sorry, EXT2 can only access the first 12 (direct) blocks "
+					  "of an inode for now. Indirect look-up will be added later\n");
+		kpanic("Invalid inode block");
+	}
+
+	uint block_address = inode->blocks[block];
+
+	ext2_read_block(sb, buffer, block_address);
+}
diff --git a/src/kernel/main.c b/src/kernel/main.c
index 53fb71e..59fc111 100644
--- a/src/kernel/main.c
+++ b/src/kernel/main.c
@@ -101,19 +101,15 @@
 	test_ata_pio();
 #endif
 
-#if 1 // ifdef TEST_EXT2
 	if (ext2_valid_filesystem())
 	{
-		kprintf(OKAY "EXT2 filesystem is valid, again = %b\n", ext2_valid_filesystem());
-
-		//kprintf(INFO "Mounting EXT2 to /\n");
-		//ext2_mount(&root);
+		kprintf(OKAY "EXT2 filesystem is valid\n");
+		ext2_mount(&root);
 	}
-
-	// Try reading the regions that the ext2 needs
-	// uchar buffer[1024];
-	// ata_pio_read_sectors(buffer, 2, 2);
-#endif
+	else
+	{
+		kprintf(WARN "Filesystem is not a valid EXT2 format, only EXT2 is supported\n");
+	}
 
 	while (true)
 		asm("hlt");