#include #include #include #include #include #include #include #include #include #include #define EXT2_SUPERBLOCK_SECTOR 2 #define EXT2_ROOT_INODE 2 #define BLOCKS_REQUIRED(_a, _b) ((_a) / (_b) + (((_a) % (_b)) != 0)) superblock_t *superblock; u32 block_byte_size; u32 inode_size; u32 inodes_per_block; vfs_fd_t *mount_fd = NULL; void ext2_close(vfs_fd_t *fd) { return; // There is nothing to clear } int read_inode(int inode_num, u8 *data, u64 size, u64 offset, u64 *file_size); void get_inode_data_size(int inode_num, u64 *file_size) { read_inode(inode_num, NULL, 0, 0, file_size); } struct block_cache { int is_used; u32 last_use; u32 block_num; u8 *block; u8 has_write; }; size_t num_block_cache = 4; struct block_cache *cache; u32 cold_cache_hits = 0; void cached_read_block(u32 block, void *address, size_t size, size_t offset) { assert(offset + size <= block_byte_size); int free_found = -1; for (size_t i = 0; i < num_block_cache; i++) { if (!cache[i].is_used) { free_found = i; continue; } if (cache[i].block_num == block) { cache[i].last_use = timer_get_uptime(); memcpy(address, cache[i].block + offset, size); return; } } if (-1 == free_found) { u32 min_last_used = U32_MAX; int min_index = 0; for (size_t i = 0; i < num_block_cache; i++) { if (cache[i].last_use < min_last_used) { min_last_used = cache[i].last_use; min_index = i; } } free_found = min_index; } struct block_cache *c = &cache[free_found]; if (c->is_used && c->has_write) { raw_vfs_pwrite(mount_fd, c->block, block_byte_size, c->block_num * block_byte_size); } c->is_used = 1; c->block_num = block; c->last_use = timer_get_uptime(); c->has_write = 0; raw_vfs_pread(mount_fd, c->block, block_byte_size, block * block_byte_size); cached_read_block(block, address, size, offset); } void ext2_read_block(u32 block, void *address, size_t size, size_t offset) { cached_read_block(block, address, size, offset); } void ext2_flush_writes(void) { for (size_t i = 0; i < num_block_cache; i++) { if (!cache[i].is_used) { continue; } if (!cache[i].has_write) { continue; } raw_vfs_pwrite(mount_fd, cache[i].block, block_byte_size, cache[i].block_num * block_byte_size); cache[i].has_write = 0; } } void ext2_write_block(u32 block, u8 *address, size_t size, size_t offset) { assert(offset + size <= block_byte_size); int cache_index = -1; for (size_t i = 0; i < num_block_cache; i++) { if (!cache[i].is_used) { continue; } if (cache[i].block_num == block) { cache_index = i; break; } } if (-1 != cache_index) { memcpy(cache[cache_index].block + offset, address, size); cache[cache_index].has_write = 1; return; } raw_vfs_pwrite(mount_fd, address, size, block * block_byte_size + offset); } void write_group_descriptor(u32 group_index, bgdt_t *block_group) { int starting_block = (1024 == block_byte_size) ? 2 : 1; ext2_write_block(starting_block, (u8 *)block_group, sizeof(bgdt_t), group_index * sizeof(bgdt_t)); } void get_group_descriptor(u32 group_index, bgdt_t *block_group) { int starting_block = (1024 == block_byte_size) ? 2 : 1; ext2_read_block(starting_block, block_group, sizeof(bgdt_t), group_index * sizeof(bgdt_t)); } u32 num_block_groups(void) { // Determining the Number of Block Groups // From the Superblock, extract the size of each block, the total // number of inodes, the total number of blocks, the number of blocks // per block group, and the number of inodes in each block group. From // this information we can infer the number of block groups there are // by: // Rounding up the total number of blocks divided by the number of // blocks per block group u32 num_blocks = superblock->num_blocks; u32 num_blocks_in_group = superblock->num_blocks_in_group; u32 b = num_blocks / num_blocks_in_group; if (num_blocks % num_blocks_in_group != 0) { b++; } // Rounding up the total number of inodes divided by the number of // inodes per block group u32 num_inodes = superblock->num_inodes; u32 num_inodes_in_group = superblock->num_inodes_in_group; u32 i = num_inodes / num_inodes_in_group; if (num_inodes % num_inodes_in_group != 0) { i++; } // Both (and check them against each other) assert(i == b); return i; } void ext2_block_containing_inode(u32 inode_index, u32 *block_index, u32 *offset) { assert(0 != inode_index); bgdt_t block_group; get_group_descriptor((inode_index - 1) / superblock->num_inodes_in_group, &block_group); u64 full_offset = ((inode_index - 1) % superblock->num_inodes_in_group) * inode_size; *block_index = block_group.starting_inode_table + (full_offset >> (superblock->block_size + 10)); *offset = full_offset & (block_byte_size - 1); } int ext2_last_inode_read = -1; inode_t ext2_last_inode; void ext2_get_inode_header(int inode_index, u8 *data) { memset(data + sizeof(inode_t), 0, inode_size - sizeof(inode_t)); // Very simple cache. If the inode_index is a inode already read then // just copy the old data. if (ext2_last_inode_read == inode_index) { memcpy(data, &ext2_last_inode, sizeof(inode_t)); return; } u32 block_index; u32 block_offset; ext2_block_containing_inode(inode_index, &block_index, &block_offset); u8 mem_block[inode_size]; ext2_read_block(block_index, mem_block, inode_size, block_offset); memcpy(data, mem_block, inode_size); memcpy(&ext2_last_inode, mem_block, sizeof(inode_t)); ext2_last_inode_read = inode_index; } void ext2_write_inode(int inode_index, inode_t *data) { if (ext2_last_inode_read == inode_index) { ext2_last_inode_read = -1; // Invalidate the cache } u32 block_index; u32 block_offset; ext2_block_containing_inode(inode_index, &block_index, &block_offset); u8 mem_block[inode_size]; memcpy(mem_block, data, inode_size); ext2_write_block(block_index, mem_block, inode_size, block_offset); } int ext2_get_inode_in_directory(int dir_inode, char *file, direntry_header_t *entry) { if ('\0' == *file) { return dir_inode; } u64 file_size; ASSERT_BUT_FIXME_PROPOGATE(-1 != read_inode(dir_inode, NULL, 0, 0, &file_size)); u64 allocation_size = file_size; u8 *data = kmalloc(allocation_size); ASSERT_BUT_FIXME_PROPOGATE( -1 != read_inode(dir_inode, data, allocation_size, 0, NULL)); direntry_header_t *dir; u8 *data_p = data; u8 *data_end = data + allocation_size; int file_len = strlen(file); for (; data_p <= (data_end - sizeof(direntry_header_t)) && (dir = (direntry_header_t *)data_p)->inode; data_p += dir->size) { if (0 == dir->size) { break; } if (0 == dir->name_length) { continue; } if (file_len < dir->name_length) { continue; } assert(data_p + sizeof(direntry_header_t) + dir->name_length <= data_end); if (0 == memcmp(data_p + sizeof(direntry_header_t), file, dir->name_length)) { if (strlen(file) > dir->name_length) { continue; } if (entry) { memcpy(entry, data_p, sizeof(direntry_header_t)); } int r = dir->inode; kfree(data); return r; } } kfree(data); return 0; } int ext2_read_dir(int dir_inode, u8 *buffer, size_t len, size_t offset) { u64 file_size; get_inode_data_size(dir_inode, &file_size); u8 *data = kmalloc(file_size); read_inode(dir_inode, data, file_size, 0, NULL); direntry_header_t *dir; struct dirent tmp_entry; size_t n_dir = 0; int rc = 0; u8 *data_p = data; u8 *data_end = data + file_size; for (; data_p <= (data_end - sizeof(direntry_header_t)) && (dir = (direntry_header_t *)data_p)->inode && len > 0; data_p += dir->size, n_dir++) { if (0 == dir->size) { break; } if (0 == dir->name_length) { continue; } if (n_dir < (offset / sizeof(struct dirent))) { continue; } memcpy(tmp_entry.d_name, data_p + sizeof(direntry_header_t), dir->name_length); tmp_entry.d_name[dir->name_length] = '\0'; u8 *p = (u8 *)&tmp_entry; size_t l = sizeof(struct dirent); l = min(len, l); memcpy(buffer + rc, p, l); len -= l; rc += l; } kfree(data); return rc; } u32 ext2_find_inode(const char *file) { int cur_path_inode = EXT2_ROOT_INODE; if (*file == '/' && *(file + 1) == '\0') { return cur_path_inode; } char *str = copy_and_allocate_string(file); char *orig_str = str; char *start; for (;;) { int final = 0; start = str + 1; str++; for (; '/' != *str && '\0' != *str; str++) ; if ('\0' == *str) { final = 1; } *str = '\0'; direntry_header_t a; if (0 == (cur_path_inode = ext2_get_inode_in_directory(cur_path_inode, start, &a))) { kfree(orig_str); return 0; } if (final) { break; } // The expected returned entry is a directory if (TYPE_INDICATOR_DIRECTORY != a.type_indicator) { kfree(orig_str); klog(LOG_WARN, "ext2: Expected diretory but got: %d", a.type_indicator); return 0; } } kfree(orig_str); return cur_path_inode; } u32 get_singly_block_index(u32 singly_block_ptr, u32 i) { u8 block[block_byte_size]; ext2_read_block(singly_block_ptr, block, block_byte_size, 0); u32 index = *(u32 *)(block + (i * (32 / 8))); return index; } int get_block(inode_t *inode, u32 i) { if (i < 12) { return inode->block_pointers[i]; } i -= 12; u32 singly_block_byte_size = block_byte_size / (32 / 8); u32 double_block_byte_size = (singly_block_byte_size * singly_block_byte_size); if (i < singly_block_byte_size) { return get_singly_block_index(inode->single_indirect_block_pointer, i); } else if (i < double_block_byte_size) { i -= singly_block_byte_size; u32 singly_entry = get_singly_block_index( inode->double_indirect_block_pointer, i / singly_block_byte_size); u32 offset_in_entry = i % singly_block_byte_size; int block = get_singly_block_index(singly_entry, offset_in_entry); return block; } assert(0); return 0; } int get_free_blocks(int allocate, int entries[], u32 num_entries) { u32 current_entry = 0; bgdt_t block_group; if (num_entries > superblock->num_blocks_unallocated) { return 0; } assert(0 == superblock->num_blocks_in_group % 8); for (u32 group = 0; group < num_block_groups() && current_entry < num_entries; group++) { get_group_descriptor(group, &block_group); if (0 == block_group.num_unallocated_blocks_in_group) { continue; } u8 bitmap[(superblock->num_blocks_in_group) / 8]; ext2_read_block(block_group.block_usage_bitmap, bitmap, (superblock->num_blocks_in_group) / 8, 0); int found_block = 0; for (u32 index = 0; index < superblock->num_blocks_in_group / 8 && current_entry < num_entries; index++) { if (0xFF == bitmap[index]) { continue; } for (u32 offset = 0; offset < 8 && current_entry < num_entries; offset++) { if (bitmap[index] & (1 << offset)) { continue; } u32 block_index = index * 8 + offset + group * superblock->num_blocks_in_group; bitmap[index] |= (1 << offset); entries[current_entry] = block_index; current_entry++; found_block = 1; } } if (allocate && found_block) { ext2_write_block(block_group.block_usage_bitmap, bitmap, superblock->num_blocks_in_group / 8, 0); block_group.num_unallocated_blocks_in_group--; write_group_descriptor(group, &block_group); superblock->num_blocks_unallocated--; raw_vfs_pwrite(mount_fd, superblock, 2 * SECTOR_SIZE, 0); } } return current_entry; } int get_free_block(int allocate) { int entry[1]; if (0 == get_free_blocks(allocate, entry, 1)) { return -1; } return entry[0]; } int get_free_inode(int allocate) { bgdt_t block_group; if (0 == superblock->num_inodes_unallocated) { return -1; } assert(0 == superblock->num_inodes_in_group % 8); u8 bitmap[(superblock->num_inodes_in_group) / 8]; for (u32 group = 0; group < num_block_groups(); group++) { get_group_descriptor(group, &block_group); if (0 == block_group.num_unallocated_inodes_in_group) { continue; } ext2_read_block(block_group.inode_usage_bitmap, bitmap, (superblock->num_inodes_in_group) / 8, 0); for (u32 index = 0; index < superblock->num_inodes_in_group / 8; index++) { if (0xFF == bitmap[index]) { continue; } for (u32 offset = 0; offset < 8; offset++) { if (bitmap[index] & (1 << offset)) { continue; } u32 inode_index = index * 8 + offset + group * superblock->num_inodes_in_group + 1 /* inodes are 1 addressed */; if (allocate) { bitmap[index] |= (1 << offset); ext2_write_block(block_group.inode_usage_bitmap, bitmap, superblock->num_inodes_in_group / 8, 0); block_group.num_unallocated_inodes_in_group--; write_group_descriptor(group, &block_group); superblock->num_inodes_unallocated--; raw_vfs_pwrite(mount_fd, superblock, 2 * SECTOR_SIZE, 0); } return inode_index; } } } return -1; } #define INDIRECT_BLOCK_CAPACITY (block_byte_size / sizeof(u32)) void write_to_indirect_block(u32 indirect_block, u32 index, u32 new_block) { index %= INDIRECT_BLOCK_CAPACITY; ext2_write_block(indirect_block, (u8 *)&new_block, sizeof(u32), index * sizeof(u32)); } int ext2_allocate_block(inode_t *inode, u32 index, int block) { if (index < 12) { inode->block_pointers[index] = block; return 1; } index -= 12; if (index < INDIRECT_BLOCK_CAPACITY) { if (0 == index) { int n = get_free_block(1 /*true*/); if (-1 == n) { return 0; } inode->single_indirect_block_pointer = n; } write_to_indirect_block(inode->single_indirect_block_pointer, index, block); return 1; } index -= INDIRECT_BLOCK_CAPACITY; if (index < INDIRECT_BLOCK_CAPACITY * INDIRECT_BLOCK_CAPACITY) { if (0 == index) { int n = get_free_block(1 /*true*/); if (-1 == n) { return 0; } inode->double_indirect_block_pointer = n; } u32 value; if (0 == (index % INDIRECT_BLOCK_CAPACITY)) { int n = get_free_block(1 /*true*/); if (-1 == n) { return 0; } write_to_indirect_block(inode->double_indirect_block_pointer, index / INDIRECT_BLOCK_CAPACITY, n); value = n; } else { value = get_singly_block_index(inode->double_indirect_block_pointer, index / INDIRECT_BLOCK_CAPACITY); } write_to_indirect_block(value, index, block); return 1; } return 0; } int write_inode(int inode_num, u8 *data, u64 size, u64 offset, u64 *file_size, int append) { (void)file_size; u8 inode_buffer[inode_size]; ext2_get_inode_header(inode_num, inode_buffer); inode_t *inode = (inode_t *)inode_buffer; u64 fsize = (u64)(((u64)inode->_upper_32size << 32) | (u64)inode->low_32size); if (append) { offset = fsize; } u32 block_start = offset / block_byte_size; u32 block_offset = offset % block_byte_size; int num_blocks_used = inode->num_disk_sectors / (block_byte_size / SECTOR_SIZE); if (size + offset > fsize) { fsize = size + offset; } u32 num_blocks_required = BLOCKS_REQUIRED(fsize, block_byte_size); u32 delta = num_blocks_required - num_blocks_used; if (delta > 0) { int blocks[delta]; get_free_blocks(1, blocks, delta); for (u32 i = num_blocks_used; i < num_blocks_required; i++) { assert(ext2_allocate_block(inode, i, blocks[i - num_blocks_used])); } } inode->num_disk_sectors = num_blocks_required * (block_byte_size / SECTOR_SIZE); int bytes_written = 0; for (int i = block_start; size; i++) { u32 block = get_block(inode, i); if (0 == block) { break; } int write_len = ((size + block_offset) > block_byte_size) ? (block_byte_size - block_offset) : size; ext2_write_block(block, data + bytes_written, write_len, block_offset); block_offset = 0; bytes_written += write_len; size -= write_len; } inode->low_32size = fsize; inode->_upper_32size = (fsize >> 32); ext2_write_inode(inode_num, inode); ext2_flush_writes(); return bytes_written; } int read_inode(int inode_num, u8 *data, u64 size, u64 offset, u64 *file_size) { // TODO: Fail if size is lower than the size of the file being read, and // return the size of the file the callers is trying to read. u8 inode_buffer[inode_size]; ext2_get_inode_header(inode_num, inode_buffer); inode_t *inode = (inode_t *)inode_buffer; u64 fsize = (u64)(((u64)inode->_upper_32size << 32) | (u64)inode->low_32size); if (file_size) { *file_size = fsize; } if (size > fsize - offset) { size -= ((size + offset) - fsize); } if (size == 0) { return 0; } if (offset > fsize) { return 0; } u32 block_start = offset / block_byte_size; u32 block_offset = offset % block_byte_size; int bytes_read = 0; for (int i = block_start; size; i++) { int read_len = ((size + block_offset) > block_byte_size) ? (block_byte_size - block_offset) : size; u32 block = get_block(inode, i); if (0 == block) { memset(data + bytes_read, 0, read_len); } else { ext2_read_block(block, data + bytes_read, read_len, block_offset); } block_offset = 0; bytes_read += read_len; size -= read_len; } return bytes_read; } size_t ext2_read_file_offset(const char *file, u8 *data, u64 size, u64 offset, u64 *file_size) { // TODO: Fail if the file does not exist. u32 inode = ext2_find_inode(file); return read_inode(inode, data, size, offset, file_size); } size_t ext2_read_file(const char *file, u8 *data, size_t size, u64 *file_size) { return ext2_read_file_offset(file, data, size, 0, file_size); } int resolve_link(int inode_num) { u8 tmp_inode_buffer[inode_size]; inode_t *inode = (inode_t *)tmp_inode_buffer; u64 inode_size = (((u64)inode->_upper_32size) << 32) & inode->low_32size; assert(inode_size <= 60); ext2_get_inode_header(inode_num, tmp_inode_buffer); char *path = (char *)(tmp_inode_buffer + (10 * 4)); path--; *path = '/'; return ext2_find_inode(path); } int ext2_write(u8 *buffer, u64 offset, u64 len, vfs_fd_t *fd) { int inode_num = fd->inode->inode_num; assert(fd->inode->type != FS_TYPE_DIRECTORY); if (fd->inode->type == FS_TYPE_LINK) { inode_num = resolve_link(inode_num); } return write_inode(inode_num, buffer, len, offset, NULL, 0); } int ext2_read(u8 *buffer, u64 offset, u64 len, vfs_fd_t *fd) { int inode_num = fd->inode->inode_num; if (fd->inode->type == FS_TYPE_LINK) { inode_num = resolve_link(inode_num); } u8 inode_buffer[inode_size]; ext2_get_inode_header(inode_num, inode_buffer); inode_t *inode = (inode_t *)inode_buffer; if (DIRECTORY & inode->types_permissions) { return ext2_read_dir(inode_num, buffer, len, offset); } return read_inode(inode_num, buffer, len, offset, NULL); } int ext2_stat(vfs_fd_t *fd, struct stat *buf) { u8 buffer[inode_size]; ext2_get_inode_header(fd->inode->inode_num, buffer); inode_t *inode = (inode_t *)buffer; buf->st_size = (u64)inode->low_32size | ((u64)inode->_upper_32size); if (DIRECTORY & inode->types_permissions) { buf->st_mode = STAT_DIR; } else { buf->st_mode = STAT_REG; } return 0; } int ext2_truncate(vfs_fd_t *fd, size_t length) { // TODO: Blocks that are no longer used should be freed. u8 inode_buffer[inode_size]; ext2_get_inode_header(fd->inode->inode_num, inode_buffer); inode_t *ext2_inode = (inode_t *)inode_buffer; // FIXME: ftruncate should support 64 bit lengths ext2_inode->_upper_32size = 0; ext2_inode->low_32size = length; ext2_write_inode(fd->inode->inode_num, ext2_inode); return 0; } vfs_inode_t *ext2_open(const char *path) { u32 inode_num = ext2_find_inode(path); if (0 == inode_num) { return NULL; } u8 buffer[inode_size]; ext2_get_inode_header(inode_num, buffer); inode_t *ext2_inode = (inode_t *)buffer; u64 file_size = ((u64)(ext2_inode->_upper_32size) << 32) | ext2_inode->low_32size; u8 type; switch ((ext2_inode->types_permissions / 0x1000)) { case 0xA: type = FS_TYPE_LINK; break; case 0x4: type = FS_TYPE_DIRECTORY; break; default: type = FS_TYPE_FILE; break; } return vfs_create_inode( inode_num, type, NULL, NULL, 1 /*is_open*/, 0, NULL /*internal_object*/, file_size, ext2_open, ext2_create_file, ext2_read, ext2_write, ext2_close, ext2_create_directory, NULL /*get_vm_object*/, ext2_truncate /*truncate*/, ext2_stat, NULL /*connect*/); } u64 end_of_last_entry_position(int dir_inode, u64 *entry_offset, direntry_header_t *meta) { u64 file_size; get_inode_data_size(dir_inode, &file_size); u8 *data = kmalloc(file_size); read_inode(dir_inode, data, file_size, 0, NULL); direntry_header_t *dir; u8 *data_p = data; u64 pos = 0; u64 prev = pos; for (; pos < file_size && (dir = (direntry_header_t *)data_p)->size; data_p += dir->size, prev = pos, pos += dir->size) ; if (entry_offset) { *entry_offset = prev; } if (meta) { memcpy(meta, ((u8 *)data) + prev, sizeof(direntry_header_t)); } kfree(data); return pos; } void ext2_create_entry(int directory_inode, direntry_header_t entry_header, const char *name) { u64 entry_offset = 0; direntry_header_t meta; end_of_last_entry_position(directory_inode, &entry_offset, &meta); u32 padding_in_use = block_byte_size - entry_offset; // assert(padding_in_use == meta.size); assert(padding_in_use >= (sizeof(direntry_header_t) + entry_header.name_length)); // Modify the entry to have its real size meta.size = sizeof(direntry_header_t) + meta.name_length; meta.size += (4 - (meta.size % 4)); write_inode(directory_inode, (u8 *)&meta, sizeof(direntry_header_t), entry_offset, NULL, 0); // Create new entry u32 new_entry_offset = entry_offset + meta.size; entry_header.size = (sizeof(direntry_header_t) + entry_header.name_length); entry_header.size += (4 - (entry_header.size % 4)); u32 length_till_next_block = block_byte_size - (new_entry_offset % block_byte_size); if (0 == length_till_next_block) { length_till_next_block = block_byte_size; } assert(entry_header.size < length_till_next_block); entry_header.size = length_till_next_block; u8 buffer[entry_header.size]; memset(buffer, 0, entry_header.size); memcpy(buffer, &entry_header, sizeof(entry_header)); memcpy(buffer + sizeof(entry_header), name, entry_header.name_length); write_inode(directory_inode, (u8 *)buffer, entry_header.size, new_entry_offset, NULL, 0); } int ext2_find_parent(char *path, u32 *parent_inode, char **filename) { char *e = path; for (; *e; e++) ; for (; *e != '/'; e--) ; *e = '\0'; *filename = e + 1; if (*path == '\0') { *parent_inode = EXT2_ROOT_INODE; return 1; } else { int r = ext2_find_inode(path); if (0 == r) { return 0; } *parent_inode = r; return 1; } return 0; } int ext2_create_directory(const char *path, int mode) { (void)mode; // Check if the directory already exists u32 inode_num = ext2_find_inode(path); if (0 != inode_num) { klog(LOG_WARN, "ext2_create_directory: Directory already exists"); return inode_num; } u32 parent_inode; // Get the parent directory char path_buffer[strlen(path) + 1]; char *filename; strcpy(path_buffer, path); if (!ext2_find_parent(path_buffer, &parent_inode, &filename)) { klog(LOG_WARN, "ext2_create_file: Parent does not exist"); return -1; } int new_file_inode = get_free_inode(1); if (-1 == new_file_inode) { klog(LOG_WARN, "ext2_create_file: Unable to find free inode"); return -1; } assert(0 != new_file_inode); direntry_header_t entry_header; entry_header.inode = new_file_inode; entry_header.name_length = strlen(filename); entry_header.type_indicator = TYPE_INDICATOR_DIRECTORY; entry_header.size = sizeof(entry_header) + entry_header.name_length; ext2_create_entry(parent_inode, entry_header, filename); // Create the inode header u8 inode_buffer[inode_size]; inode_t *new_inode = (inode_t *)inode_buffer; memset(inode_buffer, 0, inode_size); new_inode->types_permissions = DIRECTORY; new_inode->num_hard_links = 2; // 2 since the directory references // itself with the "." entry ext2_write_inode(new_file_inode, new_inode); // Populate the new directory with "." and ".." { // "." direntry_header_t child_entry_header; child_entry_header.inode = new_file_inode; child_entry_header.name_length = 1; child_entry_header.type_indicator = TYPE_INDICATOR_DIRECTORY; child_entry_header.size = sizeof(entry_header) + entry_header.name_length; ext2_create_entry(new_file_inode, child_entry_header, "."); // ".." child_entry_header.inode = parent_inode; child_entry_header.name_length = 2; child_entry_header.type_indicator = TYPE_INDICATOR_DIRECTORY; child_entry_header.size = sizeof(entry_header) + entry_header.name_length; ext2_create_entry(new_file_inode, child_entry_header, ".."); } return new_file_inode; } int ext2_create_file(const char *path, int mode) { // Check if the file already exists u32 inode_num = ext2_find_inode(path); if (0 != inode_num) { klog(LOG_WARN, "ext2_create_file: File already exists"); return inode_num; } u32 parent_inode; // Get the parent directory char path_buffer[strlen(path) + 1]; char *filename; strcpy(path_buffer, path); if (!ext2_find_parent(path_buffer, &parent_inode, &filename)) { klog(LOG_WARN, "ext2_create_file: Parent does not exist"); return -1; } int new_file_inode = get_free_inode(1); if (-1 == new_file_inode) { klog(LOG_WARN, "ext2_create_file: Unable to find free inode"); return -1; } assert(0 != new_file_inode); direntry_header_t entry_header; entry_header.inode = new_file_inode; entry_header.name_length = strlen(filename); entry_header.type_indicator = TYPE_INDICATOR_REGULAR; entry_header.size = sizeof(entry_header) + entry_header.name_length; ext2_create_entry(parent_inode, entry_header, filename); // Create the inode header u8 inode_buffer[inode_size]; inode_t *new_inode = (inode_t *)inode_buffer; memset(inode_buffer, 0, inode_size); new_inode->types_permissions = 0x8000; new_inode->num_hard_links = 1; ext2_write_inode(new_file_inode, new_inode); return new_file_inode; } vfs_inode_t *ext2_mount(void) { int fd = vfs_open("/dev/sda", O_RDWR, 0); if (0 > fd) { return NULL; } // TODO: Can this be done better? Maybe create a seperate function in // the VFS? mount_fd = get_vfs_fd(fd, NULL); // Remove the FD from the current task // FIXME: This is a hacky solution relist_remove(¤t_task->file_descriptors, fd); parse_superblock(); for (size_t i = 0; i < num_block_cache; i++) { cache[i].block = kmalloc(block_byte_size); if (!cache[i].block) { goto ext2_mount_error; } } vfs_inode_t *inode = vfs_create_inode( 0 /*inode_num*/, 0 /*type*/, 0 /*has_data*/, 0 /*can_write*/, 0 /*is_open*/, 0, NULL /*internal_object*/, 0 /*file_size*/, ext2_open, ext2_create_file, ext2_read, ext2_write, ext2_close, ext2_create_directory, NULL /*get_vm_object*/, ext2_truncate /*truncate*/, ext2_stat, NULL /*connect*/); if (!inode) { goto ext2_mount_error; } return inode; ext2_mount_error: vfs_close(fd); for (size_t i = 0; i < num_block_cache; i++) { kfree(cache[i].block); } return NULL; } void parse_superblock(void) { superblock = kmalloc(2 * SECTOR_SIZE); raw_vfs_pread(mount_fd, superblock, 2 * SECTOR_SIZE, EXT2_SUPERBLOCK_SECTOR * SECTOR_SIZE); block_byte_size = 1024 << superblock->block_size; if (0xEF53 != superblock->ext2_signature) { klog(LOG_ERROR, "Incorrect ext2 signature in superblock."); for (;;) ; // TODO: Fail properly } if (1 <= superblock->major_version) { inode_size = ((ext_superblock_t *)superblock)->inode_size; } inodes_per_block = block_byte_size / inode_size; cache = kcalloc(num_block_cache, sizeof(struct block_cache)); }