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);