aboutsummaryrefslogtreecommitdiff
path: root/src/kernel/fs/ext2/ext2.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/kernel/fs/ext2/ext2.c')
-rw-r--r--src/kernel/fs/ext2/ext2.c797
1 files changed, 797 insertions, 0 deletions
diff --git a/src/kernel/fs/ext2/ext2.c b/src/kernel/fs/ext2/ext2.c
new file mode 100644
index 0000000..fe45547
--- /dev/null
+++ b/src/kernel/fs/ext2/ext2.c
@@ -0,0 +1,797 @@
+#include <stddef.h>
+#include <stdint.h>
+#include <kernel/system.h>
+#include <kernel/lib/stdlib.h>
+#include <kernel/lib/stdio.h>
+#include <kernel/lib/lib.h>
+#include <kernel/memory/alloc.h>
+#include <kernel/fs/ext2/ext2.h>
+
+uint32_t ext2_file_size(vfs_node_t *node)
+{
+ ext2_fs_t *ext2fs = node->device;
+ inode_t *inode = kmalloc(sizeof(inode_t));
+ read_inode_metadata(ext2fs, inode, node->inode_num);
+ uint32_t ret = inode->size;
+ kfree(inode);
+ return ret;
+}
+
+void ext2_mkdir(vfs_node_t *parent, char *name, uint16_t permission)
+{
+ ext2_fs_t *ext2fs = parent->device;
+ uint32_t inode_idx = alloc_inode(ext2fs);
+ inode_t *inode = kmalloc(sizeof(inode_t));
+ read_inode_metadata(ext2fs, inode, inode_idx);
+ inode->permission = EXT2_S_IFDIR;
+ inode->permission |= 0xFFF & permission;
+ inode->atime = 0;
+ inode->ctime = 0;
+ inode->dtime = 0;
+ inode->gid = 0;
+ inode->userid = 0;
+ inode->f_block_addr = 0;
+ inode->num_sectors = 0;
+ inode->size = ext2fs->block_size;
+ inode->hard_links = 2;
+ inode->flags = 0;
+ inode->file_acl = 0;
+ inode->dir_acl = 0;
+ inode->generation = 0;
+ inode->os_specific1 = 0;
+ memset(inode->blocks, 0, sizeof(inode->blocks));
+ memset(inode->os_specific2, 0, 12);
+ alloc_inode_block(ext2fs, inode, inode_idx, 0);
+ write_inode_metadata(ext2fs, inode, inode_idx);
+ ext2_create_entry(parent, name, inode_idx);
+
+ inode_t *p_inode = kmalloc(sizeof(inode_t));
+ read_inode_metadata(ext2fs, p_inode, parent->inode_num);
+ p_inode->hard_links++;
+ write_inode_metadata(ext2fs, p_inode, parent->inode_num);
+ rewrite_bgds(ext2fs);
+}
+
+void ext2_mkfile(vfs_node_t *parent, char *name, uint16_t permission)
+{
+ ext2_fs_t *ext2fs = parent->device;
+ uint32_t inode_idx = alloc_inode(ext2fs);
+ inode_t *inode = kmalloc(sizeof(inode_t));
+ read_inode_metadata(ext2fs, inode, inode_idx);
+ inode->permission = EXT2_S_IFREG;
+ inode->permission |= 0xFFF & permission;
+ inode->atime = 0;
+ inode->ctime = 0;
+ inode->dtime = 0;
+ inode->gid = 0;
+ inode->userid = 0;
+ inode->f_block_addr = 0;
+ inode->num_sectors = 0;
+ inode->size = ext2fs->block_size;
+ inode->hard_links = 2;
+ inode->flags = 0;
+ inode->file_acl = 0;
+ inode->dir_acl = 0;
+ inode->generation = 0;
+ inode->os_specific1 = 0;
+ memset(inode->blocks, 0, sizeof(inode->blocks));
+ memset(inode->os_specific2, 0, 12);
+ alloc_inode_block(ext2fs, inode, inode_idx, 0);
+ write_inode_metadata(ext2fs, inode, inode_idx);
+ ext2_create_entry(parent, name, inode_idx);
+
+ inode_t *p_inode = kmalloc(sizeof(inode_t));
+ read_inode_metadata(ext2fs, p_inode, parent->inode_num);
+ p_inode->hard_links++;
+ write_inode_metadata(ext2fs, p_inode, parent->inode_num);
+ rewrite_bgds(ext2fs);
+}
+
+void ext2_unlink(vfs_node_t *parent, char *name)
+{
+ ext2_fs_t *ext2fs = parent->device;
+ ext2_remove_entry(parent, name);
+
+ inode_t *p_inode = kmalloc(sizeof(inode_t));
+ read_inode_metadata(ext2fs, p_inode, parent->inode_num);
+ p_inode->hard_links--;
+ write_inode_metadata(ext2fs, p_inode, parent->inode_num);
+ rewrite_bgds(ext2fs);
+}
+
+char **ext2_listdir(vfs_node_t *parent)
+{
+ ext2_fs_t *ext2fs = parent->device;
+ inode_t *p_inode = kmalloc(sizeof(inode_t));
+ read_inode_metadata(ext2fs, p_inode, parent->inode_num);
+ uint32_t curr_offset = 0;
+ uint32_t block_offset = 0;
+ uint32_t in_block_offset = 0;
+ int size = 0, cap = 10;
+ char **ret = kmalloc(sizeof(char *) * cap);
+ char *block_buf = read_inode_block(ext2fs, p_inode, block_offset);
+ while (curr_offset < p_inode->size) {
+ if (in_block_offset >= ext2fs->block_size) {
+ block_offset++;
+ in_block_offset = 0;
+ block_buf = read_inode_block(ext2fs, p_inode, block_offset);
+ }
+ if (size + 1 == cap) {
+ ret = krealloc(ret, sizeof(char *) * cap * 2);
+ cap = cap * 2;
+ }
+
+ direntry_t *curr_dir = (direntry_t *)(block_buf + in_block_offset);
+ if (curr_dir->inode != 0) {
+ char *temp = kcalloc(curr_dir->name_len + 1, 1);
+ memcpy(temp, curr_dir->name, curr_dir->name_len);
+ ret[size++] = temp;
+ }
+ uint32_t expected_size =
+ ((sizeof(direntry_t) + curr_dir->name_len) & 0xfffffffc) + 0x4;
+ uint32_t real_size = curr_dir->size;
+ if (real_size != expected_size) {
+ break;
+ }
+ in_block_offset += curr_dir->size;
+ curr_offset += curr_dir->size;
+ }
+ ret[size] = NULL;
+ return ret;
+}
+
+vfs_node_t *vfsnode_from_direntry(ext2_fs_t *ext2fs, direntry_t *dir, inode_t *inode)
+{
+ vfs_node_t *ret = kcalloc(sizeof(vfs_node_t), 1);
+
+ ret->device = (void *)ext2fs;
+ ret->inode_num = dir->inode;
+ memcpy(ret->name, dir->name, dir->name_len);
+
+ ret->uid = inode->userid;
+ ret->uid = inode->gid;
+ ret->size = inode->size;
+ ret->mask = inode->permission & 0xFFF;
+ ret->nlink = inode->hard_links;
+
+ ret->flags = 0;
+ if ((inode->permission & EXT2_S_IFREG) == EXT2_S_IFREG) {
+ ret->flags |= FS_FILE;
+ ret->read = ext2_read;
+ ret->write = ext2_write;
+ ret->unlink = ext2_unlink;
+ ret->get_file_size = ext2_file_size;
+ }
+ if ((inode->permission & EXT2_S_IFDIR) == EXT2_S_IFDIR) {
+ ret->flags |= FS_DIRECTORY;
+ ret->mkdir = ext2_mkdir;
+ ret->finddir = ext2_finddir;
+ ret->unlink = ext2_unlink;
+ ret->create = ext2_mkfile;
+ ret->listdir = ext2_listdir;
+ ret->read = ext2_read;
+ ret->write = ext2_write;
+ }
+ if ((inode->permission & EXT2_S_IFBLK) == EXT2_S_IFBLK) {
+ ret->flags |= FS_BLOCKDEVICE;
+ }
+ if ((inode->permission & EXT2_S_IFCHR) == EXT2_S_IFCHR) {
+ ret->flags |= FS_CHARDEVICE;
+ }
+ if ((inode->permission & EXT2_S_IFIFO) == EXT2_S_IFIFO) {
+ ret->flags |= FS_PIPE;
+ }
+ if ((inode->permission & EXT2_S_IFLNK) == EXT2_S_IFLNK) {
+ ret->flags |= FS_SYMLINK;
+ }
+
+ ret->access_time = inode->atime;
+ ret->modified_time = inode->mtime;
+ ret->create_time = inode->ctime;
+
+ ret->chmod = ext2_chmod;
+ ret->open = ext2_open;
+ ret->close = ext2_close;
+ return ret;
+}
+
+vfs_node_t *ext2_finddir(vfs_node_t *parent, char *name)
+{
+ ext2_fs_t *ext2fs = parent->device;
+ inode_t *p_inode = kmalloc(sizeof(inode_t));
+ read_inode_metadata(ext2fs, p_inode, parent->inode_num);
+ uint32_t expected_size;
+ uint32_t real_size;
+ uint32_t curr_offset = 0;
+ uint32_t block_offset = 0;
+ uint32_t in_block_offset = 0;
+ char *block_buf = read_inode_block(ext2fs, p_inode, block_offset);
+ while (curr_offset < p_inode->size) {
+ if (in_block_offset >= ext2fs->block_size) {
+ block_offset++;
+ in_block_offset = 0;
+ block_buf = read_inode_block(ext2fs, p_inode, block_offset);
+ }
+
+ direntry_t *curr_dir = (direntry_t *)(block_buf + in_block_offset);
+ char *temp = kcalloc(curr_dir->name_len + 1, 1);
+ memcpy(temp, curr_dir->name, curr_dir->name_len);
+ if (curr_dir->inode != 0 && !strcmp(temp, name)) {
+ inode_t *inode = kmalloc(sizeof(inode_t));
+ read_inode_metadata(ext2fs, inode, curr_dir->inode);
+ return vfsnode_from_direntry(ext2fs, curr_dir, inode);
+ }
+ if (((sizeof(direntry_t) + curr_dir->name_len) & 0x00000003) != 0)
+ expected_size =
+ ((sizeof(direntry_t) + curr_dir->name_len) & 0xfffffffc) + 0x4;
+ else
+ expected_size = ((sizeof(direntry_t) + curr_dir->name_len) & 0xfffffffc);
+ real_size = curr_dir->size;
+ if (real_size != expected_size) {
+ break;
+ }
+ in_block_offset += curr_dir->size;
+ curr_offset += curr_dir->size;
+ }
+ return NULL;
+}
+
+void ext2_create_entry(vfs_node_t *parent, char *entry_name, uint32_t entry_inode)
+{
+ ext2_fs_t *ext2fs = parent->device;
+ inode_t *p_inode = kmalloc(sizeof(inode_t));
+ read_inode_metadata(ext2fs, p_inode, parent->inode_num);
+ uint32_t curr_offset = 0;
+ uint32_t block_offset = 0;
+ uint32_t in_block_offset = 0;
+ uint32_t found = 0;
+ uint32_t entry_name_len = strlen(entry_name);
+ char *check = kcalloc(entry_name_len + 1, 1);
+ char *block_buf = read_inode_block(ext2fs, p_inode, block_offset);
+
+ while (curr_offset < p_inode->size) {
+ if (in_block_offset >= ext2fs->block_size) {
+ block_offset++;
+ in_block_offset = 0;
+ block_buf = read_inode_block(ext2fs, p_inode, block_offset);
+ }
+ direntry_t *curr_dir = (direntry_t *)(block_buf + in_block_offset);
+ if (curr_dir->name_len == entry_name_len) {
+ memcpy(check, curr_dir->name, entry_name_len);
+ if (curr_dir->inode != 0 && !strcmp(entry_name, check)) {
+ log("Entry by the same name %s already exist\n", check);
+ return;
+ }
+ }
+ if (found) {
+ curr_dir->inode = entry_inode;
+ curr_dir->size =
+ (uint32_t)block_buf + ext2fs->block_size - (uint32_t)curr_dir;
+ curr_dir->name_len = strlen(entry_name);
+ curr_dir->type = 0;
+ memcpy(curr_dir->name, entry_name, strlen(entry_name));
+ write_inode_block(ext2fs, p_inode, block_offset, block_buf);
+ in_block_offset += curr_dir->size;
+ if (in_block_offset >= ext2fs->block_size) {
+ block_offset++;
+ in_block_offset = 0;
+ block_buf = read_inode_block(ext2fs, p_inode, block_offset);
+ }
+ curr_dir = (direntry_t *)(block_buf + in_block_offset);
+ memset(curr_dir, 0, sizeof(direntry_t));
+ write_inode_block(ext2fs, p_inode, block_offset, block_buf);
+ return;
+ }
+ uint32_t expected_size =
+ ((sizeof(direntry_t) + curr_dir->name_len) & 0xfffffffc) + 0x4;
+ uint32_t real_size = curr_dir->size;
+ if (real_size != expected_size) {
+ found = 1;
+ curr_dir->size = expected_size;
+ in_block_offset += expected_size;
+ curr_offset += expected_size;
+ continue;
+ }
+ in_block_offset += curr_dir->size;
+ curr_offset += curr_dir->size;
+ }
+}
+
+void ext2_remove_entry(vfs_node_t *parent, char *entry_name)
+{
+ ext2_fs_t *ext2fs = parent->device;
+ inode_t *p_inode = kmalloc(sizeof(inode_t));
+ read_inode_metadata(ext2fs, p_inode, parent->inode_num);
+ uint32_t curr_offset = 0;
+ uint32_t block_offset = 0;
+ uint32_t in_block_offset = 0;
+ uint32_t entry_name_len = strlen(entry_name);
+ char *check = kcalloc(entry_name_len + 1, 1);
+ char *block_buf = read_inode_block(ext2fs, p_inode, block_offset);
+ while (curr_offset < p_inode->size) {
+ if (in_block_offset >= ext2fs->block_size) {
+ block_offset++;
+ in_block_offset = 0;
+ block_buf = read_inode_block(ext2fs, p_inode, block_offset);
+ }
+ direntry_t *curr_dir = (direntry_t *)(block_buf + in_block_offset);
+ if (curr_dir->name_len == entry_name_len) {
+ memcpy(check, curr_dir->name, entry_name_len);
+ if (curr_dir->inode != 0 && !strcmp(entry_name, check)) {
+ curr_dir->inode = 0;
+ write_inode_block(ext2fs, p_inode, block_offset, block_buf);
+ return;
+ }
+ }
+ uint32_t expected_size =
+ ((sizeof(direntry_t) + curr_dir->name_len) & 0xfffffffc) + 0x4;
+ uint32_t real_size = curr_dir->size;
+ if (real_size != expected_size)
+ return;
+ in_block_offset += curr_dir->size;
+ curr_offset += curr_dir->size;
+ }
+}
+
+void ext2_chmod(vfs_node_t *file, uint32_t mode)
+{
+ ext2_fs_t *ext2fs = file->device;
+ inode_t *inode = kmalloc(sizeof(inode_t));
+ read_inode_metadata(ext2fs, inode, file->inode_num);
+ inode->permission = (inode->permission & 0xFFFFF000) | mode;
+ write_inode_metadata(ext2fs, inode, file->inode_num);
+}
+
+uint32_t ext2_read(vfs_node_t *file, uint32_t offset, uint32_t size, char *buf)
+{
+ ext2_fs_t *ext2fs = file->device;
+ inode_t *inode = kmalloc(sizeof(inode_t));
+ read_inode_metadata(ext2fs, inode, file->inode_num);
+ read_inode_filedata(ext2fs, inode, offset, size, buf);
+ return size;
+}
+
+uint32_t ext2_write(vfs_node_t *file, uint32_t offset, uint32_t size, char *buf)
+{
+ ext2_fs_t *ext2fs = file->device;
+ inode_t *inode = kmalloc(sizeof(inode_t));
+ read_inode_metadata(ext2fs, inode, file->inode_num);
+ write_inode_filedata(ext2fs, inode, file->inode_num, offset, size, buf);
+ return size;
+}
+
+void ext2_open(vfs_node_t *file, uint32_t flags)
+{
+ ext2_fs_t *ext2fs = file->device;
+ if (flags & O_TRUNC) {
+ inode_t *inode = kmalloc(sizeof(inode_t));
+ read_inode_metadata(ext2fs, inode, file->inode_num);
+ inode->size = 0;
+ write_inode_metadata(ext2fs, inode, file->inode_num);
+ }
+}
+
+void ext2_close()
+{
+ return;
+}
+
+void read_inode_metadata(ext2_fs_t *ext2fs, inode_t *inode, uint32_t inode_idx)
+{
+ uint32_t group = inode_idx / ext2fs->inodes_per_group;
+ uint32_t inode_table_block = ext2fs->bgds[group].inode_table;
+ uint32_t idx_in_group = inode_idx - group * ext2fs->inodes_per_group;
+ uint32_t block_offset = (idx_in_group - 1) * ext2fs->sb->inode_size / ext2fs->block_size;
+ uint32_t offset_in_block =
+ (idx_in_group - 1) - block_offset * (ext2fs->block_size / ext2fs->sb->inode_size);
+ char *block_buf = kmalloc(ext2fs->block_size);
+ read_disk_block(ext2fs, inode_table_block + block_offset, block_buf);
+ memcpy(inode, block_buf + offset_in_block * ext2fs->sb->inode_size, ext2fs->sb->inode_size);
+ kfree(block_buf);
+}
+
+void write_inode_metadata(ext2_fs_t *ext2fs, inode_t *inode, uint32_t inode_idx)
+{
+ uint32_t group = inode_idx / ext2fs->inodes_per_group;
+ uint32_t inode_table_block = ext2fs->bgds[group].inode_table;
+ uint32_t block_offset = (inode_idx - 1) * ext2fs->sb->inode_size / ext2fs->block_size;
+ uint32_t offset_in_block =
+ (inode_idx - 1) - block_offset * (ext2fs->block_size / ext2fs->sb->inode_size);
+ char *block_buf = kmalloc(ext2fs->block_size);
+ read_disk_block(ext2fs, inode_table_block + block_offset, block_buf);
+ memcpy(block_buf + offset_in_block * ext2fs->sb->inode_size, inode, ext2fs->sb->inode_size);
+ write_disk_block(ext2fs, inode_table_block + block_offset, block_buf);
+ kfree(block_buf);
+}
+
+uint32_t read_inode_filedata(ext2_fs_t *ext2fs, inode_t *inode, uint32_t offset, uint32_t size,
+ char *buf)
+{
+ uint32_t end_offset = (inode->size >= offset + size) ? (offset + size) : (inode->size);
+ uint32_t start_block = offset / ext2fs->block_size;
+ uint32_t end_block = end_offset / ext2fs->block_size;
+ uint32_t start_off = offset % ext2fs->block_size;
+ uint32_t end_size = end_offset - end_block * ext2fs->block_size;
+
+ uint32_t i = start_block;
+ uint32_t curr_off = 0;
+ while (i <= end_block) {
+ uint32_t left = 0, right = ext2fs->block_size - 1;
+ char *block_buf = read_inode_block(ext2fs, inode, i);
+ if (i == start_block)
+ left = start_off;
+ if (i == end_block)
+ right = end_size - 1;
+ memcpy(buf + curr_off, block_buf + left, (right - left + 1));
+ curr_off = curr_off + (right - left + 1);
+ kfree(block_buf);
+ i++;
+ }
+ return end_offset - offset;
+}
+
+void write_inode_filedata(ext2_fs_t *ext2fs, inode_t *inode, uint32_t inode_idx, uint32_t offset,
+ uint32_t size, char *buf)
+{
+ if (offset + size > inode->size) {
+ inode->size = offset + size;
+ write_inode_metadata(ext2fs, inode, inode_idx);
+ }
+ uint32_t end_offset = (inode->size >= offset + size) ? (offset + size) : (inode->size);
+ uint32_t start_block = offset / ext2fs->block_size;
+ uint32_t end_block = end_offset / ext2fs->block_size;
+ uint32_t start_off = offset % ext2fs->block_size;
+ uint32_t end_size = end_offset - end_block * ext2fs->block_size;
+
+ uint32_t i = start_block;
+ uint32_t curr_off = 0;
+ while (i <= end_block) {
+ uint32_t left = 0, right = ext2fs->block_size;
+ char *block_buf = read_inode_block(ext2fs, inode, i);
+
+ if (i == start_block)
+ left = start_off;
+ if (i == end_block)
+ right = end_size - 1;
+ memcpy(block_buf + left, buf + curr_off, (right - left + 1));
+ curr_off = curr_off + (right - left + 1);
+ write_inode_block(ext2fs, inode, i, block_buf);
+ kfree(block_buf);
+ i++;
+ }
+}
+
+char *read_inode_block(ext2_fs_t *ext2fs, inode_t *inode, uint32_t iblock)
+{
+ char *buf = kmalloc(ext2fs->block_size);
+ uint32_t disk_block = get_disk_block_number(ext2fs, inode, iblock);
+ read_disk_block(ext2fs, disk_block, buf);
+ return buf;
+}
+
+void write_inode_block(ext2_fs_t *ext2fs, inode_t *inode, uint32_t iblock, char *buf)
+{
+ uint32_t disk_block = get_disk_block_number(ext2fs, inode, iblock);
+ write_disk_block(ext2fs, disk_block, buf);
+}
+
+void read_disk_block(ext2_fs_t *ext2fs, uint32_t block, char *buf)
+{
+ vfs_read(ext2fs->disk_device, ext2fs->block_size * block, ext2fs->block_size, buf);
+}
+
+void write_disk_block(ext2_fs_t *ext2fs, uint32_t block, char *buf)
+{
+ vfs_write(ext2fs->disk_device, ext2fs->block_size * block, ext2fs->block_size, buf);
+}
+
+void rewrite_bgds(ext2_fs_t *ext2fs)
+{
+ for (uint32_t i = 0; i < ext2fs->bgd_blocks; i++)
+ write_disk_block(ext2fs, 2, (void *)ext2fs->bgds + i * ext2fs->block_size);
+}
+
+void rewrite_superblock(ext2_fs_t *ext2fs)
+{
+ write_disk_block(ext2fs, 1, (void *)ext2fs->sb);
+}
+
+int alloc_inode_metadata_block(uint32_t *block_ptr, ext2_fs_t *ext2fs, inode_t *inode,
+ uint32_t inode_idx, char *buffer, unsigned int block_overwrite)
+{
+ if (!(*block_ptr)) {
+ unsigned int block_no = ext2_alloc_block(ext2fs);
+ if (!block_no)
+ return 0;
+ *block_ptr = block_no;
+ if (buffer)
+ write_disk_block(ext2fs, block_overwrite, (void *)buffer);
+ else
+ write_inode_metadata(ext2fs, inode, inode_idx);
+ return 1;
+ }
+ return 0;
+}
+
+uint32_t get_disk_block_number(ext2_fs_t *ext2fs, inode_t *inode, uint32_t inode_block)
+{
+ unsigned int p = ext2fs->block_size / 4;
+ int a, b, c, d, e, f, g;
+ uint32_t *tmp = kmalloc(ext2fs->block_size);
+ uint32_t ret = -1;
+ a = inode_block - EXT2_DIRECT_BLOCKS;
+ if (a < 0) {
+ ret = inode->blocks[inode_block];
+ goto done;
+ }
+ b = a - p;
+ if (b < 0) {
+ read_disk_block(ext2fs, inode->blocks[EXT2_DIRECT_BLOCKS], (void *)tmp);
+ ret = tmp[a];
+ goto done;
+ }
+ c = b - p * p;
+ if (c < 0) {
+ c = b / p;
+ d = b - c * p;
+ read_disk_block(ext2fs, inode->blocks[EXT2_DIRECT_BLOCKS + 1], (void *)tmp);
+ read_disk_block(ext2fs, tmp[c], (void *)tmp);
+ ret = tmp[d];
+ goto done;
+ }
+ d = c - p * p * p;
+ if (d < 0) {
+ e = c / (p * p);
+ f = (c - e * p * p) / p;
+ g = (c - e * p * p - f * p);
+ read_disk_block(ext2fs, inode->blocks[EXT2_DIRECT_BLOCKS + 2], (void *)tmp);
+ read_disk_block(ext2fs, tmp[e], (void *)tmp);
+ read_disk_block(ext2fs, tmp[f], (void *)tmp);
+ ret = tmp[g];
+ goto done;
+ }
+done:
+ kfree(tmp);
+ return ret;
+}
+
+void set_disk_block_number(ext2_fs_t *ext2fs, inode_t *inode, uint32_t inode_idx,
+ uint32_t inode_block, uint32_t disk_block)
+{
+ unsigned int p = ext2fs->block_size / 4;
+ int a, b, c, d, e, f, g;
+ int iblock = inode_block;
+ uint32_t *tmp = kmalloc(ext2fs->block_size);
+
+ a = iblock - EXT2_DIRECT_BLOCKS;
+ if (a <= 0) {
+ inode->blocks[inode_block] = disk_block;
+ goto done;
+ }
+ b = a - p;
+ if (b <= 0) {
+ alloc_inode_metadata_block(&(inode->blocks[EXT2_DIRECT_BLOCKS]), ext2fs, inode,
+ inode_idx, NULL, 0);
+ read_disk_block(ext2fs, inode->blocks[EXT2_DIRECT_BLOCKS], (void *)tmp);
+ ((unsigned int *)tmp)[a] = disk_block;
+ write_disk_block(ext2fs, inode->blocks[EXT2_DIRECT_BLOCKS], (void *)tmp);
+ tmp[a] = disk_block;
+ goto done;
+ }
+ c = b - p * p;
+ if (c <= 0) {
+ c = b / p;
+ d = b - c * p;
+ alloc_inode_metadata_block(&(inode->blocks[EXT2_DIRECT_BLOCKS + 1]), ext2fs, inode,
+ inode_idx, NULL, 0);
+ read_disk_block(ext2fs, inode->blocks[EXT2_DIRECT_BLOCKS + 1], (void *)tmp);
+ alloc_inode_metadata_block(&(tmp[c]), ext2fs, inode, inode_idx, (void *)tmp,
+ inode->blocks[EXT2_DIRECT_BLOCKS + 1]);
+ unsigned int temp = tmp[c];
+ read_disk_block(ext2fs, temp, (void *)tmp);
+ tmp[d] = disk_block;
+ write_disk_block(ext2fs, temp, (void *)tmp);
+ goto done;
+ }
+ d = c - p * p * p;
+ if (d <= 0) {
+ e = c / (p * p);
+ f = (c - e * p * p) / p;
+ g = (c - e * p * p - f * p);
+ alloc_inode_metadata_block(&(inode->blocks[EXT2_DIRECT_BLOCKS + 2]), ext2fs, inode,
+ inode_idx, NULL, 0);
+ read_disk_block(ext2fs, inode->blocks[EXT2_DIRECT_BLOCKS + 2], (void *)tmp);
+ alloc_inode_metadata_block(&(tmp[e]), ext2fs, inode, inode_idx, (void *)tmp,
+ inode->blocks[EXT2_DIRECT_BLOCKS + 2]);
+ unsigned int temp = tmp[e];
+ read_disk_block(ext2fs, tmp[e], (void *)tmp);
+ alloc_inode_metadata_block(&(tmp[f]), ext2fs, inode, inode_idx, (void *)tmp, temp);
+ temp = tmp[f];
+ read_disk_block(ext2fs, tmp[f], (void *)tmp);
+ tmp[g] = disk_block;
+ write_disk_block(ext2fs, temp, (void *)tmp);
+ goto done;
+ }
+done:
+ kfree(tmp);
+}
+
+uint32_t ext2_alloc_block(ext2_fs_t *ext2fs)
+{
+ uint32_t *buf = kcalloc(ext2fs->block_size, 1);
+ for (uint32_t i = 0; i < ext2fs->total_groups; i++) {
+ if (!ext2fs->bgds[i].free_blocks)
+ continue;
+
+ uint32_t bitmap_block = ext2fs->bgds[i].block_bitmap;
+ read_disk_block(ext2fs, bitmap_block, (void *)buf);
+ for (uint32_t j = 0; j < ext2fs->block_size / 4; j++) {
+ uint32_t sub_bitmap = buf[j];
+ if (sub_bitmap == 0xFFFFFFFF)
+ continue;
+ for (uint32_t k = 0; k < 32; k++) {
+ uint32_t free = !((sub_bitmap >> k) & 0x1);
+ if (free) {
+ uint32_t mask = (0x1 << k);
+ buf[j] = buf[j] | mask;
+ write_disk_block(ext2fs, bitmap_block, (void *)buf);
+ ext2fs->bgds[i].free_blocks--;
+ rewrite_bgds(ext2fs);
+ return i * ext2fs->blocks_per_group + j * 32 + k;
+ }
+ }
+ }
+ }
+ panic("We're out of blocks!\n");
+ return (uint32_t)-1;
+}
+
+void ext2_free_block(ext2_fs_t *ext2fs, uint32_t block)
+{
+ uint32_t *buf = kcalloc(ext2fs->block_size, 1);
+ uint32_t group_idx = block / ext2fs->blocks_per_group;
+ uint32_t sub_bitmap_idx = (block - (ext2fs->blocks_per_group * group_idx)) / 4;
+ uint32_t idx = (block - (ext2fs->blocks_per_group * group_idx)) % 4;
+
+ uint32_t bitmap_block = ext2fs->bgds[group_idx].block_bitmap;
+ read_disk_block(ext2fs, bitmap_block, (void *)buf);
+
+ uint32_t mask = ~(0x1 << idx);
+ buf[sub_bitmap_idx] = buf[sub_bitmap_idx] & mask;
+
+ write_disk_block(ext2fs, bitmap_block, (void *)buf);
+
+ ext2fs->bgds[group_idx].free_blocks++;
+ rewrite_bgds(ext2fs);
+}
+
+void alloc_inode_block(ext2_fs_t *ext2fs, inode_t *inode, uint32_t inode_idx, uint32_t block)
+{
+ uint32_t ret = ext2_alloc_block(ext2fs);
+ set_disk_block_number(ext2fs, inode, inode_idx, block, ret);
+ inode->num_sectors = (block + 1) * (ext2fs->block_size / 512);
+ write_inode_metadata(ext2fs, inode, inode_idx);
+}
+
+void free_inode_block(ext2_fs_t *ext2fs, inode_t *inode, uint32_t inode_idx, uint32_t block)
+{
+ uint32_t ret = get_disk_block_number(ext2fs, inode, block);
+ ext2_free_block(ext2fs, ret);
+ set_disk_block_number(ext2fs, inode, inode_idx, ret, 0);
+ write_inode_metadata(ext2fs, inode, inode_idx);
+}
+
+uint32_t alloc_inode(ext2_fs_t *ext2fs)
+{
+ uint32_t *buf = kcalloc(ext2fs->block_size, 1);
+ for (uint32_t i = 0; i < ext2fs->total_groups; i++) {
+ if (!ext2fs->bgds[i].free_inodes)
+ continue;
+
+ uint32_t bitmap_block = ext2fs->bgds[i].inode_bitmap;
+ read_disk_block(ext2fs, bitmap_block, (void *)buf);
+ for (uint32_t j = 0; j < ext2fs->block_size / 4; j++) {
+ uint32_t sub_bitmap = buf[j];
+ if (sub_bitmap == 0xFFFFFFFF)
+ continue;
+ for (uint32_t k = 0; k < 32; k++) {
+ uint32_t free = !((sub_bitmap >> k) & 0x1);
+ if (free) {
+ uint32_t mask = (0x1 << k);
+ buf[j] = buf[j] | mask;
+ write_disk_block(ext2fs, bitmap_block, (void *)buf);
+ ext2fs->bgds[i].free_inodes--;
+ rewrite_bgds(ext2fs);
+ return i * ext2fs->inodes_per_group + j * 32 + k;
+ }
+ }
+ }
+ }
+ panic("We're out of inodes!\n");
+ return (uint32_t)-1;
+}
+
+void free_inode(ext2_fs_t *ext2fs, uint32_t inode)
+{
+ uint32_t *buf = kcalloc(ext2fs->block_size, 1);
+ uint32_t group_idx = inode / ext2fs->inodes_per_group;
+ uint32_t sub_bitmap_idx = (inode - (ext2fs->inodes_per_group * group_idx)) / 4;
+ uint32_t idx = (inode - (ext2fs->inodes_per_group * group_idx)) % 4;
+
+ uint32_t bitmap_block = ext2fs->bgds[group_idx].inode_bitmap;
+ read_disk_block(ext2fs, bitmap_block, (void *)buf);
+
+ uint32_t mask = ~(0x1 << idx);
+ buf[sub_bitmap_idx] = buf[sub_bitmap_idx] & mask;
+
+ write_disk_block(ext2fs, bitmap_block, (void *)buf);
+
+ ext2fs->bgds[group_idx].free_inodes++;
+ rewrite_bgds(ext2fs);
+}
+
+vfs_node_t *get_ext2_root(ext2_fs_t *ext2fs, inode_t *inode)
+{
+ vfs_node_t *ext2root = kcalloc(sizeof(vfs_node_t), 1);
+ strcpy(ext2root->name, "/");
+ ext2root->device = ext2fs;
+ ext2root->mask = inode->permission;
+ ext2root->inode_num = ROOT_INODE_NUMBER;
+
+ ext2root->access_time = inode->atime;
+ ext2root->modified_time = inode->mtime;
+ ext2root->create_time = inode->ctime;
+
+ ext2root->flags |= FS_DIRECTORY;
+ ext2root->read = NULL;
+ ext2root->write = NULL;
+ ext2root->chmod = ext2_chmod;
+ ext2root->open = ext2_open;
+ ext2root->close = ext2_close;
+ ext2root->read = ext2_read;
+ ext2root->write = ext2_write;
+ ext2root->mkdir = ext2_mkdir;
+ ext2root->create = ext2_mkfile;
+ ext2root->listdir = ext2_listdir;
+ ext2root->finddir = ext2_finddir;
+ ext2root->unlink = ext2_unlink;
+ return ext2root;
+}
+
+void ext2_init(char *device_path, char *mountpoint)
+{
+ ext2_fs_t *ext2fs = kcalloc(sizeof(ext2_fs_t), 1);
+ ext2fs->disk_device = file_open(device_path, 0);
+ ext2fs->sb = kmalloc(SUPERBLOCK_SIZE);
+ ext2fs->block_size = 1024;
+ read_disk_block(ext2fs, 1, (void *)ext2fs->sb);
+ ext2fs->block_size = (1024 << ext2fs->sb->log2block_size);
+ ext2fs->blocks_per_group = ext2fs->sb->blocks_per_group;
+ ext2fs->inodes_per_group = ext2fs->sb->inodes_per_group;
+
+ ext2fs->total_groups = ext2fs->sb->total_blocks / ext2fs->blocks_per_group; // REMEMBER: Zero Division Exception
+ log("9");
+ if (ext2fs->blocks_per_group * ext2fs->total_groups < ext2fs->total_groups)
+ ext2fs->total_groups++;
+ log("10");
+
+ ext2fs->bgd_blocks = (ext2fs->total_groups * sizeof(bgd_t)) / ext2fs->block_size;
+ log("11");
+ if (ext2fs->bgd_blocks * ext2fs->block_size < ext2fs->total_groups * sizeof(bgd_t))
+ ext2fs->bgd_blocks++;
+ log("12");
+
+ ext2fs->bgds = kcalloc(sizeof(bgd_t), ext2fs->bgd_blocks * ext2fs->block_size);
+ log("13");
+ for (uint32_t i = 0; i < ext2fs->bgd_blocks; i++) {
+ read_disk_block(ext2fs, 2, (void *)ext2fs->bgds + i * ext2fs->block_size);
+ }
+ log("14");
+
+ inode_t *root_inode = kcalloc(sizeof(inode_t), 1);
+ log("15");
+ read_inode_metadata(ext2fs, root_inode, ROOT_INODE_NUMBER);
+ log("16");
+ vfs_mount(mountpoint, get_ext2_root(ext2fs, root_inode));
+ log("17");
+}