summaryrefslogtreecommitdiff
path: root/kernel/fs
diff options
context:
space:
mode:
authorAnton Kling <anton@kling.gg>2023-10-30 22:12:14 +0100
committerAnton Kling <anton@kling.gg>2023-10-31 00:18:38 +0100
commit8a9208612eec8ddae4c418485d848ecfa0613699 (patch)
tree2f4b29200c2f0c19ae52f45bdb9b38a41b356e30 /kernel/fs
parentca76600acc8bf7a02346efa5bd8f17072210ec01 (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.c91
-rw-r--r--kernel/fs/devfs.h26
-rw-r--r--kernel/fs/ext2.c763
-rw-r--r--kernel/fs/ext2.h140
-rw-r--r--kernel/fs/fifo.c97
-rw-r--r--kernel/fs/fifo.h27
-rw-r--r--kernel/fs/shm.c94
-rw-r--r--kernel/fs/shm.h13
-rw-r--r--kernel/fs/tmpfs.c96
-rw-r--r--kernel/fs/tmpfs.h16
-rw-r--r--kernel/fs/vfs.c318
-rw-r--r--kernel/fs/vfs.h100
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