Add ext2 VFS implementation
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,