From f3d7e220ac552adf594679ee415a3663728a62a6 Mon Sep 17 00:00:00 2001 From: apio Date: Sat, 8 Oct 2022 21:22:46 +0200 Subject: [PATCH 1/7] The beginnings of a VFS implementation!! --- kernel/include/fs/VFS.h | 22 ++++++++++++++++ kernel/src/fs/VFS.cpp | 24 ++++++++++++++++++ kernel/src/init/InitRD.cpp | 51 ++++++++++++++++++++++++++++++++++++++ kernel/src/main.cpp | 16 ++++++++++++ 4 files changed, 113 insertions(+) create mode 100644 kernel/include/fs/VFS.h create mode 100644 kernel/src/fs/VFS.cpp diff --git a/kernel/include/fs/VFS.h b/kernel/include/fs/VFS.h new file mode 100644 index 00000000..2ab19a1c --- /dev/null +++ b/kernel/include/fs/VFS.h @@ -0,0 +1,22 @@ +#pragma once +#include + +namespace VFS +{ + struct Node; + + typedef int32_t (*node_read)(Node*, uint32_t, uint32_t, char*); + typedef Node* (*node_finddir)(Node*, const char*); + + struct Node + { + char name[64]; + uint64_t inode; + node_read read_func; + node_finddir find_func; + }; + + int32_t read(Node* node, uint32_t offset, uint32_t length, char* buffer); + Node* open(const char* filename); + void mount_root(Node* root); +} \ No newline at end of file diff --git a/kernel/src/fs/VFS.cpp b/kernel/src/fs/VFS.cpp new file mode 100644 index 00000000..e196b7ea --- /dev/null +++ b/kernel/src/fs/VFS.cpp @@ -0,0 +1,24 @@ +#include "fs/VFS.h" + +static VFS::Node* vfs_root; + +int32_t VFS::read(Node* node, uint32_t offset, uint32_t length, char* buffer) +{ + if (!node) return -1; + if (!node->read_func) return -1; + + return node->read_func(node, offset, length, buffer); +} + +VFS::Node* VFS::open(const char* filename) +{ + if (!vfs_root) return 0; + if (!vfs_root->find_func) return 0; + + return vfs_root->find_func(vfs_root, filename); +} + +void VFS::mount_root(Node* root) +{ + vfs_root = root; +} \ No newline at end of file diff --git a/kernel/src/init/InitRD.cpp b/kernel/src/init/InitRD.cpp index ee921098..195efd93 100644 --- a/kernel/src/init/InitRD.cpp +++ b/kernel/src/init/InitRD.cpp @@ -2,6 +2,7 @@ #include "init/InitRD.h" #include "bootboot.h" +#include "fs/VFS.h" #include "io/Serial.h" #include "log/Log.h" #include "memory/MemoryManager.h" @@ -14,6 +15,8 @@ extern BOOTBOOT bootboot; static void* initrd_base; static bool initrd_initialized = false; +static VFS::Node initrd_root; + bool InitRD::is_initialized() { return initrd_initialized; @@ -112,10 +115,58 @@ void InitRD::for_each(void (*callback)(File& f)) } } +static InitRD::File files[32]; +static uint32_t total_files = 0; + +static VFS::Node nodes[32]; + +int32_t initrd_read(VFS::Node* node, uint32_t offset, uint32_t length, char* buffer) +{ + if (!node) return -1; + if (node->inode >= total_files) return -1; + InitRD::File& file = files[node->inode]; + if (offset > file.size) return -1; + if (offset + length > file.size) { length = (uint32_t)file.size - offset; } + memcpy(buffer, (void*)((uint64_t)file.addr + offset), length); + return (int32_t)length; +} + +VFS::Node* initrd_scan_root(VFS::Node*, const char* filename) +{ + for (uint32_t i = 0; i < total_files; i++) + { + if (strncmp(nodes[i].name, filename, sizeof(VFS::Node::name)) == 0) { return &nodes[i]; } + } + return 0; +} + +void initrd_scan() +{ + InitRD::for_each([](InitRD::File& f) { + if (total_files >= 32) return; + kdbgln("registering file %s", f.name); + files[total_files++] = f; + }); + for (uint32_t i = 0; i < total_files; i++) + { + VFS::Node& node = nodes[i]; + node.inode = i; + node.read_func = initrd_read; + strncpy(node.name, files[i].name, sizeof(node.name)); + } +} + void InitRD::init() { initrd_base = MemoryManager::get_unaligned_mappings((void*)bootboot.initrd_ptr, bootboot.initrd_size / PAGE_SIZE + 1); kdbgln("physical base at %lx, size %lx, mapped to %p", bootboot.initrd_ptr, bootboot.initrd_size, initrd_base); + kdbgln("total blocks: %ld", get_total_blocks()); + initrd_scan(); + initrd_root.inode = (uint64_t)-1; + strncpy(initrd_root.name, "initrd", 7); + initrd_root.find_func = initrd_scan_root; + VFS::mount_root(&initrd_root); + kdbgln("mounted initrd at VFS root, total files: %d", total_files); initrd_initialized = true; } \ No newline at end of file diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 4b5c71b9..1b55ac69 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -4,6 +4,7 @@ #include "assert.h" #include "config.h" #include "cpu/CPU.h" +#include "fs/VFS.h" #include "gdt/GDT.h" #include "init/Init.h" #include "init/InitRD.h" @@ -159,6 +160,21 @@ extern "C" void _start() kinfoln("Interrupts enabled"); + kinfoln("Trying VFS"); + + VFS::Node* node = VFS::open("sys/moon.sym"); + if (!node) { kerrorln("Unable to find file in VFS"); } + else + { + kinfoln("Found '%s'", node->name); + char buffer[256]; + kinfoln("Reading 255 bytes at offset 128..."); + int nwritten = VFS::read(node, 128, 255, buffer); + kinfoln("Read a total of %d bytes", nwritten); + buffer[255] = 0; + kinfoln("Read: %s", buffer); + } + PCI::scan([](PCI::Device& dev) { kinfoln("Found PCI device %x:%x, %s", dev.id().vendor, dev.id().device, pci_type_name(dev.type())); }); From b38c52f8c7664d44b614c18be355a5d96c00561e Mon Sep 17 00:00:00 2001 From: apio Date: Sat, 8 Oct 2022 21:35:19 +0200 Subject: [PATCH 2/7] more vfs stuff --- kernel/include/fs/VFS.h | 1 + kernel/src/fs/VFS.cpp | 38 ++++++++++++++++++++++++++++++++++---- kernel/src/init/InitRD.cpp | 4 ++++ kernel/src/main.cpp | 12 ++++++------ 4 files changed, 45 insertions(+), 10 deletions(-) diff --git a/kernel/include/fs/VFS.h b/kernel/include/fs/VFS.h index 2ab19a1c..082dbe8f 100644 --- a/kernel/include/fs/VFS.h +++ b/kernel/include/fs/VFS.h @@ -12,6 +12,7 @@ namespace VFS { char name[64]; uint64_t inode; + uint64_t length; node_read read_func; node_finddir find_func; }; diff --git a/kernel/src/fs/VFS.cpp b/kernel/src/fs/VFS.cpp index e196b7ea..047cc645 100644 --- a/kernel/src/fs/VFS.cpp +++ b/kernel/src/fs/VFS.cpp @@ -1,24 +1,54 @@ +#define MODULE "vfs" + #include "fs/VFS.h" +#include "log/Log.h" static VFS::Node* vfs_root; int32_t VFS::read(Node* node, uint32_t offset, uint32_t length, char* buffer) { - if (!node) return -1; - if (!node->read_func) return -1; + if (!node) + { + kwarnln("read() failed: trying to read from nullptr"); + return -1; + } + if (!node->read_func) + { + kwarnln("read() failed: the chosen node doesn't support reading"); + return -1; + } return node->read_func(node, offset, length, buffer); } VFS::Node* VFS::open(const char* filename) { - if (!vfs_root) return 0; - if (!vfs_root->find_func) return 0; + if (!vfs_root) + { + kwarnln("open() failed: root not mounted"); + return 0; + } + if (!vfs_root->find_func) + { + kwarnln("open() failed: root doesn't support finding files"); + return 0; + } return vfs_root->find_func(vfs_root, filename); } void VFS::mount_root(Node* root) { + if (!root) + { + kwarnln("mount_root() failed: attempted to mount nullptr"); + return; + } + if (vfs_root) + { + kwarnln("mount_root() failed: root filesystem already mounted"); + return; + } + kinfoln("mounting node '%s' as vfs root", root->name); vfs_root = root; } \ No newline at end of file diff --git a/kernel/src/init/InitRD.cpp b/kernel/src/init/InitRD.cpp index 195efd93..3406f740 100644 --- a/kernel/src/init/InitRD.cpp +++ b/kernel/src/init/InitRD.cpp @@ -152,6 +152,7 @@ void initrd_scan() VFS::Node& node = nodes[i]; node.inode = i; node.read_func = initrd_read; + node.length = files[i].size; strncpy(node.name, files[i].name, sizeof(node.name)); } } @@ -163,10 +164,13 @@ void InitRD::init() kdbgln("physical base at %lx, size %lx, mapped to %p", bootboot.initrd_ptr, bootboot.initrd_size, initrd_base); kdbgln("total blocks: %ld", get_total_blocks()); initrd_scan(); + initrd_root.length = 0; initrd_root.inode = (uint64_t)-1; strncpy(initrd_root.name, "initrd", 7); initrd_root.find_func = initrd_scan_root; VFS::mount_root(&initrd_root); + VFS::mount_root(0); + VFS::mount_root(&initrd_root); kdbgln("mounted initrd at VFS root, total files: %d", total_files); initrd_initialized = true; } \ No newline at end of file diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 1b55ac69..c26e715b 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -162,17 +162,17 @@ extern "C" void _start() kinfoln("Trying VFS"); - VFS::Node* node = VFS::open("sys/moon.sym"); + VFS::Node* node = VFS::open("sys/config"); if (!node) { kerrorln("Unable to find file in VFS"); } else { kinfoln("Found '%s'", node->name); - char buffer[256]; - kinfoln("Reading 255 bytes at offset 128..."); - int nwritten = VFS::read(node, 128, 255, buffer); - kinfoln("Read a total of %d bytes", nwritten); - buffer[255] = 0; + char* buffer = (char*)kmalloc(node->length + 1); + buffer[node->length] = 0; + int nread = VFS::read(node, 0, (uint32_t)node->length, buffer); + kdbgln("Read %d bytes", nread); kinfoln("Read: %s", buffer); + kfree(buffer); } PCI::scan([](PCI::Device& dev) { From 8158ddc94fd483685bf77308e810d85228e25122 Mon Sep 17 00:00:00 2001 From: apio Date: Sun, 9 Oct 2022 21:19:22 +0200 Subject: [PATCH 3/7] VFS: be more verbose --- kernel/include/fs/VFS.h | 3 +++ kernel/src/fs/VFS.cpp | 11 +++++++++++ 2 files changed, 14 insertions(+) diff --git a/kernel/include/fs/VFS.h b/kernel/include/fs/VFS.h index 082dbe8f..928d579c 100644 --- a/kernel/include/fs/VFS.h +++ b/kernel/include/fs/VFS.h @@ -19,5 +19,8 @@ namespace VFS int32_t read(Node* node, uint32_t offset, uint32_t length, char* buffer); Node* open(const char* filename); + void mount_root(Node* root); + + Node* root(); } \ No newline at end of file diff --git a/kernel/src/fs/VFS.cpp b/kernel/src/fs/VFS.cpp index 047cc645..f64b7cf3 100644 --- a/kernel/src/fs/VFS.cpp +++ b/kernel/src/fs/VFS.cpp @@ -2,6 +2,7 @@ #include "fs/VFS.h" #include "log/Log.h" +#include "std/string.h" static VFS::Node* vfs_root; @@ -18,6 +19,9 @@ int32_t VFS::read(Node* node, uint32_t offset, uint32_t length, char* buffer) return -1; } + kdbgln("read(): node %s (inode %ld), offset %d, %d bytes, into %p", node->name, node->inode, offset, length, + (void*)buffer); + return node->read_func(node, offset, length, buffer); } @@ -34,6 +38,8 @@ VFS::Node* VFS::open(const char* filename) return 0; } + kinfoln("open(): opening %s", filename); + return vfs_root->find_func(vfs_root, filename); } @@ -51,4 +57,9 @@ void VFS::mount_root(Node* root) } kinfoln("mounting node '%s' as vfs root", root->name); vfs_root = root; +} + +VFS::Node* VFS::root() +{ + return vfs_root; } \ No newline at end of file From 2be70d0bc1fdf3216f195c84028e9751a0cdcc7c Mon Sep 17 00:00:00 2001 From: apio Date: Sun, 9 Oct 2022 21:30:38 +0200 Subject: [PATCH 4/7] VFS: Use 64-bit numbers in read() There is no need for any kind of 32-bit compatibility. --- kernel/include/fs/VFS.h | 7 +++++-- kernel/src/fs/VFS.cpp | 4 ++-- kernel/src/init/InitRD.cpp | 10 ++++------ kernel/src/main.cpp | 4 ++-- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/kernel/include/fs/VFS.h b/kernel/include/fs/VFS.h index 928d579c..f7e49dd2 100644 --- a/kernel/include/fs/VFS.h +++ b/kernel/include/fs/VFS.h @@ -1,11 +1,14 @@ #pragma once +#include #include +typedef long ssize_t; + namespace VFS { struct Node; - typedef int32_t (*node_read)(Node*, uint32_t, uint32_t, char*); + typedef ssize_t (*node_read)(Node*, size_t, size_t, char*); typedef Node* (*node_finddir)(Node*, const char*); struct Node @@ -17,7 +20,7 @@ namespace VFS node_finddir find_func; }; - int32_t read(Node* node, uint32_t offset, uint32_t length, char* buffer); + ssize_t read(Node* node, size_t offset, size_t length, char* buffer); Node* open(const char* filename); void mount_root(Node* root); diff --git a/kernel/src/fs/VFS.cpp b/kernel/src/fs/VFS.cpp index f64b7cf3..0a36408d 100644 --- a/kernel/src/fs/VFS.cpp +++ b/kernel/src/fs/VFS.cpp @@ -6,7 +6,7 @@ static VFS::Node* vfs_root; -int32_t VFS::read(Node* node, uint32_t offset, uint32_t length, char* buffer) +ssize_t VFS::read(Node* node, size_t offset, size_t length, char* buffer) { if (!node) { @@ -19,7 +19,7 @@ int32_t VFS::read(Node* node, uint32_t offset, uint32_t length, char* buffer) return -1; } - kdbgln("read(): node %s (inode %ld), offset %d, %d bytes, into %p", node->name, node->inode, offset, length, + kdbgln("read(): node %s (inode %ld), offset %zd, %zd bytes, into %p", node->name, node->inode, offset, length, (void*)buffer); return node->read_func(node, offset, length, buffer); diff --git a/kernel/src/init/InitRD.cpp b/kernel/src/init/InitRD.cpp index 3406f740..c2df7ea6 100644 --- a/kernel/src/init/InitRD.cpp +++ b/kernel/src/init/InitRD.cpp @@ -120,15 +120,15 @@ static uint32_t total_files = 0; static VFS::Node nodes[32]; -int32_t initrd_read(VFS::Node* node, uint32_t offset, uint32_t length, char* buffer) +ssize_t initrd_read(VFS::Node* node, size_t offset, size_t length, char* buffer) { if (!node) return -1; if (node->inode >= total_files) return -1; InitRD::File& file = files[node->inode]; if (offset > file.size) return -1; - if (offset + length > file.size) { length = (uint32_t)file.size - offset; } - memcpy(buffer, (void*)((uint64_t)file.addr + offset), length); - return (int32_t)length; + if (offset + length > file.size) { length = file.size - offset; } + memcpy(buffer, (void*)((uintptr_t)file.addr + offset), length); + return length; } VFS::Node* initrd_scan_root(VFS::Node*, const char* filename) @@ -169,8 +169,6 @@ void InitRD::init() strncpy(initrd_root.name, "initrd", 7); initrd_root.find_func = initrd_scan_root; VFS::mount_root(&initrd_root); - VFS::mount_root(0); - VFS::mount_root(&initrd_root); kdbgln("mounted initrd at VFS root, total files: %d", total_files); initrd_initialized = true; } \ No newline at end of file diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index c26e715b..9dcf2402 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -169,8 +169,8 @@ extern "C" void _start() kinfoln("Found '%s'", node->name); char* buffer = (char*)kmalloc(node->length + 1); buffer[node->length] = 0; - int nread = VFS::read(node, 0, (uint32_t)node->length, buffer); - kdbgln("Read %d bytes", nread); + ssize_t nread = VFS::read(node, 0, node->length, buffer); + kdbgln("Read %zd bytes", nread); kinfoln("Read: %s", buffer); kfree(buffer); } From bbe7c6e658ea6f23586188f6e465ed9e3608ad13 Mon Sep 17 00:00:00 2001 From: apio Date: Mon, 10 Oct 2022 18:44:43 +0200 Subject: [PATCH 5/7] VFS: Implement resolve_path and form the initial ramdisk's VFS properly Finally, resolve_path: a function which takes a path (/etc/fstab for example), and walks the VFS: In this case, it would start with the root FS node, and ask it: "do you have a directory/file named etc?" The node could say 'yes', 'no', or 'i'm not a directory, I'm a file' (should not be the case for the VFS root, but for the other ones it could be) If it says yes, we continue and ask the child if it has a file named fstab. Etc... --- kernel/include/fs/VFS.h | 3 +- kernel/src/fs/VFS.cpp | 64 ++++++++---- kernel/src/init/InitRD.cpp | 202 +++++++++++++++++++++++++++++++++---- kernel/src/main.cpp | 22 ++-- 4 files changed, 244 insertions(+), 47 deletions(-) diff --git a/kernel/include/fs/VFS.h b/kernel/include/fs/VFS.h index f7e49dd2..7d35061f 100644 --- a/kernel/include/fs/VFS.h +++ b/kernel/include/fs/VFS.h @@ -21,9 +21,10 @@ namespace VFS }; ssize_t read(Node* node, size_t offset, size_t length, char* buffer); - Node* open(const char* filename); void mount_root(Node* root); + Node* resolve_path(const char* filename, Node* root = nullptr); + Node* root(); } \ No newline at end of file diff --git a/kernel/src/fs/VFS.cpp b/kernel/src/fs/VFS.cpp index 0a36408d..bbf997ed 100644 --- a/kernel/src/fs/VFS.cpp +++ b/kernel/src/fs/VFS.cpp @@ -2,6 +2,7 @@ #include "fs/VFS.h" #include "log/Log.h" +#include "std/stdlib.h" #include "std/string.h" static VFS::Node* vfs_root; @@ -25,24 +26,6 @@ ssize_t VFS::read(Node* node, size_t offset, size_t length, char* buffer) return node->read_func(node, offset, length, buffer); } -VFS::Node* VFS::open(const char* filename) -{ - if (!vfs_root) - { - kwarnln("open() failed: root not mounted"); - return 0; - } - if (!vfs_root->find_func) - { - kwarnln("open() failed: root doesn't support finding files"); - return 0; - } - - kinfoln("open(): opening %s", filename); - - return vfs_root->find_func(vfs_root, filename); -} - void VFS::mount_root(Node* root) { if (!root) @@ -62,4 +45,49 @@ void VFS::mount_root(Node* root) VFS::Node* VFS::root() { return vfs_root; +} + +VFS::Node* VFS::resolve_path(const char* filename, Node* root) +{ + if (!root) root = vfs_root; + + if (strlen(filename) == 0) return 0; + if (*filename == '/') // Absolute path. + { + filename++; + root = vfs_root; + } + + Node* current_node = root; + + while (true) + { + while (*filename == '/') { filename++; } + if (*filename == 0) { return current_node; } + + size_t path_section_size = 0; + while (filename[path_section_size] && filename[path_section_size] != '/') { path_section_size++; } + + if (strncmp(filename, ".", path_section_size) != 0) // The current path section is not '.' + { + char* buffer = (char*)kmalloc(path_section_size + 1); + memcpy(buffer, filename, path_section_size); + buffer[path_section_size] = 0; + if (!current_node->find_func) + { + kwarnln("Current node has no way to find child nodes"); + return 0; + } + Node* child = current_node->find_func(current_node, buffer); + if (!child) + { + kwarnln("Current node did not find our target node"); + return 0; + } + current_node = child; + kfree(buffer); + } + + filename += path_section_size; + } } \ No newline at end of file diff --git a/kernel/src/init/InitRD.cpp b/kernel/src/init/InitRD.cpp index c2df7ea6..268575f2 100644 --- a/kernel/src/init/InitRD.cpp +++ b/kernel/src/init/InitRD.cpp @@ -115,10 +115,52 @@ void InitRD::for_each(void (*callback)(File& f)) } } +#define INITRD_MAX_FILES_IN_DIR 8 +#define INITRD_MAX_FILES 32 + +namespace InitRD +{ + struct Directory + { + char name[64]; + int entries = 0; + VFS::Node* files[INITRD_MAX_FILES_IN_DIR]; + }; +} + +void initrd_for_each_dir(void (*callback)(InitRD::Directory& f)) +{ + uint64_t block = 0; + uint64_t total_blocks = InitRD::get_total_blocks(); + while (block < total_blocks) + { + InitRD::TarHeader* hdr = (InitRD::TarHeader*)InitRD::get_block(block); + if (!InitRD::is_valid_header(hdr)) + { + block++; + continue; + } + if (hdr->typeflag == 53) + { + InitRD::Directory dir; + strncpy(dir.name, hdr->name, sizeof(dir.name)); + callback(dir); + block++; + continue; + } + auto f = get_file(hdr); + block += get_file_size_in_blocks(f) + 1; + } +} + static InitRD::File files[32]; static uint32_t total_files = 0; -static VFS::Node nodes[32]; +static InitRD::Directory dirs[32]; +static uint32_t total_dirs = 0; + +static VFS::Node nodes[63]; // One of the dirs is the initrd_root +static uint32_t total_nodes = 0; ssize_t initrd_read(VFS::Node* node, size_t offset, size_t length, char* buffer) { @@ -131,30 +173,154 @@ ssize_t initrd_read(VFS::Node* node, size_t offset, size_t length, char* buffer) return length; } -VFS::Node* initrd_scan_root(VFS::Node*, const char* filename) +VFS::Node* initrd_scan_dir(VFS::Node* node, const char* filename) { - for (uint32_t i = 0; i < total_files; i++) + if (!node) return 0; + if (node->inode >= total_dirs) return 0; + InitRD::Directory dir = dirs[node->inode]; + for (int i = 0; i < dir.entries; i++) { - if (strncmp(nodes[i].name, filename, sizeof(VFS::Node::name)) == 0) { return &nodes[i]; } + if (strncmp(dir.files[i]->name, filename, sizeof(VFS::Node::name)) == 0) { return dir.files[i]; } } return 0; } -void initrd_scan() +static bool initrd_register_dir(InitRD::Directory& dir, uint64_t inode) { + const char* filename = dir.name; + VFS::Node* current_node = &initrd_root; + while (true) + { + while (*filename == '/') { filename++; } + if (*filename == 0) { return false; } + + size_t path_section_size = 0; + while (filename[path_section_size] && filename[path_section_size] != '/') { path_section_size++; } + + if (filename[path_section_size]) // We are in a '/' + { + char* buffer = (char*)kmalloc(path_section_size + 1); + memcpy(buffer, filename, path_section_size); + buffer[path_section_size] = 0; + if (!current_node->find_func) { return false; } + VFS::Node* child = current_node->find_func(current_node, buffer); + if (!child) { return false; } + current_node = child; + kfree(buffer); + } + else + { + if (strncmp(filename, ".", path_section_size) != 0) // The current path section is not '.' + { + if (strncmp(filename, "..", path_section_size) == 0) { return false; } + + if (!current_node->find_func) { return false; } + + InitRD::Directory& parent = dirs[current_node->inode]; + if (parent.entries == INITRD_MAX_FILES_IN_DIR) { return false; } + + char* buffer = (char*)kmalloc(path_section_size + 1); + memcpy(buffer, filename, path_section_size); + buffer[path_section_size] = 0; + + VFS::Node& node = nodes[total_nodes++]; + node.inode = inode; + node.find_func = initrd_scan_dir; + node.length = 0; + strncpy(node.name, buffer, sizeof(node.name)); + strncpy(dir.name, buffer, sizeof(dir.name)); + + parent.files[parent.entries++] = &node; + + kfree(buffer); + return true; + } + else { return false; } + } + + filename += path_section_size; + } +} + +static bool initrd_register_file(InitRD::File& f, uint64_t inode) +{ + const char* filename = f.name; + VFS::Node* current_node = &initrd_root; + while (true) + { + while (*filename == '/') { filename++; } + if (*filename == 0) { return false; } + + size_t path_section_size = 0; + while (filename[path_section_size] && filename[path_section_size] != '/') { path_section_size++; } + + if (filename[path_section_size]) // We are in a '/' + { + char* buffer = (char*)kmalloc(path_section_size + 1); + memcpy(buffer, filename, path_section_size); + buffer[path_section_size] = 0; + if (!current_node->find_func) { return false; } + VFS::Node* child = current_node->find_func(current_node, buffer); + if (!child) { return false; } + current_node = child; + kfree(buffer); + } + else + { + if (strncmp(filename, ".", path_section_size) != 0) // The current path section is not '.' + { + if (strncmp(filename, "..", path_section_size) == 0) { return false; } + + if (!current_node->find_func) { return false; } + + InitRD::Directory& parent = dirs[current_node->inode]; + if (parent.entries == INITRD_MAX_FILES_IN_DIR) { return false; } + + char* buffer = (char*)kmalloc(path_section_size + 1); + memcpy(buffer, filename, path_section_size); + buffer[path_section_size] = 0; + + VFS::Node& node = nodes[total_nodes++]; + node.inode = inode; + node.read_func = initrd_read; + node.length = f.size; + strncpy(node.name, buffer, sizeof(node.name)); + strncpy(f.name, buffer, sizeof(f.name)); + + parent.files[parent.entries++] = &node; + kfree(buffer); + return true; + } + else { return false; } + } + + filename += path_section_size; + } +} + +static void initrd_scan() +{ + initrd_for_each_dir([](InitRD::Directory& dir) { + if (total_dirs >= 32) return; + uint64_t inode = total_dirs; + if (initrd_register_dir(dir, inode)) dirs[total_dirs++] = dir; + }); InitRD::for_each([](InitRD::File& f) { if (total_files >= 32) return; - kdbgln("registering file %s", f.name); - files[total_files++] = f; + uint64_t inode = total_files; + if (initrd_register_file(f, inode)) files[total_files++] = f; }); - for (uint32_t i = 0; i < total_files; i++) - { - VFS::Node& node = nodes[i]; - node.inode = i; - node.read_func = initrd_read; - node.length = files[i].size; - strncpy(node.name, files[i].name, sizeof(node.name)); - } +} + +static void initrd_initialize_root() +{ + initrd_root.length = 0; + initrd_root.inode = 0; + InitRD::Directory& root = dirs[0]; + total_dirs++; + strncpy(initrd_root.name, "initrd", sizeof(initrd_root.name)); + strncpy(root.name, "initrd", sizeof(root.name)); + initrd_root.find_func = initrd_scan_dir; } void InitRD::init() @@ -163,12 +329,8 @@ void InitRD::init() MemoryManager::get_unaligned_mappings((void*)bootboot.initrd_ptr, bootboot.initrd_size / PAGE_SIZE + 1); kdbgln("physical base at %lx, size %lx, mapped to %p", bootboot.initrd_ptr, bootboot.initrd_size, initrd_base); kdbgln("total blocks: %ld", get_total_blocks()); + initrd_initialize_root(); initrd_scan(); - initrd_root.length = 0; - initrd_root.inode = (uint64_t)-1; - strncpy(initrd_root.name, "initrd", 7); - initrd_root.find_func = initrd_scan_root; VFS::mount_root(&initrd_root); - kdbgln("mounted initrd at VFS root, total files: %d", total_files); initrd_initialized = true; } \ No newline at end of file diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 9dcf2402..10c4b81a 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -162,17 +162,23 @@ extern "C" void _start() kinfoln("Trying VFS"); - VFS::Node* node = VFS::open("sys/config"); - if (!node) { kerrorln("Unable to find file in VFS"); } + VFS::Node* node = VFS::resolve_path("/sys/"); + if (!node) { kerrorln("Unable to find /sys in VFS"); } else { kinfoln("Found '%s'", node->name); - char* buffer = (char*)kmalloc(node->length + 1); - buffer[node->length] = 0; - ssize_t nread = VFS::read(node, 0, node->length, buffer); - kdbgln("Read %zd bytes", nread); - kinfoln("Read: %s", buffer); - kfree(buffer); + node = VFS::resolve_path("config", node); + if (!node) kerrorln("unable to find /sys/config in VFS"); + else + { + kinfoln("Found '%s'", node->name); + char* buffer = (char*)kmalloc(node->length + 1); + buffer[node->length] = 0; + ssize_t nread = VFS::read(node, 0, node->length, buffer); + kdbgln("Read %zd bytes", nread); + kinfoln("Read: %s", buffer); + kfree(buffer); + } } PCI::scan([](PCI::Device& dev) { From 63b2de4e3c35c0972c8c66c8c037feb5bcb9f469 Mon Sep 17 00:00:00 2001 From: apio Date: Mon, 10 Oct 2022 19:00:24 +0200 Subject: [PATCH 6/7] Basic FDs --- kernel/include/fs/FileDescriptor.h | 35 ++++++++++++++++++++++++++++++ kernel/src/fs/FileDescriptor.cpp | 24 ++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 kernel/include/fs/FileDescriptor.h create mode 100644 kernel/src/fs/FileDescriptor.cpp diff --git a/kernel/include/fs/FileDescriptor.h b/kernel/include/fs/FileDescriptor.h new file mode 100644 index 00000000..798fa988 --- /dev/null +++ b/kernel/include/fs/FileDescriptor.h @@ -0,0 +1,35 @@ +#pragma once +#include "fs/VFS.h" +#include + +struct Descriptor +{ + bool is_open() + { + return m_is_open; + } + + bool can_read() + { + return m_can_read && m_is_open; + } + + void close() + { + m_is_open = false; + } + + ssize_t read(size_t size, char* buffer); + + void open(VFS::Node* node, bool can_read); + + Descriptor(const Descriptor& other); + Descriptor(); + + private: + uint64_t m_offset; + VFS::Node* m_node; + bool m_can_read; + + bool m_is_open; +} \ No newline at end of file diff --git a/kernel/src/fs/FileDescriptor.cpp b/kernel/src/fs/FileDescriptor.cpp new file mode 100644 index 00000000..84f91692 --- /dev/null +++ b/kernel/src/fs/FileDescriptor.cpp @@ -0,0 +1,24 @@ +#include "fs/FileDescriptor.h" + +Descriptor::Descriptor() : m_is_open(false) +{ +} + +Descriptor::Descriptor(const Descriptor& other) + : m_is_open(other.is_open()), m_can_read(other.can_read()), m_node(other.m_node), m_offset(other.m_offset) +{ +} + +void Descriptor::open(VFS::Node* node, bool can_read) +{ + m_can_read = can_read; + m_node = node; + m_offset = 0; +} + +ssize_t Descriptor::read(size_t size, char* buffer) +{ + ssize_t result = VFS::read(m_node, m_offset, size, buffer); + m_offset += result; + return result; +} \ No newline at end of file From da2ede345012d335ab84bde48593b9870ce28da7 Mon Sep 17 00:00:00 2001 From: apio Date: Mon, 10 Oct 2022 20:21:39 +0200 Subject: [PATCH 7/7] Kernel, libc, userspace: Implement file descriptors Kernel: Implement a descriptor struct which stores the opened node and read offset, and give each task 8 of those. Implement three syscalls: sys_read, sys_open and sys_close (sys_write still writes to the console instead of using a fd, for now) Implement three new errors: ENOENT, EBADF and EMFILE. libc: Implement the new errors, and the new syscalls in syscall(). Also fix _RETURN_WITH_ERRNO() to set errno correctly, which was making strerror() return null, thus crashing perror(). userspace: make init demonstrate the new file API. --- apps/src/init.c | 40 ++++++++++-- kernel/include/errno.h | 3 + kernel/include/fs/FileDescriptor.h | 9 ++- kernel/include/sys/Syscall.h | 8 ++- kernel/include/thread/Task.h | 5 ++ kernel/src/fs/FileDescriptor.cpp | 3 +- kernel/src/sys/Syscall.cpp | 3 + kernel/src/sys/stdio.cpp | 99 ++++++++++++++++++++++++++++++ kernel/src/trace/StackTracer.cpp | 7 +-- libs/libc/include/bits/error.h | 2 +- libs/libc/include/errno.h | 3 + libs/libc/include/luna/syscall.h | 3 + libs/libc/src/string.cpp | 6 +- libs/libc/src/unistd.cpp | 3 + 14 files changed, 174 insertions(+), 20 deletions(-) diff --git a/apps/src/init.c b/apps/src/init.c index b03c1d24..1d7c62b2 100644 --- a/apps/src/init.c +++ b/apps/src/init.c @@ -5,6 +5,8 @@ #include #include +typedef long ssize_t; + int main() { if (gettid() == 0) // why are we the idle task? @@ -25,14 +27,42 @@ int main() sleep(2); + const char* filename = "/sys/config"; + + printf("Opening %s for reading...\n", filename); + + int fd = syscall(SYS_open, filename, 1); + if (fd < 0) { - char* variable = malloc(200); - *variable = 3; - printf("Allocated variable at address %p\n", variable); - free(variable); + perror("open"); + // return 1; + } + else { printf("Got fd %d\n", fd); } + + char buf[4096]; + + ssize_t nread = syscall(SYS_read, fd, sizeof(buf), buf); + if (nread < 0) + { + perror("read"); + // return 1; + } + else + { + buf[nread] = 0; + + printf("Read %zd bytes\n\n", nread); + + printf("%s", buf); } - printf("Press any key to restart.\n"); + if (syscall(SYS_close, fd) < 0) + { + perror("close"); + // return 1; + } + + printf("\n\nPress any key to restart.\n"); return 0; } diff --git a/kernel/include/errno.h b/kernel/include/errno.h index 64cb4364..8c9786df 100644 --- a/kernel/include/errno.h +++ b/kernel/include/errno.h @@ -1,6 +1,9 @@ #pragma once #define EPERM 1 +#define ENOENT 2 +#define EBADF 9 #define ENOMEM 12 #define EINVAL 22 +#define EMFILE 24 #define ENOSYS 38 \ No newline at end of file diff --git a/kernel/include/fs/FileDescriptor.h b/kernel/include/fs/FileDescriptor.h index 798fa988..20b1199e 100644 --- a/kernel/include/fs/FileDescriptor.h +++ b/kernel/include/fs/FileDescriptor.h @@ -27,9 +27,8 @@ struct Descriptor Descriptor(); private: - uint64_t m_offset; - VFS::Node* m_node; - bool m_can_read; - bool m_is_open; -} \ No newline at end of file + bool m_can_read; + VFS::Node* m_node; + uint64_t m_offset; +}; \ No newline at end of file diff --git a/kernel/include/sys/Syscall.h b/kernel/include/sys/Syscall.h index e9564a53..9facde47 100644 --- a/kernel/include/sys/Syscall.h +++ b/kernel/include/sys/Syscall.h @@ -12,6 +12,9 @@ #define SYS_gettid 7 #define SYS_mmap 8 #define SYS_munmap 9 +#define SYS_open 10 +#define SYS_read 11 +#define SYS_close 12 namespace Syscall { @@ -27,4 +30,7 @@ void sys_rand(Context* context); void sys_getversion(Context* context, char* buffer, size_t max); void sys_gettid(Context* context); void sys_mmap(Context* context, void* address, size_t size, int flags); -void sys_munmap(Context* context, void* address, size_t size); \ No newline at end of file +void sys_munmap(Context* context, void* address, size_t size); +void sys_open(Context* context, const char* filename, int flags); +void sys_read(Context* context, int fd, size_t size, char* buffer); +void sys_close(Context* context, int fd); \ No newline at end of file diff --git a/kernel/include/thread/Task.h b/kernel/include/thread/Task.h index 4803d275..59239d24 100644 --- a/kernel/include/thread/Task.h +++ b/kernel/include/thread/Task.h @@ -1,7 +1,10 @@ #pragma once +#include "fs/FileDescriptor.h" #include "interrupts/Context.h" #include "sys/elf/Image.h" +#define TASK_MAX_FDS 8 + struct Task { enum TaskState @@ -36,6 +39,8 @@ struct Task bool is_user_task(); ELFImage* image = nullptr; + + Descriptor files[TASK_MAX_FDS]; }; void set_context_from_task(Task& task, Context* ctx); diff --git a/kernel/src/fs/FileDescriptor.cpp b/kernel/src/fs/FileDescriptor.cpp index 84f91692..4af4d25d 100644 --- a/kernel/src/fs/FileDescriptor.cpp +++ b/kernel/src/fs/FileDescriptor.cpp @@ -5,7 +5,7 @@ Descriptor::Descriptor() : m_is_open(false) } Descriptor::Descriptor(const Descriptor& other) - : m_is_open(other.is_open()), m_can_read(other.can_read()), m_node(other.m_node), m_offset(other.m_offset) + : m_is_open(other.m_is_open), m_can_read(other.m_can_read), m_node(other.m_node), m_offset(other.m_offset) { } @@ -14,6 +14,7 @@ void Descriptor::open(VFS::Node* node, bool can_read) m_can_read = can_read; m_node = node; m_offset = 0; + m_is_open = true; } ssize_t Descriptor::read(size_t size, char* buffer) diff --git a/kernel/src/sys/Syscall.cpp b/kernel/src/sys/Syscall.cpp index 70111a09..78c68ae5 100644 --- a/kernel/src/sys/Syscall.cpp +++ b/kernel/src/sys/Syscall.cpp @@ -26,6 +26,9 @@ void Syscall::entry(Context* context) case SYS_gettid: sys_gettid(context); break; case SYS_mmap: sys_mmap(context, (void*)context->rdi, context->rsi, (int)context->rdx); break; case SYS_munmap: sys_munmap(context, (void*)context->rdi, context->rsi); break; + case SYS_open: sys_open(context, (const char*)context->rdi, (int)context->rsi); break; + case SYS_read: sys_read(context, (int)context->rdi, context->rsi, (char*)context->rdx); break; + case SYS_close: sys_close(context, (int)context->rdi); break; default: context->rax = -ENOSYS; break; } } \ No newline at end of file diff --git a/kernel/src/sys/stdio.cpp b/kernel/src/sys/stdio.cpp index 857501bc..91b1a63d 100644 --- a/kernel/src/sys/stdio.cpp +++ b/kernel/src/sys/stdio.cpp @@ -1,9 +1,108 @@ +#define MODULE "stdio" + +#include "errno.h" #include "interrupts/Context.h" #include "io/Serial.h" +#include "log/Log.h" #include "render/TextRenderer.h" +#include "thread/Scheduler.h" +#include "thread/Task.h" + +#define STDIO_FAIL(function, error) kwarnln("%s failed with %s", #function, #error) void sys_write(Context* context, const char* addr, size_t size) { context->rax = size; TextRenderer::write(addr, size); +} + +void sys_open(Context* context, const char* filename, int flags) +{ + Task* current_task = Scheduler::current_task(); + int fd = -1; + for (fd = 0; fd < TASK_MAX_FDS; fd++) + { + if (!current_task->files[fd].is_open()) break; + } + if (fd == -1) + { + STDIO_FAIL(open, EMFILE); + context->rax = -EMFILE; + return; + } + VFS::Node* node = VFS::resolve_path(filename); + if (!node) + { + STDIO_FAIL(open, ENOENT); + context->rax = -ENOENT; + return; + } + current_task->files[fd].open(node, + (bool)flags); // FIXME: Implement more flags. (right now, 1 is can_read, 0 is not) + context->rax = fd; + return; +} + +void sys_read(Context* context, int fd, size_t size, char* buffer) +{ + if (!buffer) + { + STDIO_FAIL(read, EINVAL); + context->rax = -EINVAL; + return; + } + if (fd >= TASK_MAX_FDS) + { + STDIO_FAIL(read, EBADF); + context->rax = -EBADF; + return; + } + if (fd < 0) + { + STDIO_FAIL(read, EBADF); + context->rax = -EBADF; + return; + } + Task* current_task = Scheduler::current_task(); + if (!current_task->files[fd].is_open()) + { + STDIO_FAIL(read, EBADF); + context->rax = -EBADF; + return; + } + if (!current_task->files[fd].can_read()) + { + STDIO_FAIL(read, EBADF); + context->rax = -EBADF; + return; + } + ssize_t result = current_task->files[fd].read(size, buffer); + context->rax = (size_t)result; + return; +} + +void sys_close(Context* context, int fd) +{ + if (fd >= TASK_MAX_FDS) + { + STDIO_FAIL(close, EBADF); + context->rax = -EBADF; + return; + } + if (fd < 0) + { + STDIO_FAIL(close, EBADF); + context->rax = -EBADF; + return; + } + Task* current_task = Scheduler::current_task(); + if (!current_task->files[fd].is_open()) + { + STDIO_FAIL(close, EBADF); + context->rax = -EBADF; + return; + } + current_task->files[fd].close(); + context->rax = 0; + return; } \ No newline at end of file diff --git a/kernel/src/trace/StackTracer.cpp b/kernel/src/trace/StackTracer.cpp index 58cdbeeb..a44670c7 100644 --- a/kernel/src/trace/StackTracer.cpp +++ b/kernel/src/trace/StackTracer.cpp @@ -21,7 +21,7 @@ typedef struct stackframe void StackTracer::trace() { stackframe* frame = (stackframe*)m_base_pointer; - while (Memory::is_kernel_address((uintptr_t)frame)) + while (frame) { char symbol_name[512]; get_symbol_name(frame->instruction, symbol_name); @@ -32,11 +32,6 @@ void StackTracer::trace() void StackTracer::trace_with_ip(uintptr_t ip) { - if (!Memory::is_kernel_address(ip)) - { - printf("(user stack)"); - return; - } char symbol_name[512]; get_symbol_name(ip, symbol_name); printf("%lx: %s\n", ip, symbol_name); diff --git a/libs/libc/include/bits/error.h b/libs/libc/include/bits/error.h index 607695d8..43d4a195 100644 --- a/libs/libc/include/bits/error.h +++ b/libs/libc/include/bits/error.h @@ -7,7 +7,7 @@ do { \ if (rc < 0) \ { \ - errno = (int)rc; \ + errno = (int)(-rc); \ return -1; \ } \ return (type)rc; \ diff --git a/libs/libc/include/errno.h b/libs/libc/include/errno.h index 27deecbf..912dd606 100644 --- a/libs/libc/include/errno.h +++ b/libs/libc/include/errno.h @@ -4,8 +4,11 @@ extern int errno; #define EPERM 1 +#define ENOENT 2 +#define EBADF 9 #define ENOMEM 12 #define EINVAL 22 +#define EMFILE 24 #define ENOSYS 38 #endif \ No newline at end of file diff --git a/libs/libc/include/luna/syscall.h b/libs/libc/include/luna/syscall.h index d11a83a2..8c0715dc 100644 --- a/libs/libc/include/luna/syscall.h +++ b/libs/libc/include/luna/syscall.h @@ -11,6 +11,9 @@ #define SYS_gettid 7 #define SYS_mmap 8 #define SYS_munmap 9 +#define SYS_open 10 +#define SYS_read 11 +#define SYS_close 12 #ifndef __want_syscalls #ifdef __cplusplus diff --git a/libs/libc/src/string.cpp b/libs/libc/src/string.cpp index c99ef0ad..d35c6689 100644 --- a/libs/libc/src/string.cpp +++ b/libs/libc/src/string.cpp @@ -121,7 +121,11 @@ extern "C" case EINVAL: return "Invalid argument"; case ENOMEM: return "Out of memory"; case ENOSYS: return "Function not implemented"; - default: return 0; + case ENOENT: return "No such file or directory"; + case EBADF: return "Bad file descriptor"; + case EMFILE: return "Too many open files"; + case 0: return "Success"; + default: return (char*)(unsigned long int)errnum; } } diff --git a/libs/libc/src/unistd.cpp b/libs/libc/src/unistd.cpp index 783c39b8..bf1ca1e9 100644 --- a/libs/libc/src/unistd.cpp +++ b/libs/libc/src/unistd.cpp @@ -35,15 +35,18 @@ extern "C" case SYS_gettid: case SYS_rand: result = __luna_syscall0(number); break; case SYS_exit: + case SYS_close: case SYS_sleep: result = __luna_syscall1(number, va_arg(ap, arg)); break; case SYS_write: case SYS_munmap: + case SYS_open: case SYS_getversion: { arg arg0 = va_arg(ap, arg); arg arg1 = va_arg(ap, arg); result = __luna_syscall2(number, arg0, arg1); break; } + case SYS_read: case SYS_mmap: { arg arg0 = va_arg(ap, arg); arg arg1 = va_arg(ap, arg);