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...
This commit is contained in:
apio 2022-10-10 18:44:43 +02:00
parent 2be70d0bc1
commit bbe7c6e658
4 changed files with 244 additions and 47 deletions

View File

@ -21,9 +21,10 @@ namespace VFS
}; };
ssize_t read(Node* node, size_t offset, size_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); void mount_root(Node* root);
Node* resolve_path(const char* filename, Node* root = nullptr);
Node* root(); Node* root();
} }

View File

@ -2,6 +2,7 @@
#include "fs/VFS.h" #include "fs/VFS.h"
#include "log/Log.h" #include "log/Log.h"
#include "std/stdlib.h"
#include "std/string.h" #include "std/string.h"
static VFS::Node* vfs_root; 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); 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) void VFS::mount_root(Node* root)
{ {
if (!root) if (!root)
@ -62,4 +45,49 @@ void VFS::mount_root(Node* root)
VFS::Node* VFS::root() VFS::Node* VFS::root()
{ {
return 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;
}
} }

View File

@ -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 InitRD::File files[32];
static uint32_t total_files = 0; 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) 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; 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; 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) { InitRD::for_each([](InitRD::File& f) {
if (total_files >= 32) return; if (total_files >= 32) return;
kdbgln("registering file %s", f.name); uint64_t inode = total_files;
files[total_files++] = f; if (initrd_register_file(f, inode)) files[total_files++] = f;
}); });
for (uint32_t i = 0; i < total_files; i++) }
{
VFS::Node& node = nodes[i]; static void initrd_initialize_root()
node.inode = i; {
node.read_func = initrd_read; initrd_root.length = 0;
node.length = files[i].size; initrd_root.inode = 0;
strncpy(node.name, files[i].name, sizeof(node.name)); 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() void InitRD::init()
@ -163,12 +329,8 @@ void InitRD::init()
MemoryManager::get_unaligned_mappings((void*)bootboot.initrd_ptr, bootboot.initrd_size / PAGE_SIZE + 1); 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("physical base at %lx, size %lx, mapped to %p", bootboot.initrd_ptr, bootboot.initrd_size, initrd_base);
kdbgln("total blocks: %ld", get_total_blocks()); kdbgln("total blocks: %ld", get_total_blocks());
initrd_initialize_root();
initrd_scan(); 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(&initrd_root);
kdbgln("mounted initrd at VFS root, total files: %d", total_files);
initrd_initialized = true; initrd_initialized = true;
} }

View File

@ -162,17 +162,23 @@ extern "C" void _start()
kinfoln("Trying VFS"); kinfoln("Trying VFS");
VFS::Node* node = VFS::open("sys/config"); VFS::Node* node = VFS::resolve_path("/sys/");
if (!node) { kerrorln("Unable to find file in VFS"); } if (!node) { kerrorln("Unable to find /sys in VFS"); }
else else
{ {
kinfoln("Found '%s'", node->name); kinfoln("Found '%s'", node->name);
char* buffer = (char*)kmalloc(node->length + 1); node = VFS::resolve_path("config", node);
buffer[node->length] = 0; if (!node) kerrorln("unable to find /sys/config in VFS");
ssize_t nread = VFS::read(node, 0, node->length, buffer); else
kdbgln("Read %zd bytes", nread); {
kinfoln("Read: %s", buffer); kinfoln("Found '%s'", node->name);
kfree(buffer); 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) { PCI::scan([](PCI::Device& dev) {