Преглед на файлове

feat: create tmpfs and impl. basic ops

read, write, readdir, mkfile, mkdir, mklink
greatbridf преди 2 години
родител
ревизия
b19b0323e6
променени са 4 файла, в които са добавени 371 реда и са изтрити 0 реда
  1. 2 0
      CMakeLists.txt
  2. 65 0
      include/kernel/vfs.h
  3. 301 0
      src/kernel/vfs.c
  4. 3 0
      src/kernel_main.c

+ 2 - 0
CMakeLists.txt

@@ -46,6 +46,7 @@ set(KERNEL_MAIN_SOURCES src/kernel_main.c
                         src/kernel/tty.c
                         src/kernel/stdio.c
                         src/kernel/mem.c
+                        src/kernel/vfs.c
                         src/kernel/vga.c
                         src/kernel/hw/keyboard.cpp
                         src/kernel/hw/serial.c
@@ -63,6 +64,7 @@ set(KERNEL_MAIN_SOURCES src/kernel_main.c
                         include/kernel/process.h
                         include/kernel/stdio.h
                         include/kernel/mem.h
+                        include/kernel/vfs.h
                         include/kernel/vga.h
                         include/kernel/hw/keyboard.h
                         include/kernel/hw/serial.h

+ 65 - 0
include/kernel/vfs.h

@@ -0,0 +1,65 @@
+#pragma once
+
+#include <types/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct inode;
+struct inode_flags;
+struct inode_ops;
+struct dirent;
+
+typedef size_t (*inode_read)(struct inode* file, char* buf, size_t buf_size, size_t offset, size_t n);
+typedef size_t (*inode_write)(struct inode* file, const char* buf, size_t offset, size_t n);
+typedef int (*inode_readdir)(struct inode* dir, struct dirent* entry, size_t i);
+typedef int (*inode_finddir)(struct inode* dir, struct dirent* entry, const char* filename);
+typedef int (*inode_mkfile)(struct inode* dir, const char* filename);
+typedef int (*inode_rmfile)(struct inode* dir, const char* filename);
+typedef int (*inode_mkdir)(struct inode* dir, const char* dirname);
+
+struct inode_flags {
+    uint32_t file : 1;
+    uint32_t directory : 1;
+    uint32_t mount_point : 1;
+};
+
+struct inode_ops {
+    inode_read read;
+    inode_write write;
+    inode_readdir readdir;
+    inode_finddir finddir;
+    inode_mkfile mkfile;
+    inode_rmfile rmfile;
+    inode_mkdir mkdir;
+};
+
+struct inode {
+    struct inode_flags flags;
+    uint32_t perm;
+    uint32_t impl;
+    uint32_t ino;
+    const struct inode_ops* ops;
+};
+
+struct dirent {
+    char name[128];
+    uint32_t ino;
+};
+
+extern struct inode fs_root;
+
+void init_vfs(void);
+
+size_t vfs_read(struct inode* file, char* buf, size_t buf_size, size_t offset, size_t n);
+size_t vfs_write(struct inode* file, const char* buf, size_t offset, size_t n);
+int vfs_readdir(struct inode* dir, struct dirent* entry, size_t i);
+int vfs_finddir(struct inode* dir, struct dirent* entry, const char* filename);
+int vfs_mkfile(struct inode* dir, const char* filename);
+int vfs_rmfile(struct inode* dir, const char* filename);
+int vfs_mkdir(struct inode* dir, const char* dirname);
+
+#ifdef __cplusplus
+}
+#endif

+ 301 - 0
src/kernel/vfs.c

@@ -0,0 +1,301 @@
+#include <kernel/mem.h>
+#include <kernel/stdio.h>
+#include <kernel/tty.h>
+#include <kernel/vfs.h>
+
+struct tmpfs_file_entry {
+    size_t ino;
+    char filename[128];
+};
+
+struct tmpfs_inode {
+    uint32_t occupied : 1;
+    uint32_t file : 1;
+    uint32_t directory : 1;
+    uint32_t mount_point : 1;
+    size_t data_size;
+    size_t data_capacity;
+    char* data;
+};
+
+struct tmpfs_data {
+    size_t limit;
+    size_t inode_count;
+    size_t inode_capacity;
+    struct tmpfs_inode* inodes;
+};
+
+static inline size_t* dir_item_count(struct tmpfs_inode* dir)
+{
+    return (size_t*)dir->data;
+}
+
+static inline struct tmpfs_file_entry* dir_item_p(struct tmpfs_inode* dir)
+{
+    return (struct tmpfs_file_entry*)(dir->data + sizeof(size_t));
+}
+
+static inline struct tmpfs_inode* from_inode(struct tmpfs_data* data, struct inode* inode)
+{
+    return &data->inodes[inode->ino];
+}
+
+static inline void double_data_size(char** p, size_t* current_capacity)
+{
+    char* tmp = (char*)k_malloc(*current_capacity * 2);
+    memcpy(tmp, *p, *current_capacity);
+    k_free(*p);
+    *p = tmp;
+    *current_capacity *= 2;
+}
+
+static inline void double_inode_list_size(struct tmpfs_inode** p, size_t* current_capacity)
+{
+    struct tmpfs_inode* tmp = (struct tmpfs_inode*)k_malloc(*current_capacity * 2 * sizeof(struct tmpfs_inode));
+    memset(tmp, 0x00, *current_capacity * 2 * sizeof(struct tmpfs_inode));
+    memcpy(tmp, *p, *current_capacity * sizeof(struct tmpfs_inode));
+    k_free(*p);
+    *p = tmp;
+    *current_capacity *= 2;
+}
+
+static inline size_t _tmpfs_read(struct tmpfs_inode* inode, char* buf, size_t buf_size, size_t offset, size_t n)
+{
+    size_t fsize = inode->data_size;
+
+    if (offset + n > fsize)
+        n = fsize - offset;
+
+    if (buf_size < n) {
+        n = buf_size;
+    }
+
+    memcpy(buf, inode->data, n);
+
+    return n;
+}
+
+size_t tmpfs_read(struct inode* file, char* buf, size_t buf_size, size_t offset, size_t n)
+{
+    if (file->flags.file != 1)
+        return 0;
+
+    struct tmpfs_data* data = (struct tmpfs_data*)file->impl;
+    struct tmpfs_inode* inode = &data->inodes[file->ino];
+
+    return _tmpfs_read(inode, buf, buf_size, offset, n);
+}
+
+static inline size_t _tmpfs_write(struct tmpfs_inode* inode, const char* buf, size_t offset, size_t n)
+{
+    size_t fsize = inode->data_size;
+
+    while (offset + n > inode->data_capacity)
+        double_data_size(&inode->data, &inode->data_capacity);
+
+    memcpy(inode->data + offset, buf, n);
+
+    if (offset + n > fsize)
+        inode->data_size = offset + n;
+
+    return n;
+}
+
+size_t tmpfs_write(struct inode* file, const char* buf, size_t offset, size_t n)
+{
+    if (file->flags.file != 1)
+        return 0;
+
+    struct tmpfs_data* data = (struct tmpfs_data*)file->impl;
+    struct tmpfs_inode* inode = &data->inodes[file->ino];
+
+    return _tmpfs_write(inode, buf, offset, n);
+}
+
+// a directory is a special file containing the tmpfs_inode
+// ids and filenames of the files and directories. the first
+// 4 bytes of its data[] is the file count it contains
+int tmpfs_readdir(struct inode* dir, struct dirent* entry, size_t i)
+{
+    if (dir->flags.directory != 1)
+        return GB_FAILED;
+
+    struct tmpfs_data* data = (struct tmpfs_data*)dir->impl;
+    struct tmpfs_inode* inode = &data->inodes[dir->ino];
+
+    size_t n = *dir_item_count(inode);
+    struct tmpfs_file_entry* files = dir_item_p(inode);
+
+    if (i >= n)
+        return GB_FAILED;
+
+    entry->ino = files[i].ino;
+    snprintf(entry->name, sizeof(entry->name), files[i].filename);
+
+    return GB_OK;
+}
+
+static inline size_t _tmpfs_allocinode(struct tmpfs_data* data)
+{
+    // TODO: reclaim released inodes
+    if (data->inode_count == data->inode_capacity) {
+        double_inode_list_size(&data->inodes, &data->inode_capacity);
+    }
+    struct tmpfs_inode* inode = data->inodes + data->inode_count;
+
+    inode->data = (char*)k_malloc(128);
+    inode->data_capacity = 128;
+    inode->data_size = 0;
+    inode->occupied = 1;
+
+    return data->inode_count++;
+}
+
+static inline int _tmpfs_mklink(struct tmpfs_inode* dir, size_t ino, const char* filename)
+{
+    struct tmpfs_file_entry ent = {
+        .filename = 0,
+        .ino = ino,
+    };
+    snprintf(ent.filename, sizeof(ent.filename), filename);
+
+    int result = GB_OK;
+
+    if (_tmpfs_write(dir, (const char*)&ent,
+            sizeof(size_t) + sizeof(struct tmpfs_file_entry) * *dir_item_count(dir),
+            sizeof(struct tmpfs_file_entry))
+        != sizeof(struct tmpfs_file_entry)) {
+        result = GB_FAILED;
+    }
+    *dir_item_count(dir) += 1;
+    return result;
+}
+
+int tmpfs_mkfile(struct inode* dir, const char* filename)
+{
+    struct tmpfs_data* data = (struct tmpfs_data*)dir->impl;
+    size_t ino = _tmpfs_allocinode(data);
+    struct tmpfs_inode* inode = &data->inodes[ino];
+    inode->file = 1;
+
+    _tmpfs_mklink(from_inode(data, dir), ino, filename);
+
+    return GB_OK;
+}
+
+int tmpfs_mkdir(struct inode* dir, const char* dirname)
+{
+    struct tmpfs_data* data = (struct tmpfs_data*)dir->impl;
+
+    size_t ino = _tmpfs_allocinode(data);
+    struct tmpfs_inode* inode = &data->inodes[ino];
+    inode->directory = 1;
+    *dir_item_count(inode) = 0;
+
+    _tmpfs_mklink(from_inode(data, dir), ino, dirname);
+    _tmpfs_mklink(inode, ino, ".");
+    _tmpfs_mklink(inode, dir->ino, "..");
+
+    return GB_OK;
+}
+
+int mkfs_tmpfs(struct tmpfs_data* data, size_t limit)
+{
+    data->limit = limit;
+    data->inodes = (struct tmpfs_inode*)k_malloc(sizeof(struct tmpfs_inode) * 1);
+    memset(data->inodes, 0x00, sizeof(struct tmpfs_inode));
+    data->inode_capacity = 1;
+    data->inode_count = 0;
+
+    size_t root_ino = _tmpfs_allocinode(data);
+    struct tmpfs_inode* root_inode = &data->inodes[root_ino];
+
+    _tmpfs_mklink(root_inode, root_ino, ".");
+    _tmpfs_mklink(root_inode, root_ino, "..");
+
+    return GB_OK;
+}
+
+// typedef int (*inode_finddir)(struct inode* dir, struct dirent* entry, const char* filename);
+// typedef int (*inode_rmfile)(struct inode* dir, const char* filename);
+
+size_t vfs_read(struct inode* file, char* buf, size_t buf_size, size_t offset, size_t n)
+{
+    if (file->ops->read) {
+        return file->ops->read(file, buf, buf_size, offset, n);
+    } else {
+        return 0;
+    }
+}
+size_t vfs_write(struct inode* file, const char* buf, size_t offset, size_t n)
+{
+    if (file->ops->write) {
+        return file->ops->write(file, buf, offset, n);
+    } else {
+        return 0;
+    }
+}
+int vfs_readdir(struct inode* dir, struct dirent* entry, size_t i)
+{
+    if (dir->ops->readdir) {
+        return dir->ops->readdir(dir, entry, i);
+    } else {
+        return 0;
+    }
+}
+int vfs_finddir(struct inode* dir, struct dirent* entry, const char* filename)
+{
+    if (dir->ops->finddir) {
+        return dir->ops->finddir(dir, entry, filename);
+    } else {
+        return 0;
+    }
+}
+int vfs_mkfile(struct inode* dir, const char* filename)
+{
+    if (dir->ops->mkfile) {
+        return dir->ops->mkfile(dir, filename);
+    } else {
+        return 0;
+    }
+}
+int vfs_rmfile(struct inode* dir, const char* filename)
+{
+    if (dir->ops->rmfile) {
+        return dir->ops->rmfile(dir, filename);
+    } else {
+        return 0;
+    }
+}
+int vfs_mkdir(struct inode* dir, const char* dirname)
+{
+    if (dir->ops->mkdir) {
+        return dir->ops->mkdir(dir, dirname);
+    } else {
+        return 0;
+    }
+}
+
+static const struct inode_ops tmpfs_inode_ops = {
+    .read = tmpfs_read,
+    .write = tmpfs_write,
+    .readdir = tmpfs_readdir,
+    .finddir = 0,
+    .mkfile = tmpfs_mkfile,
+    .rmfile = 0,
+    .mkdir = tmpfs_mkdir,
+};
+static struct tmpfs_data rootfs_data;
+struct inode fs_root;
+
+void init_vfs(void)
+{
+    mkfs_tmpfs(&rootfs_data, 4096 * 1024);
+
+    fs_root.flags.directory = 1;
+    fs_root.flags.mount_point = 1;
+    fs_root.ino = 0;
+    fs_root.impl = (uint32_t)&rootfs_data;
+    fs_root.ops = &tmpfs_inode_ops;
+    fs_root.perm = 0777;
+}

+ 3 - 0
src/kernel_main.c

@@ -11,6 +11,7 @@
 #include <kernel/mem.h>
 #include <kernel/stdio.h>
 #include <kernel/tty.h>
+#include <kernel/vfs.h>
 #include <kernel/vga.h>
 #include <types/bitmap.h>
 
@@ -182,6 +183,8 @@ void kernel_main(void)
 
     k_malloc_buf[4096] = '\x89';
 
+    init_vfs();
+
     printkf("No work to do, halting...\n");
 
     while (1) {