#include #include #include #include #include #include #include #include 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); log("%x", ext2fs->sb->ext2_magic); 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 if (ext2fs->blocks_per_group * ext2fs->total_groups < ext2fs->total_groups) ext2fs->total_groups++; ext2fs->bgd_blocks = (ext2fs->total_groups * sizeof(bgd_t)) / ext2fs->block_size; if (ext2fs->bgd_blocks * ext2fs->block_size < ext2fs->total_groups * sizeof(bgd_t)) ext2fs->bgd_blocks++; ext2fs->bgds = kcalloc(sizeof(bgd_t), ext2fs->bgd_blocks * ext2fs->block_size); for (uint32_t i = 0; i < ext2fs->bgd_blocks; i++) { read_disk_block(ext2fs, 2, (void *)ext2fs->bgds + i * ext2fs->block_size); } inode_t *root_inode = kcalloc(sizeof(inode_t), 1); read_inode_metadata(ext2fs, root_inode, ROOT_INODE_NUMBER); vfs_mount(mountpoint, get_ext2_root(ext2fs, root_inode)); }