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) {