BarryServer : Git

All the code for all my projects
// BarryServer : Git / Nucleus / commit / 648c56b1573240b17ce0a6ab62746ab30e41e126 / vfs

// Related

Nucleus

Barry Read-only Ext2 file system driver 648c56b (3 years, 2 months ago)
diff --git a/vfs/ext2fs/block.c b/vfs/ext2fs/block.c
new file mode 100644
index 0000000..65ffdae
--- /dev/null
+++ b/vfs/ext2fs/block.c
@@ -0,0 +1,49 @@
+#include <stdint.h>
+#include <nucleus/panic.h>
+#include <nucleus/vfs.h>
+#include "ext2fs.h"
+
+/* Read a block of data */
+void
+ext2fs_read_block(SuperBlock *super, uint32_t index, char *buf)
+{
+	struct 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
+ext2fs_get_data_addr(SuperBlock *super, struct Ext2Inode *inode, uint32_t index)
+{
+	uint32_t tmp;
+	char block[PAGE_SIZE];
+	struct Ext2Super *rsuper = super->data;
+	/* Blocks per indirect block */
+	uint32_t bpib = (1024 << rsuper->blockSize) / sizeof(uint32_t);
+
+	/* Direct blocks */
+	if (index < 12)
+		return inode->directBlock[index];
+	index -= 12;
+
+	/* Single indirect block */
+	if (index < bpib) {
+		ext2fs_read_block(super, inode->singleBlock, block);
+		return *((uint32_t *) block + index);
+	}
+	index -= bpib;
+
+	/* Double indirect block */
+	if (index < bpib*bpib) {
+		panic("Attempted to read from double indirect block");
+	}
+	index -= bpib*bpib;
+
+	/* Triple indirect block */
+	if (index < bpib*bpib*bpib) {
+		panic("Attempted to read from triple indirect block");
+	}
+
+	return 0;
+}
diff --git a/vfs/ext2fs/ext2fs.h b/vfs/ext2fs/ext2fs.h
new file mode 100644
index 0000000..7d60d62
--- /dev/null
+++ b/vfs/ext2fs/ext2fs.h
@@ -0,0 +1,107 @@
+#ifndef VFS_EXT2FS_H
+#define VFS_EXT2FS_H
+
+/* 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));
+
+extern SuperOps ext2fsSuperOps;
+extern InodeOps ext2fsInodeOps;
+extern FileOps ext2fsFileOps;
+
+void ext2fs_read_inode(SuperBlock *sb, uint32_t index, struct Ext2Inode *res);
+void ext2fs_read_vnode(SuperBlock *sb, uint32_t index, Inode *res);
+void ext2fs_read_block(SuperBlock *super, uint32_t index, char *buf);
+uint32_t ext2fs_get_data_addr(SuperBlock *super, struct Ext2Inode *inode,
+                              uint32_t index);
+
+#endif
diff --git a/vfs/ext2fs/file.c b/vfs/ext2fs/file.c
new file mode 100644
index 0000000..d19df72
--- /dev/null
+++ b/vfs/ext2fs/file.c
@@ -0,0 +1,55 @@
+#include <string.h>
+#include <nucleus/vfs.h>
+#include "ext2fs.h"
+
+size_t ext2fs_read(File *file, char *buf, size_t size, off_t offset);
+int ext2fs_readdir(File *file, DirEntry *de, off_t index);
+int ext2fs_open(File *file);
+
+FileOps ext2fsFileOps = {
+	.read = ext2fs_read,
+//	.readdir = ext2fs_readdir,
+	.open = ext2fs_open,
+};
+
+/* Read an ext2fs file */
+size_t
+ext2fs_read(File *file, char *buf, size_t size, off_t offset)
+{
+	SuperBlock *sb = file->inode->super;
+	struct Ext2Inode inode;
+	uint16_t min, indent = offset % PAGE_SIZE;
+	size_t count = 0, i = offset / PAGE_SIZE;
+	uint32_t blk;
+	char ebuf[PAGE_SIZE];
+	ext2fs_read_inode(sb, file->inode->ino, &inode);
+	if (offset > inode.lsize)
+		return 0;
+	if (size + offset > inode.lsize)
+		size = inode.lsize - offset;
+	while (size) {
+		min = (size > PAGE_SIZE) ? PAGE_SIZE : size;
+		blk = ext2fs_get_data_addr(sb, &inode, i);
+		ext2fs_read_block(sb, blk, ebuf);
+		memcpy(buf + count, ebuf + indent, min);
+		size -= min;
+		count += min;
+		indent = 0;
+		i++;
+	}
+	return count;
+}
+
+/* Read an ext2fs directory entry */
+int
+ext2fs_readdir(File *file, DirEntry *de, off_t index)
+{
+	return 0;
+}
+
+/* Open an ext2fs file */
+int
+ext2fs_open(File *file)
+{
+	return 0;
+}
diff --git a/vfs/ext2fs/inode.c b/vfs/ext2fs/inode.c
new file mode 100644
index 0000000..2208ab6
--- /dev/null
+++ b/vfs/ext2fs/inode.c
@@ -0,0 +1,85 @@
+#include <stdint.h>
+#include <string.h>
+#include <nucleus/vfs.h>
+#include "ext2fs.h"
+
+Inode *ext2fs_lookup(Inode *vnode, const char *name);
+
+InodeOps ext2fsInodeOps = {
+	.lookup = ext2fs_lookup,
+};
+
+/* Read an ext2fs inode */
+void
+ext2fs_read_inode(SuperBlock *sb, uint32_t index, struct Ext2Inode *res)
+{
+	struct Ext2Super *rsuper = sb->data;
+
+	/* Find the Block Group Descriptor */
+	struct 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++;
+	sb->back->pos = (rsuper->blockSize ? 1 : 2)
+	              * (1024 << rsuper->blockSize);
+	sb->back->pos += sizeof(struct Ext2BlockGroupDesc) * group;
+	file_read(sb->back, (char *) &bgd, sizeof(struct Ext2BlockGroupDesc));
+
+	/* Read Inode */
+	sb->back->pos = bgd.inodeTable * (1024 << rsuper->blockSize);
+	sb->back->pos += rsuper->inodeSize * index;
+	file_read(sb->back, (char *) res, sizeof(struct Ext2Inode));
+}
+
+/* Read an ext2fs inode as a VFS Inode */
+void
+ext2fs_read_vnode(SuperBlock *sb, uint32_t index, Inode *res)
+{
+	struct Ext2Inode inode;
+	ext2fs_read_inode(sb, index, &inode);
+
+	res->ino = index;
+	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 = sb;
+}
+
+/* Lookup an ext2fs file */
+Inode *
+ext2fs_lookup(Inode *vnode, const char *name)
+{
+	char buf[PAGE_SIZE];
+	uint32_t block, blk = 0;
+	struct Ext2DirEntry *de;
+	struct Ext2Inode inode;
+	ext2fs_read_inode(vnode->super, vnode->ino, &inode);
+	for (blk = 0; blk < 0xFFFF; blk++) {
+		block = ext2fs_get_data_addr(vnode->super, &inode, blk);
+		if (!block)
+			return NULL;
+		ext2fs_read_block(vnode->super, block, buf);
+		for (de = (struct Ext2DirEntry *) buf;
+		     strncmp(de->name, name, de->namelen)
+		     && de < (struct Ext2DirEntry *) (buf + PAGE_SIZE);
+		     de = (void *) de + de->size);
+		if (de >= (struct Ext2DirEntry *) (buf + PAGE_SIZE))
+			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) {
+		res = new(&inodeType);
+		ext2fs_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..f7949f0
--- /dev/null
+++ b/vfs/ext2fs/super.c
@@ -0,0 +1,67 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <nucleus/task.h>
+#include <nucleus/vfs.h>
+#include "ext2fs.h"
+
+Inode *ext2fs_alloc_inode(SuperBlock *sb);
+
+const uint16_t EXT2_SIGNATURE = 0xEF53;
+
+SuperOps ext2fsSuperOps = {
+	.alloc_inode = ext2fs_alloc_inode,
+};
+
+/* Mount an ext2fs instance */
+Inode *
+ext2fs_mount(FSType *type, int flags, const char *dev, void *data)
+{
+	/* Open backing file */
+	int fd = open(dev, O_RDONLY);
+	if (fd < 0)
+		return (void *) fd;
+	File *back = get(get_file_by_fd(fd));
+	close(fd);
+	if (!S_ISBLK(back->inode->mode)) {
+		put(back);
+		return (void *) -ENOTBLK;
+	}
+
+	/* Read super block */
+	struct Ext2Super *rsuper = kmalloc(sizeof(struct Ext2Super));
+	back->pos = 1024;
+	file_read(back, (char *) rsuper, sizeof(struct Ext2Super));
+	if (rsuper->signature != EXT2_SIGNATURE)
+		goto error;
+	if (rsuper->requiredFeatures & 0x01)
+		/* Compression required */
+		goto error;
+	if ((rsuper->writableFeatures & 0x02) && !(flags & MS_RDONLY))
+		/* 64-bit file system */
+		goto error;
+
+	SuperBlock *super = new(&superBlockType);
+	super->type = get(type);
+	super->ops = &ext2fsSuperOps;
+	super->back = back;
+	super->data = rsuper;
+	super->root = new(&inodeType);
+	ext2fs_read_vnode(super, 2, super->root);
+
+	return super->root;
+
+error:
+	put(back);
+	kfree(rsuper);
+	return (void *) -EINVAL;
+}
+
+/* Allocate an inode */
+Inode *
+ext2fs_alloc_inode(SuperBlock *sb)
+{
+	return NULL;
+}
diff --git a/vfs/vfs.c b/vfs/vfs.c
index 303f9c7..cb80052 100644
--- a/vfs/vfs.c
+++ b/vfs/vfs.c
@@ -13,6 +13,7 @@ extern FileOps tmpfsFileOps;
 
 Inode *tmpfs_mount(FSType *type, int flags, const char *dev, void *data);
 Inode *devfs_mount(FSType *type, int flags, const char *dev, void *data);
+Inode *ext2fs_mount(FSType *type, int flags, const char *dev, void *data);
 
 /* Initialise the Virtual File System */
 void
@@ -20,6 +21,7 @@ init_vfs(void)
 {
 	register_fstype("tmpfs", tmpfs_mount);
 	register_fstype("devfs", devfs_mount);
+	register_fstype("ext2fs", ext2fs_mount);
 
 	mount("tmpfs", NULL, "tmpfs", 0, NULL);
 	mkdir("dev", 0);