diff options
author | Anton Kling <anton@kling.gg> | 2023-10-30 22:12:14 +0100 |
---|---|---|
committer | Anton Kling <anton@kling.gg> | 2023-10-31 00:18:38 +0100 |
commit | 8a9208612eec8ddae4c418485d848ecfa0613699 (patch) | |
tree | 2f4b29200c2f0c19ae52f45bdb9b38a41b356e30 /kernel/fs | |
parent | ca76600acc8bf7a02346efa5bd8f17072210ec01 (diff) |
Meta: Move kernel and userland to their own folders.
This is to allow both the kernel and the userland to share certain
header files and to make the folder structure a bit more clear.
Diffstat (limited to 'kernel/fs')
-rw-r--r-- | kernel/fs/devfs.c | 91 | ||||
-rw-r--r-- | kernel/fs/devfs.h | 26 | ||||
-rw-r--r-- | kernel/fs/ext2.c | 763 | ||||
-rw-r--r-- | kernel/fs/ext2.h | 140 | ||||
-rw-r--r-- | kernel/fs/fifo.c | 97 | ||||
-rw-r--r-- | kernel/fs/fifo.h | 27 | ||||
-rw-r--r-- | kernel/fs/shm.c | 94 | ||||
-rw-r--r-- | kernel/fs/shm.h | 13 | ||||
-rw-r--r-- | kernel/fs/tmpfs.c | 96 | ||||
-rw-r--r-- | kernel/fs/tmpfs.h | 16 | ||||
-rw-r--r-- | kernel/fs/vfs.c | 318 | ||||
-rw-r--r-- | kernel/fs/vfs.h | 100 |
12 files changed, 1781 insertions, 0 deletions
diff --git a/kernel/fs/devfs.c b/kernel/fs/devfs.c new file mode 100644 index 0000000..14748a7 --- /dev/null +++ b/kernel/fs/devfs.c @@ -0,0 +1,91 @@ +#include <drivers/keyboard.h> +#include <drivers/serial.h> +#include <fs/devfs.h> +#include <fs/vfs.h> +#include <random.h> + +devfs_file files[20]; +int num_files = 0; + +vfs_inode_t *devfs_add_file( + char *path, + int (*read)(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd), + int (*write)(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd), + vfs_vm_object_t *(get_vm_object)(uint64_t length, uint64_t offset, + vfs_fd_t *fd), + uint8_t has_data, uint8_t can_write, int type) { + files[num_files].name = copy_and_allocate_string(path); + + vfs_inode_t *i = kmalloc(sizeof(vfs_inode_t)); + files[num_files].inode = i; + i->type = type; + i->read = read; + i->write = write; + i->close = NULL; + i->get_vm_object = get_vm_object; + i->has_data = has_data; + i->is_open = 1; + i->can_write = can_write; + num_files++; + return i; +} + +vfs_inode_t *devfs_open(const char *file) { + for (int i = 0; i < num_files; i++) + if (isequal_n(files[i].name, file, strlen(files[i].name))) + return files[i].inode; + + return 0; +} + +int devfs_read(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd) { + return fd->inode->read(buffer, offset, len, fd); +} + +int devfs_write(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd) { + return fd->inode->write(buffer, offset, len, fd); +} + +vfs_vm_object_t *devfs_get_vm_object(uint64_t length, uint64_t offset, + vfs_fd_t *fd) { + return fd->inode->get_vm_object(length, offset, fd); +} + +int stdout_write(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd) { + (void)offset; + (void)fd; + + int rc = len; + for (; len--;) + putc(*buffer++); + return rc; +} + +int serial_write(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd) { + (void)offset; + (void)fd; + + int rc = len; + for (; len--;) + write_serial(*buffer++); + return rc; +} + +void add_serial(void) { + devfs_add_file("/serial", NULL, serial_write, NULL, 0, 1, + FS_TYPE_CHAR_DEVICE); +} + +void add_stdout(void) { + devfs_add_file("/stdout", NULL, stdout_write, NULL, 0, 1, + FS_TYPE_CHAR_DEVICE); +} + +vfs_inode_t *devfs_mount(void) { + vfs_inode_t *root = kmalloc_eternal(sizeof(vfs_inode_t)); + root->open = devfs_open; + root->read = devfs_read; + root->write = devfs_write; + root->close = NULL; + return root; +} diff --git a/kernel/fs/devfs.h b/kernel/fs/devfs.h new file mode 100644 index 0000000..23a499e --- /dev/null +++ b/kernel/fs/devfs.h @@ -0,0 +1,26 @@ +#ifndef DEVFS_H +#define DEVFS_H +#include <defs.h> +#include <fs/vfs.h> +#include <stdint.h> + +typedef struct devfs_file { + char *name; + vfs_inode_t *inode; +} devfs_file; + +vfs_inode_t *devfs_mount(void); +vfs_inode_t *devfs_open(const char *file); +int devfs_read(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd); +int devfs_write(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd); +void add_stdout(void); +void add_serial(void); +vfs_inode_t *devfs_add_file( + char *path, + int (*read)(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd), + int (*write)(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd), + vfs_vm_object_t *(*get_vm_object)(uint64_t length, uint64_t offset, + vfs_fd_t *fd), + uint8_t has_data, uint8_t can_write, int type); + +#endif diff --git a/kernel/fs/ext2.c b/kernel/fs/ext2.c new file mode 100644 index 0000000..bd0fb07 --- /dev/null +++ b/kernel/fs/ext2.c @@ -0,0 +1,763 @@ +#include <assert.h> +#include <fs/ext2.h> +#include <fs/vfs.h> +#include <stdint.h> +#include <string.h> + +#define EXT2_SUPERBLOCK_SECTOR 2 +#define EXT2_ROOT_INODE 2 + +#define BLOCKS_REQUIRED(_a, _b) ((_a) / (_b) + (((_a) % (_b)) != 0)) + +superblock_t *superblock; +uint32_t block_byte_size; +uint32_t inode_size; +uint32_t inodes_per_block; + +#define BLOCK_SIZE (block_byte_size) + +void ext2_close(vfs_fd_t *fd) { + return; // There is nothing to clear +} + +int read_inode(int inode_num, unsigned char *data, uint64_t size, + uint64_t offset, uint64_t *file_size); + +struct BLOCK_CACHE { + uint32_t block_num; + uint8_t block[1024]; +}; + +#define NUM_BLOCK_CACHE 30 +struct BLOCK_CACHE cache[NUM_BLOCK_CACHE] = {0}; +uint8_t last_taken_cache = 0; + +void cached_read_block(uint32_t block, void *address, size_t size, + size_t offset) { + int free_found = -1; + for (int i = 0; i < NUM_BLOCK_CACHE; i++) { + if (cache[i].block_num == block) { + memcpy(address, cache[i].block + offset, size); + return; + } + if (0 == cache[i].block_num) + free_found = i; + } + + if (-1 == free_found) { + free_found = last_taken_cache; + last_taken_cache++; + if (last_taken_cache >= NUM_BLOCK_CACHE) + last_taken_cache = 0; + } + + struct BLOCK_CACHE *c = &cache[free_found]; + c->block_num = block; + read_lba(block * block_byte_size / 512, c->block, 1024, 0); + return cached_read_block(block, address, size, offset); +} + +void ext2_read_block(uint32_t block, void *address, size_t size, + size_t offset) { + cached_read_block(block, address, size, offset); +} + +void ext2_write_block(uint32_t block, void *address, size_t size, + size_t offset) { + // Invalidate a old cache + for (int i = 0; i < NUM_BLOCK_CACHE; i++) { + if (cache[i].block_num == block) { + cache[i].block_num = 0; + break; + } + } + write_lba(block * block_byte_size / 512, address, size, offset); +} + +void write_group_descriptor(uint32_t group_index, bgdt_t *block_group) { + int starting_block = (1024 == block_byte_size) ? 2 : 1; + ext2_write_block(starting_block, block_group, sizeof(bgdt_t), + group_index * sizeof(bgdt_t)); +} + +void get_group_descriptor(uint32_t 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)); +} + +uint32_t 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 + uint32_t num_blocks = superblock->num_blocks; + uint32_t num_blocks_in_group = superblock->num_blocks_in_group; + uint32_t 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 + uint32_t num_inodes = superblock->num_inodes; + uint32_t num_inodes_in_group = superblock->num_inodes_in_group; + uint32_t 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(uint32_t inode_index, uint32_t *block_index, + uint32_t *offset) { + assert(0 != inode_index); + bgdt_t block_group; + get_group_descriptor((inode_index - 1) / superblock->num_inodes_in_group, + &block_group); + + uint64_t 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, inode_t *data) { + // 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; + } + uint32_t block_index; + uint32_t block_offset; + ext2_block_containing_inode(inode_index, &block_index, &block_offset); + + uint8_t 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 + uint32_t block_index; + uint32_t block_offset; + ext2_block_containing_inode(inode_index, &block_index, &block_offset); + + uint8_t 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) { + // FIXME: Allocate sufficent size each time + unsigned char *data = kmalloc(block_byte_size * 5); + ASSERT_BUT_FIXME_PROPOGATE( + -1 != read_inode(dir_inode, data, block_byte_size * 5, 0, 0)); + + direntry_header_t *dir; + unsigned char *data_p = data; + for (; (dir = (direntry_header_t *)data_p)->inode; data_p += dir->size) { + if (0 == dir->size) + break; + if (0 == dir->name_length) + continue; + 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)); + return dir->inode; + } + } + return 0; +} + +int ext2_read_dir(int dir_inode, unsigned char *buffer, size_t len, + size_t offset) { + unsigned char data[block_byte_size]; + read_inode(dir_inode, data, block_byte_size, 0, 0); + + direntry_header_t *dir; + struct dirent tmp_entry; + size_t n_dir = 0; + int rc = 0; + unsigned char *data_p = data; + for (; (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'; + uint8_t *p = (uint8_t *)&tmp_entry; + size_t l = sizeof(struct dirent); + + l = (len < l) ? len : l; + memcpy(buffer, p, l); + len -= l; + rc += l; + } + return rc; +} + +uint32_t 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); + kprintf("FAILED\n"); + return 0; + } + } + kfree(orig_str); + return cur_path_inode; +} + +uint32_t get_singly_block_index(uint32_t singly_block_ptr, uint32_t i) { + uint8_t block[block_byte_size]; + ext2_read_block(singly_block_ptr, block, block_byte_size, 0); + uint32_t index = *(uint32_t *)(block + (i * (32 / 8))); + return index; +} + +int get_block(inode_t *inode, uint32_t i) { + if (i < 12) + return inode->block_pointers[i]; + + i -= 12; + uint32_t singly_block_size = block_byte_size / (32 / 8); + uint32_t double_block_size = (singly_block_size * singly_block_size); + if (i < singly_block_size) { + return get_singly_block_index(inode->single_indirect_block_pointer, i); + } else if (i < double_block_size) { + i -= singly_block_size; + uint32_t singly_entry = get_singly_block_index( + inode->double_indirect_block_pointer, i / singly_block_size); + uint32_t offset_in_entry = i % singly_block_size; + int block = get_singly_block_index(singly_entry, offset_in_entry); + return block; + } + assert(0); + return 0; +} + +int get_free_block(int allocate) { + bgdt_t block_group; + uint8_t bitmap[BLOCK_SIZE]; + assert(0 < superblock->num_blocks_unallocated); + for (uint32_t g = 0; g < num_block_groups(); g++) { + get_group_descriptor(g, &block_group); + + if (block_group.num_unallocated_blocks_in_group == 0) { + kprintf("skip\n"); + continue; + } + + ext2_read_block(block_group.block_usage_bitmap, bitmap, BLOCK_SIZE, 0); + for (uint32_t i = 0; i < superblock->num_blocks_in_group; i++) { + if (!(bitmap[i >> 3] & (1 << (i % 8)))) { + if (allocate) { + bitmap[i >> 3] |= (1 << (i % 8)); + ext2_write_block(block_group.block_usage_bitmap, bitmap, BLOCK_SIZE, + 0); + block_group.num_unallocated_blocks_in_group--; + write_group_descriptor(g, &block_group); + superblock->num_blocks_unallocated--; + write_lba(EXT2_SUPERBLOCK_SECTOR, (void *)superblock, 2 * SECTOR_SIZE, + 0); + } + return i + g * superblock->num_blocks_in_group + 1; + } + } + } + return -1; +} + +int get_free_inode(int allocate) { + bgdt_t block_group; + assert(0 < superblock->num_inodes_unallocated); + for (uint32_t g = 0; g < num_block_groups(); g++) { + get_group_descriptor(g, &block_group); + + if (0 == block_group.num_unallocated_inodes_in_group) + continue; + + uint8_t bitmap[BLOCK_SIZE]; + ext2_read_block(block_group.inode_usage_bitmap, bitmap, BLOCK_SIZE, 0); + for (uint32_t i = 0; i < superblock->num_inodes_in_group; i++) { + if (!(bitmap[i / 8] & (1 << (i % 8)))) { + if (allocate) { + bitmap[i / 8] |= (1 << (i % 8)); + ext2_write_block(block_group.inode_usage_bitmap, bitmap, BLOCK_SIZE, + 0); + block_group.num_unallocated_inodes_in_group--; + write_group_descriptor(g, &block_group); + superblock->num_inodes_unallocated--; + write_lba(EXT2_SUPERBLOCK_SECTOR, (void *)superblock, 2 * SECTOR_SIZE, + 0); + } + return i + g * superblock->num_inodes_in_group + 1; + } + } + } + return -1; +} + +int write_inode(int inode_num, unsigned char *data, uint64_t size, + uint64_t offset, uint64_t *file_size, int append) { + (void)file_size; + uint8_t inode_buffer[inode_size]; + ext2_get_inode_header(inode_num, (inode_t *)inode_buffer); + inode_t *inode = (inode_t *)inode_buffer; + + uint64_t fsize = (uint64_t)(((uint64_t)inode->_upper_32size << 32) | + (uint64_t)inode->low_32size); + if (append) + offset = fsize; + + uint32_t block_start = offset / block_byte_size; + uint32_t block_offset = offset % block_byte_size; + + int num_blocks_used = inode->num_disk_sectors / (BLOCK_SIZE / SECTOR_SIZE); + + if (size + offset > fsize) + fsize = size + offset; + + int num_blocks_required = BLOCKS_REQUIRED(fsize, BLOCK_SIZE); + + for (int i = num_blocks_used; i < num_blocks_required; i++) { + if (i > 12) + assert(0); + int b = get_free_block(1 /*true*/); + assert(-1 != b); + inode->block_pointers[i] = b; + } + + inode->num_disk_sectors = num_blocks_required * (BLOCK_SIZE / SECTOR_SIZE); + + int bytes_written = 0; + for (int i = block_start; size; i++) { + uint32_t block = get_block(inode, i); + if (0 == block) { + kprintf("block_not_found\n"); + 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); + return bytes_written; +} + +int read_inode(int inode_num, unsigned char *data, uint64_t size, + uint64_t offset, uint64_t *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. + uint8_t inode_buffer[inode_size]; + ext2_get_inode_header(inode_num, (inode_t *)inode_buffer); + inode_t *inode = (inode_t *)inode_buffer; + + uint64_t fsize = (uint64_t)(((uint64_t)inode->_upper_32size << 32) | + (uint64_t)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; + + uint32_t block_start = offset / block_byte_size; + uint32_t block_offset = offset % block_byte_size; + + int bytes_read = 0; + for (int i = block_start; size; i++) { + uint32_t block = get_block(inode, i); + if (0 == block) { + klog("Filesystem EXT2: Unable to find block", LOG_WARN); + return -1; + } + + int read_len = ((size + block_offset) > block_byte_size) + ? (block_byte_size - block_offset) + : size; + 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, unsigned char *data, + uint64_t size, uint64_t offset, + uint64_t *file_size) { + // TODO: Fail if the file does not exist. + uint32_t inode = ext2_find_inode(file); + return read_inode(inode, data, size, offset, file_size); +} + +size_t ext2_read_file(const char *file, unsigned char *data, size_t size, + uint64_t *file_size) { + return ext2_read_file_offset(file, data, size, 0, file_size); +} + +int resolve_link(int inode_num) { + uint8_t tmp[inode_size]; + inode_t *inode = (inode_t *)tmp; + uint64_t inode_size = + (((uint64_t)inode->_upper_32size) << 32) & inode->low_32size; + assert(inode_size <= 60); + ext2_get_inode_header(inode_num, inode); + char *path = (char *)(tmp + (10 * 4)); + path--; + *path = '/'; + return ext2_find_inode(path); +} + +int ext2_write(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd) { + uint64_t file_size; + int rc; + 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); + } + rc = write_inode(inode_num, buffer, len, offset, &file_size, 0); + return rc; +} + +int ext2_read(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd) { + uint64_t file_size; + int rc; + int inode_num = fd->inode->inode_num; + if (fd->inode->type == FS_TYPE_DIRECTORY) { + rc = ext2_read_dir(inode_num, buffer, len, offset); + return rc; + } + if (fd->inode->type == FS_TYPE_LINK) { + inode_num = resolve_link(inode_num); + } + rc = read_inode(inode_num, buffer, len, offset, &file_size); + return rc; +} + +int ext2_truncate(vfs_fd_t *fd, size_t length) { + // TODO: Blocks that are no longer used should be freed. + char inode_buffer[inode_size]; + inode_t *ext2_inode = (inode_t *)inode_buffer; + + ext2_get_inode_header(fd->inode->inode_num, ext2_inode); + + // 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) { + uint32_t inode_num = ext2_find_inode(path); + if (0 == inode_num) + return NULL; + + inode_t ext2_inode[inode_size]; + ext2_get_inode_header(inode_num, ext2_inode); + uint64_t file_size = + ((uint64_t)(ext2_inode->_upper_32size) << 32) | ext2_inode->low_32size; + + uint8_t 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, 1 /*has_data*/, 1 /*can_write*/, + 1 /*is_open*/, 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*/); +} + +uint64_t end_of_last_entry_position(int dir_inode, uint64_t *entry_offset, + direntry_header_t *meta) { + // FIXME: Allocate sufficent size each time + unsigned char data[block_byte_size * 5]; + uint64_t file_size = 0; + read_inode(dir_inode, data, block_byte_size * 5, 0, &file_size); + assert(block_byte_size * 5 > file_size); + + direntry_header_t *dir; + unsigned char *data_p = data; + uint64_t pos = 0; + uint64_t 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, ((char *)data) + prev, sizeof(direntry_header_t)); + return pos; +} + +void ext2_create_entry(int directory_inode, direntry_header_t entry_header, + const char *name) { + uint64_t entry_offset = 0; + direntry_header_t meta; + end_of_last_entry_position(directory_inode, &entry_offset, &meta); + + uint32_t 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, (unsigned char *)&meta, + sizeof(direntry_header_t), entry_offset, NULL, 0); + + // Create new entry + uint32_t 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)); + + uint32_t length_till_next_block = 1024 - (new_entry_offset % 1024); + if (0 == length_till_next_block) + length_till_next_block = 1024; + assert(entry_header.size < length_till_next_block); + entry_header.size = length_till_next_block; + + uint8_t 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, (unsigned char *)buffer, entry_header.size, + new_entry_offset, NULL, 0); +} + +int ext2_find_parent(char *path, uint32_t *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 + uint32_t inode_num = ext2_find_inode(path); + if (0 != inode_num) { + klog("ext2_create_directory: Directory already exists", LOG_WARN); + return inode_num; + } + + uint32_t 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("ext2_create_file: Parent does not exist", LOG_WARN); + return -1; + } + + int new_file_inode = get_free_inode(1); + if (-1 == new_file_inode) { + klog("ext2_create_file: Unable to find free inode", LOG_WARN); + 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 + uint8_t 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 + uint32_t inode_num = ext2_find_inode(path); + if (0 != inode_num) { + klog("ext2_create_file: File already exists", LOG_WARN); + return inode_num; + } + + uint32_t 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("ext2_create_file: Parent does not exist", LOG_WARN); + return -1; + } + + int new_file_inode = get_free_inode(1); + if (-1 == new_file_inode) { + klog("ext2_create_file: Unable to find free inode", LOG_WARN); + 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 + uint8_t 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) { + parse_superblock(); + return vfs_create_inode(0 /*inode_num*/, 0 /*type*/, 0 /*has_data*/, + 0 /*can_write*/, 0 /*is_open*/, + 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*/); +} + +void parse_superblock(void) { + superblock = ksbrk(2 * SECTOR_SIZE); + read_lba(EXT2_SUPERBLOCK_SECTOR, (void *)superblock, 2 * SECTOR_SIZE, 0); + block_byte_size = 1024 << superblock->block_size; + + if (0xEF53 != superblock->ext2_signature) { + klog("Incorrect ext2 signature in superblock.", LOG_ERROR); + 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; +} diff --git a/kernel/fs/ext2.h b/kernel/fs/ext2.h new file mode 100644 index 0000000..3a2f800 --- /dev/null +++ b/kernel/fs/ext2.h @@ -0,0 +1,140 @@ +#ifndef EXT2_H +#define EXT2_H +#include <drivers/ata.h> +#include <fs/vfs.h> +#include <kmalloc.h> +#include <stdint.h> + +typedef struct Superblock { + uint32_t num_inodes; + uint32_t num_blocks; + uint32_t num_blocks_reserved; + uint32_t num_blocks_unallocated; + uint32_t num_inodes_unallocated; + uint32_t superblock_block_num; + uint32_t block_size; + uint32_t fragment_size; + uint32_t num_blocks_in_group; + uint32_t num_fragments_in_group; + uint32_t num_inodes_in_group; + uint32_t last_mount; + uint32_t last_write; + uint16_t num_mounts_since_fsck; + uint16_t num_mounts_allowed; + uint16_t ext2_signature; // 0xEF53 + uint16_t fs_state; + uint16_t when_error; + uint16_t minor_version; + uint32_t last_fsck; + uint32_t interval_fsck; + uint32_t os_id; + uint32_t major_version; + uint16_t userid_reserved_blocks; + uint16_t groupid_reserved_blocks; +} __attribute__((packed)) superblock_t; + +typedef struct ExtendedSuperblock { + uint32_t num_inodes; + uint32_t num_blocks; + uint32_t num_blocks_reserved; + uint32_t num_blocks_unallocated; + uint32_t num_inodes_unallocated; + uint32_t superblock_block_num; + uint32_t block_size; + uint32_t fragment_size; + uint32_t num_blocks_group; + uint32_t num_fragments_group; + uint32_t num_inodes_group; + uint32_t last_mount; + uint32_t last_write; + uint16_t num_mounts_since_fsck; + uint16_t num_mounts_allowed; + uint16_t ext2_signature; // 0xEF53 + uint16_t fs_state; + uint16_t when_error; + uint16_t minor_version; + uint32_t last_fsck; + uint32_t interval_fsck; + uint32_t os_id; + uint32_t major_version; + uint16_t userid_reserved_blocks; + uint16_t groupid_reserved_blocks; + uint32_t pad; + uint16_t inode_size; +} __attribute__((packed)) ext_superblock_t; + +typedef struct BlockGroupDescriptorTable { + uint32_t block_usage_bitmap; + uint32_t inode_usage_bitmap; + uint32_t starting_inode_table; + uint16_t num_unallocated_blocks_in_group; + uint16_t num_unallocated_inodes_in_group; + uint16_t num_directories_group; +} __attribute__((packed)) bgdt_t; + +typedef struct INode { + uint16_t types_permissions; + uint16_t user_id; + uint32_t low_32size; + uint32_t last_access_time; + uint32_t creation_time; + uint32_t last_modification_time; + uint32_t deletion_time; + uint16_t group_id; + uint16_t num_hard_links; + uint32_t num_disk_sectors; + uint32_t flags; + uint32_t os_specific; + uint32_t block_pointers[12]; + uint32_t single_indirect_block_pointer; + uint32_t double_indirect_block_pointer; + uint32_t triple_indirect_block_pointer; + uint32_t gen_number; + uint32_t _extended_attribute_block; + uint32_t _upper_32size; + uint32_t address_fragment; + uint32_t os_specific2; +} __attribute__((packed)) inode_t; + +// 0 Unknown type +// 1 Regular file +// 2 Directory +// 3 Character device +// 4 Block device +// 5 FIFO +// 6 Socket +// 7 Symbolic link (soft link) +#define TYPE_INDICATOR_UNKOWN 0 +#define TYPE_INDICATOR_REGULAR 1 +#define TYPE_INDICATOR_DIRECTORY 2 +#define TYPE_INDICATOR_CHARACTER_DEVICE 3 +#define TYPE_INDICATOR_BLOCK_DEVICE 4 +#define TYPE_INDICATOR_FIFO 5 +#define TYPE_INDICATOR_SOCKET 6 +#define TYPE_INDICATOR_SOFT_LINK 7 + +#define FIFO 0x1000 +#define CHARACTER_DEVICE 0x2000 +#define DIRECTORY 0x4000 +#define BLOCK_DEVICE 0x6000 +#define REGULAR_FILE 0x8000 +#define SYMBOLIC_LINK 0xA000 +#define UNIX_SOCKET 0xC000 + +typedef struct DirectoryEntryHeader { + uint32_t inode; + uint16_t size; + uint8_t name_length; + uint8_t type_indicator; +} __attribute__((packed)) direntry_header_t; + +int ext2_create_file(const char *path, int mode); +vfs_inode_t *ext2_mount(void); +void parse_superblock(void); +size_t ext2_read_file_offset(const char *file, unsigned char *data, + uint64_t size, uint64_t offset, + uint64_t *file_size); +size_t ext2_read_file(const char *file, unsigned char *data, size_t size, + uint64_t *file_size); +int ext2_create_directory(const char *path, int mode); +#endif diff --git a/kernel/fs/fifo.c b/kernel/fs/fifo.c new file mode 100644 index 0000000..d515ed7 --- /dev/null +++ b/kernel/fs/fifo.c @@ -0,0 +1,97 @@ +#include "fifo.h" +#include <errno.h> + +#define STARTING_SIZE 4096 + +void fifo_close(vfs_fd_t *fd) { + // TODO: Implement + (void)fd; + return; +} + +int fifo_object_write(uint8_t *buffer, uint64_t offset, uint64_t len, + FIFO_FILE *file) { + (void)offset; + file->has_data = 1; + if (file->write_len + len >= file->buffer_len) { + file->can_write = 0; + return -EAGAIN; + } + memcpy(file->buffer + file->write_len, buffer, len); + file->write_len += len; + return len; +} + +int fifo_object_read(uint8_t *buffer, uint64_t offset, uint64_t len, + FIFO_FILE *file) { + (void)offset; + if (file->write_len == 0) { + file->has_data = 0; + return -EAGAIN; + } + + if (len == 0) + return 0; + + file->can_write = 1; + if (len > file->write_len) + len = file->write_len; + + memcpy(buffer, file->buffer, len); + // Shift bufffer to the left + memcpy(file->buffer, file->buffer + len, file->buffer_len - len); + + file->write_len -= len; + if (file->write_len == 0) { + file->has_data = 0; + } + return len; +} + +FIFO_FILE *create_fifo_object(void) { + FIFO_FILE *n = kmalloc(sizeof(FIFO_FILE)); + n->buffer = kmalloc(STARTING_SIZE); + n->buffer_len = STARTING_SIZE; + n->write_len = 0; + return n; +} + +int create_fifo(void) { + + int fd_n = 0; + for (; get_current_task()->file_descriptors[fd_n]; fd_n++) + ; + + vfs_fd_t *fd = kmalloc(sizeof(vfs_fd_t)); + fd->flags = O_RDWR | O_NONBLOCK; + get_current_task()->file_descriptors[fd_n] = fd; + fd->inode = kmalloc(sizeof(vfs_inode_t)); + + fd->inode->internal_object = (void *)create_fifo_object(); + fd->inode->open = NULL; + fd->inode->read = fifo_read; + fd->inode->write = fifo_write; + fd->inode->close = fifo_close; + fd->inode->get_vm_object = NULL; + fd->inode->is_open = 1; + + return fd_n; +} + +int fifo_write(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd) { + (void)offset; + FIFO_FILE *file = (FIFO_FILE *)fd->inode->internal_object; + int rc = fifo_object_write(buffer, offset, len, file); + fd->inode->has_data = file->has_data; + fd->inode->can_write = file->can_write; + return rc; +} + +int fifo_read(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd) { + FIFO_FILE *file = (FIFO_FILE *)fd->inode->internal_object; + file->is_blocking = !(fd->flags & O_NONBLOCK); + int rc = fifo_object_read(buffer, offset, len, file); + fd->inode->has_data = file->has_data; + fd->inode->can_write = file->can_write; + return rc; +} diff --git a/kernel/fs/fifo.h b/kernel/fs/fifo.h new file mode 100644 index 0000000..1ba7168 --- /dev/null +++ b/kernel/fs/fifo.h @@ -0,0 +1,27 @@ +typedef struct S_FIFO_FILE FIFO_FILE; +#ifndef FIFO_H +#define FIFO_H +#include "vfs.h" +#include <stddef.h> +#include <stdint.h> + +struct S_FIFO_FILE { + char *buffer; + uint64_t buffer_len; + uint64_t write_len; + uint8_t is_blocking; + uint8_t has_data; + uint8_t can_write; +}; + +int create_fifo(void); +FIFO_FILE *create_fifo_object(void); +int fifo_object_write(uint8_t *buffer, uint64_t offset, uint64_t len, + FIFO_FILE *file); +int fifo_object_read(uint8_t *buffer, uint64_t offset, uint64_t len, + FIFO_FILE *file); +int fifo_write(uint8_t *buffer, uint64_t offset, uint64_t len, + vfs_fd_t *fd); +int fifo_read(uint8_t *buffer, uint64_t offset, uint64_t len, + vfs_fd_t *fd); +#endif diff --git a/kernel/fs/shm.c b/kernel/fs/shm.c new file mode 100644 index 0000000..4d5f2ab --- /dev/null +++ b/kernel/fs/shm.c @@ -0,0 +1,94 @@ +#include <assert.h> +#include <errno.h> +#include <fs/shm.h> +#include <fs/vfs.h> +#include <hashmap/hashmap.h> +#include <mmu.h> +#include <sched/scheduler.h> +#include <stddef.h> + +HashMap *shared_memory_objects; + +void shm_init(void) { shared_memory_objects = hashmap_create(10); } + +int shm_write(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd) { + vfs_vm_object_t *p = fd->inode->internal_object; + + if (offset > p->size) + return -EFBIG; + + if (offset + len > p->size) + len = p->size - offset; + + memcpy((void *)((uintptr_t)((uintptr_t)p->virtual_object + offset)), buffer, + len); + return len; +} + +int shm_read(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd) { + vfs_vm_object_t *p = fd->inode->internal_object; + + if (offset > p->size) + return -EFBIG; + + if (offset + len > p->size) + len = p->size - offset; + + memcpy((void *)buffer, + (void *)((uintptr_t)((uintptr_t)p->virtual_object + offset)), len); + return len; +} + +vfs_vm_object_t *shm_get_vm_object(uint64_t length, uint64_t offset, + vfs_fd_t *fd) { + (void)length; + (void)offset; + vfs_vm_object_t *p = fd->inode->internal_object; + return p; +} + +int shm_ftruncate(vfs_fd_t *fd, size_t length) { + vfs_vm_object_t *p = fd->inode->internal_object; + p->size = length; + p->virtual_object = ksbrk(length); + int n = (uintptr_t)align_page((void *)(uint32_t)length) / 0x1000; + p->object = kmalloc(sizeof(void *) * n); + for (int i = 0; i < n; i++) + p->object[i] = + (void *)(get_page(p->virtual_object + (i * 0x1000), NULL, 0, 0)->frame * + 0x1000); + return 0; +} + +int shm_open(const char *name, int oflag, mode_t mode) { + // Try to find or create a new shared memory object. + vfs_vm_object_t *internal_object = + hashmap_get_entry(shared_memory_objects, name); + if (!internal_object) { + // if (!(oflag & O_CREAT)) + // return -EMFILE; + internal_object = kmalloc(sizeof(vfs_vm_object_t)); + internal_object->object = NULL; + internal_object->size = 0; + hashmap_add_entry(shared_memory_objects, name, internal_object, NULL, 0); + } + + vfs_inode_t *inode = vfs_create_inode( + 0 /*inode_num*/, 0 /*type*/, 1 /*has_data*/, 1 /*can_write*/, + 1 /*is_open*/, internal_object, 0 /*file_size*/, NULL /*open*/, + NULL /*create_file*/, shm_read, shm_write, NULL /*close*/, + NULL /*create_directory*/, shm_get_vm_object, shm_ftruncate); + + vfs_fd_t *fd_ptr; + int fd = vfs_create_fd(oflag, mode, inode, &fd_ptr); + if (-1 == fd) { + kfree(inode); + return -EMFILE; + } + return fd; +} + +int shm_unlink(const char *name) { + (void)name; + return 0; +} diff --git a/kernel/fs/shm.h b/kernel/fs/shm.h new file mode 100644 index 0000000..fbbdb5c --- /dev/null +++ b/kernel/fs/shm.h @@ -0,0 +1,13 @@ +#ifndef SHM_H +#define SHM_H +#include <stddef.h> +#include <stdint.h> + +typedef int mode_t; + +void shm_init(void); +int shm_open(const char *name, int oflag, mode_t mode); +int shm_unlink(const char *name); +int ftruncate(int fildes, uint64_t length); + +#endif diff --git a/kernel/fs/tmpfs.c b/kernel/fs/tmpfs.c new file mode 100644 index 0000000..a9a3c1f --- /dev/null +++ b/kernel/fs/tmpfs.c @@ -0,0 +1,96 @@ +#include <assert.h> +#include <errno.h> +#include <fs/fifo.h> +#include <fs/tmpfs.h> +#include <halts.h> +#include <sched/scheduler.h> +#include <stdint.h> + +void tmp_close(vfs_fd_t *fd) { + fd->inode->is_open = 0; + ((tmp_inode *)fd->inode->internal_object)->read_inode->is_open = 0; +} + +int tmp_write(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd) { + tmp_inode *calling_file = fd->inode->internal_object; + tmp_inode *child_file = calling_file->read_inode->internal_object; + if (child_file->is_closed) + return -EPIPE; + + int rc = fifo_object_write(buffer, offset, len, child_file->fifo); + calling_file->read_inode->has_data = child_file->fifo->has_data; + fd->inode->can_write = child_file->fifo->can_write; + return rc; +} + +int tmp_read(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd) { + tmp_inode *calling_file = fd->inode->internal_object; + tmp_inode *child_file = calling_file->read_inode->internal_object; + if (calling_file->is_closed) + return -EPIPE; + + int rc = fifo_object_read(buffer, offset, len, calling_file->fifo); + fd->inode->has_data = calling_file->fifo->has_data; + calling_file->read_inode->can_write = child_file->fifo->can_write; + return rc; +} + +void dual_pipe(int fd[2]) { + for (int i = 0; i < 2; i++) { + tmp_inode *pipe = kmalloc(sizeof(tmp_inode)); + pipe->fifo = create_fifo_object(); + + int has_data = 0; + int can_write = 1; + int is_open = 1; + void *internal_object = pipe; + vfs_inode_t *inode = vfs_create_inode( + 0 /*inode_num*/, 0 /*type*/, has_data, can_write, is_open, + internal_object, 0 /*file_size*/, NULL /*open*/, NULL /*create_file*/, + tmp_read, tmp_write, tmp_close, NULL /*create_directory*/, + NULL /*get_vm_object*/, NULL /*truncate*/); + assert(inode); + + vfs_fd_t *fd_ptr; + fd[i] = vfs_create_fd(O_RDWR | O_NONBLOCK, 0, inode, &fd_ptr); + assert(-1 != fd[i]); + } + vfs_inode_t *f_inode = get_current_task()->file_descriptors[fd[0]]->inode; + vfs_inode_t *s_inode = get_current_task()->file_descriptors[fd[1]]->inode; + tmp_inode *f_pipe = f_inode->internal_object; + tmp_inode *s_pipe = s_inode->internal_object; + f_pipe->read_inode = s_inode; + s_pipe->read_inode = f_inode; + f_pipe->is_closed = 0; + s_pipe->is_closed = 0; +} + +void pipe(int fd[2]) { + for (int i = 0; i < 2; i++) { + tmp_inode *pipe = kmalloc(sizeof(tmp_inode)); + pipe->fifo = create_fifo_object(); + + int has_data = 0; + int can_write = 1; + int is_open = 1; + void *internal_object = pipe; + vfs_inode_t *inode = vfs_create_inode( + 0 /*inode_num*/, 0 /*type*/, has_data, can_write, is_open, + internal_object, 0 /*file_size*/, NULL /*open*/, NULL /*create_file*/, + tmp_read, tmp_write, tmp_close, NULL /*create_directory*/, + NULL /*get_vm_object*/, NULL/*truncate*/); + assert(inode); + + vfs_fd_t *fd_ptr; + fd[i] = vfs_create_fd(O_RDWR, 0, inode, &fd_ptr); + assert(-1 != fd[i]); + } + vfs_inode_t *f_inode = get_current_task()->file_descriptors[fd[0]]->inode; + vfs_inode_t *s_inode = get_current_task()->file_descriptors[fd[1]]->inode; + tmp_inode *f_pipe = f_inode->internal_object; + tmp_inode *s_pipe = s_inode->internal_object; + f_pipe->read_inode = s_inode; + s_pipe->read_inode = f_inode; + f_pipe->is_closed = 0; + s_pipe->is_closed = 0; +} diff --git a/kernel/fs/tmpfs.h b/kernel/fs/tmpfs.h new file mode 100644 index 0000000..4052bd5 --- /dev/null +++ b/kernel/fs/tmpfs.h @@ -0,0 +1,16 @@ +#ifndef TMP_H +#define TMP_H +#include <fs/fifo.h> +#include <fs/vfs.h> + +#define TMP_BUFFER_SIZE (1024 * 10) + +typedef struct { + FIFO_FILE *fifo; + uint8_t is_closed; + vfs_inode_t *read_inode; +} tmp_inode; + +void pipe(int fd[2]); +void dual_pipe(int fd[2]); +#endif diff --git a/kernel/fs/vfs.c b/kernel/fs/vfs.c new file mode 100644 index 0000000..0c616a2 --- /dev/null +++ b/kernel/fs/vfs.c @@ -0,0 +1,318 @@ +#include <assert.h> +#include <errno.h> +#include <fs/vfs.h> +#include <mmu.h> +#include <poll.h> + +vfs_inode_t *root_dir; +vfs_mounts_t mounts[10]; +int num_mounts = 0; + +vfs_fd_t *get_vfs_fd(int fd) { + if (fd >= 100) { + klog("get_vfs_fd(): Tried to get out of range fd", LOG_WARN); + dump_backtrace(12); + return NULL; + } + if (fd < 0) { + klog("get_vfs_fd(): Tried to get out of range fd", LOG_WARN); + dump_backtrace(12); + return NULL; + } + return get_current_task()->file_descriptors[fd]; +} + +vfs_inode_t *vfs_create_inode( + int inode_num, int type, uint8_t has_data, uint8_t can_write, + uint8_t is_open, void *internal_object, uint64_t file_size, + vfs_inode_t *(*open)(const char *path), + int (*create_file)(const char *path, int mode), + int (*read)(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd), + int (*write)(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd), + void (*close)(vfs_fd_t *fd), + int (*create_directory)(const char *path, int mode), + vfs_vm_object_t *(*get_vm_object)(uint64_t length, uint64_t offset, + vfs_fd_t *fd), + int (*truncate)(vfs_fd_t *fd, size_t length)) { + vfs_inode_t *r = kmalloc(sizeof(inode_t)); + r->inode_num = inode_num; + r->type = type; + r->has_data = has_data; + r->can_write = can_write; + r->is_open = is_open; + r->internal_object = internal_object; + r->file_size = file_size; + r->open = open; + r->create_file = create_file; + r->read = read; + r->write = write; + r->close = close; + r->create_directory = create_directory; + r->get_vm_object = get_vm_object; + r->truncate = truncate; + return r; +} + +int vfs_create_fd(int flags, int mode, vfs_inode_t *inode, vfs_fd_t **fd) { + process_t *p = (process_t *)get_current_task(); + int i; + for (i = 0; i < 100; i++) + if (!p->file_descriptors[i]) + break; + if (p->file_descriptors[i]) + return -1; + vfs_fd_t *r = kmalloc(sizeof(vfs_fd_t)); + r->flags = flags; + r->mode = mode; + r->inode = inode; + r->reference_count = 1; + p->file_descriptors[i] = r; + if (fd) + *fd = r; + return i; +} + +int vfs_create_file(const char *file) { + vfs_mounts_t *file_mount = 0; + int length = 0; + for (int i = 0; i < num_mounts; i++) { + int path_len = strlen(mounts[i].path); + if (path_len <= length) + continue; + + if (isequal_n(mounts[i].path, file, path_len)) { + length = path_len; + file_mount = &mounts[i]; + } + } + if (1 != length) + file += length; + + if (!file_mount) { + kprintf("vfs_internal_open could not find mounted path for file : %s\n", + file); + return 0; + } + // ext2_create_file("/etc/oscreated", 0); + assert(file_mount->local_root->create_file); + kprintf("Creating a file\n"); + return file_mount->local_root->create_file(file, 0); +} + +vfs_inode_t *vfs_internal_open(const char *file) { + vfs_mounts_t *file_mount = 0; + int length = 0; + for (int i = 0; i < num_mounts; i++) { + int path_len = strlen(mounts[i].path); + if (path_len <= length) + continue; + + if (isequal_n(mounts[i].path, file, path_len)) { + length = path_len; + file_mount = &mounts[i]; + } + } + if (1 != length) + file += length; + + if (!file_mount) { + kprintf("vfs_internal_open could not find mounted path for file : %s\n", + file); + return NULL; + } + + vfs_inode_t *ret = file_mount->local_root->open(file); + return ret; +} + +char *vfs_clean_path(const char *path, char *resolved_path) { + // char *const clean = kmalloc(strlen(path) + 1); + char *clean = resolved_path; + int prev_slash = 0; + char *ptr = clean; + for (; *path; path++) { + if (prev_slash && '/' == *path) { + continue; + } + prev_slash = ('/' == *path); + *ptr = *path; + ptr++; + } + *ptr = '\0'; + return clean; +} + +char *vfs_resolve_path(const char *file, char *resolved_path) { + if ('/' == *file) { + return vfs_clean_path(file, resolved_path); + } + const char *cwd = get_current_task()->current_working_directory; + size_t l = strlen(cwd); + assert(l > 0); + assert('/' == cwd[l - 1]); + // char *r = kmalloc(l + strlen(file) + 1); + char r[256]; + strcpy(r, cwd); + strcat(r, file); + char *final = vfs_clean_path(r, resolved_path); + // kfree(r); + return final; +} + +int vfs_mkdir(const char *path, int mode) { + vfs_mounts_t *file_mount = 0; + int length = 0; + for (int i = 0; i < num_mounts; i++) { + int path_len = strlen(mounts[i].path); + if (path_len <= length) + continue; + + if (isequal_n(mounts[i].path, path, path_len)) { + length = path_len; + file_mount = &mounts[i]; + } + } + if (1 != length) + path += length; + + if (!file_mount) { + kprintf("vfs_internal_open could not find mounted path for file : %s\n", + path); + return 0; + } + assert(file_mount->local_root->create_directory); + // TODO: Error checking, don't just assume it is fine + file_mount->local_root->create_directory(path, mode); + return 0; +} + +int vfs_open(const char *file, int flags, int mode) { + char resolved_path[256] = {0}; + vfs_resolve_path(file, resolved_path); + vfs_inode_t *inode = vfs_internal_open(resolved_path); + if (0 == inode) { + if (mode & O_CREAT) { + if (vfs_create_file(resolved_path)) { + klog("VFS: File created", LOG_NOTE); + return vfs_open(file, flags, mode); + } + klog("VFS: Could not create file", LOG_WARN); + } + return -ENOENT; + } + if (inode->type == FS_TYPE_UNIX_SOCKET) { + return uds_open(resolved_path); + } + + return vfs_create_fd(flags, mode, inode, NULL); +} + +int vfs_close(int fd) { + vfs_fd_t *fd_ptr = get_vfs_fd(fd); + if (NULL == fd_ptr) { + return -1; + } + assert(0 < fd_ptr->reference_count); + // Remove process reference + fd_ptr->reference_count--; + get_current_task()->file_descriptors[fd] = 0; + // If no references left then free the contents + if (0 == fd_ptr->reference_count) { + if (fd_ptr->inode->close) + fd_ptr->inode->close(fd_ptr); + + kfree(fd_ptr); + } + return 0; +} + +int raw_vfs_pread(vfs_fd_t *vfs_fd, void *buf, uint64_t count, + uint64_t offset) { + if (!(vfs_fd->flags & O_READ)) + return -EBADF; + return vfs_fd->inode->read(buf, offset, count, vfs_fd); +} + +int vfs_pread(int fd, void *buf, uint64_t count, uint64_t offset) { + if (fd >= 100) { + kprintf("EBADF : %x\n", fd); + return -EBADF; + } + if (fd < 0) { + dump_backtrace(12); + kprintf("EBADF : %x\n", fd); + return -EBADF; + } + vfs_fd_t *vfs_fd = get_current_task()->file_descriptors[fd]; + if (!vfs_fd) + return -EBADF; + int rc = raw_vfs_pread(vfs_fd, buf, count, offset); + if (-EAGAIN == rc && count > 0) { + if (!(vfs_fd->flags & O_NONBLOCK)) { + struct pollfd fds; + fds.fd = fd; + fds.events = POLLIN; + fds.revents = 0; + poll(&fds, 1, 0); + return vfs_pread(fd, buf, count, offset); + } + } + return rc; +} + +int raw_vfs_pwrite(vfs_fd_t *vfs_fd, void *buf, uint64_t count, + uint64_t offset) { + assert(vfs_fd); + assert(vfs_fd->inode); + assert(vfs_fd->inode->write); + return vfs_fd->inode->write(buf, offset, count, vfs_fd); +} + +int vfs_pwrite(int fd, void *buf, uint64_t count, uint64_t offset) { + vfs_fd_t *vfs_fd = get_vfs_fd(fd); + if (!vfs_fd) + return -EBADF; + if (!(vfs_fd->flags & O_WRITE)) { + return -EBADF; + } + return raw_vfs_pwrite(vfs_fd, buf, count, offset); +} + +vfs_vm_object_t *vfs_get_vm_object(int fd, uint64_t length, uint64_t offset) { + vfs_fd_t *vfs_fd = get_vfs_fd(fd); + if (!vfs_fd) + return NULL; + vfs_vm_object_t *r = vfs_fd->inode->get_vm_object(length, offset, vfs_fd); + return r; +} + +int vfs_dup2(int org_fd, int new_fd) { + get_current_task()->file_descriptors[new_fd] = + get_current_task()->file_descriptors[org_fd]; + get_current_task()->file_descriptors[new_fd]->reference_count++; + return 1; +} + +int vfs_ftruncate(int fd, size_t length) { + vfs_fd_t *fd_ptr = get_vfs_fd(fd); + if (!fd_ptr) + return -EBADF; + if (!(fd_ptr->flags & O_READ)) + return -EINVAL; + vfs_inode_t *inode = fd_ptr->inode; + if (!inode) + return -EINVAL; + if (!inode->truncate) + return -EINVAL; + + return inode->truncate(fd_ptr, length); +} + +void vfs_mount(char *path, vfs_inode_t *local_root) { + int len = strlen(path); + mounts[num_mounts].path = kmalloc_eternal(len + 1); + memcpy(mounts[num_mounts].path, path, len); + mounts[num_mounts].path[len] = '\0'; + mounts[num_mounts].local_root = local_root; + num_mounts++; +} diff --git a/kernel/fs/vfs.h b/kernel/fs/vfs.h new file mode 100644 index 0000000..f8a4b19 --- /dev/null +++ b/kernel/fs/vfs.h @@ -0,0 +1,100 @@ +typedef struct vfs_fd vfs_fd_t; +typedef struct vfs_inode vfs_inode_t; +typedef struct vfs_vm_object vfs_vm_object_t; +typedef struct vfs_mounts vfs_mounts_t; +#ifndef VFS_H +#define VFS_H +#include <limits.h> +#include <sched/scheduler.h> +#include <socket.h> +#include <stddef.h> +#include <stdint.h> + +// FIXME: Is there some standard value for this? +#define O_NONBLOCK (1 << 0) +#define O_READ (1 << 1) +#define O_WRITE (1 << 2) +#define O_CREAT (1 << 3) +#define O_RDONLY O_READ +#define O_WRONLY O_WRITE +#define O_RDWR (O_WRITE | O_READ) + +#define FS_TYPE_FILE 0 +#define FS_TYPE_UNIX_SOCKET 1 +#define FS_TYPE_CHAR_DEVICE 2 +#define FS_TYPE_BLOCK_DEVICE 3 +#define FS_TYPE_DIRECTORY 4 +#define FS_TYPE_LINK 7 + +struct vfs_vm_object { + void *virtual_object; + void **object; + uint64_t size; +}; + +struct vfs_mounts { + char *path; + vfs_inode_t *local_root; +}; + +struct dirent { + unsigned int d_ino; // File serial number. + char d_name[PATH_MAX]; // Filename string of entry. +}; + +struct vfs_fd { + size_t offset; + int flags; + int mode; + int reference_count; // Number of usages of this file descriptor, + // once it reaches zero then the contents can + // be freed. + vfs_inode_t *inode; +}; + +struct vfs_inode { + int inode_num; + int type; + uint8_t has_data; + uint8_t can_write; + uint8_t is_open; + void *internal_object; + uint64_t file_size; + vfs_inode_t *(*open)(const char *path); + int (*create_file)(const char *path, int mode); + int (*read)(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd); + int (*write)(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd); + void (*close)(vfs_fd_t *fd); + int (*create_directory)(const char *path, int mode); + vfs_vm_object_t *(*get_vm_object)(uint64_t length, uint64_t offset, + vfs_fd_t *fd); + int (*truncate)(vfs_fd_t *fd, size_t length); +}; + +int vfs_close(int fd); +vfs_fd_t *get_vfs_fd(int fd); +int vfs_open(const char *file, int flags, int mode); +void vfs_mount(char *path, vfs_inode_t *local_root); +int vfs_pwrite(int fd, void *buf, uint64_t count, uint64_t offset); +int raw_vfs_pwrite(vfs_fd_t *vfs_fd, void *buf, uint64_t count, + uint64_t offset); +int vfs_pread(int fd, void *buf, uint64_t count, uint64_t offset); +vfs_vm_object_t *vfs_get_vm_object(int fd, uint64_t length, uint64_t offset); +int vfs_dup2(int org_fd, int new_fd); +vfs_inode_t *vfs_internal_open(const char *file); +int vfs_mkdir(const char *path, int mode); +int vfs_create_fd(int flags, int mode, vfs_inode_t *inode, vfs_fd_t **fd); +int vfs_ftruncate(int fd, size_t length); +vfs_inode_t *vfs_create_inode( + int inode_num, int type, uint8_t has_data, uint8_t can_write, + uint8_t is_open, void *internal_object, uint64_t file_size, + vfs_inode_t *(*open)(const char *path), + int (*create_file)(const char *path, int mode), + int (*read)(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd), + int (*write)(uint8_t *buffer, uint64_t offset, uint64_t len, vfs_fd_t *fd), + void (*close)(vfs_fd_t *fd), + int (*create_directory)(const char *path, int mode), + vfs_vm_object_t *(*get_vm_object)(uint64_t length, uint64_t offset, + vfs_fd_t *fd), + int (*truncate)(vfs_fd_t *fd, size_t length)); +#endif |