Luna/kernel/src/fs/VFS.cpp
apio da61e3648f Kernel: Implement blocking reads
This is a huge step forward!! bc actually runs now, without echo or backspace, but it runs!!
2022-10-21 21:26:19 +02:00

204 lines
4.9 KiB
C++

#define MODULE "vfs"
#include "fs/VFS.h"
#include "log/Log.h"
#include "std/errno.h"
#include "std/libgen.h"
#include "std/stdlib.h"
#include "std/string.h"
static VFS::Node* vfs_root;
int VFS::would_block(Node* node)
{
if (!node) { return 0; }
if (!node->block_func) { return 0; }
return node->block_func(node);
}
ssize_t VFS::read(Node* node, size_t offset, size_t length, char* buffer)
{
if (!node)
{
kwarnln("read() failed: trying to read from nullptr");
return -1;
}
if (node->type == VFS_DIRECTORY)
{
kwarnln("read() failed: is a directory");
return -EISDIR;
}
if (!node->read_func)
{
kwarnln("read() failed: the chosen node doesn't support reading");
return -1;
}
return node->read_func(node, offset, length, buffer);
}
ssize_t VFS::write(Node* node, size_t offset, size_t length, const char* buffer)
{
if (!node)
{
kwarnln("write() failed: trying to write to nullptr");
return -1;
}
if (node->type == VFS_DIRECTORY)
{
kwarnln("write() failed: is a directory");
return -EISDIR;
}
if (!node->write_func)
{
kwarnln("write() failed: the chosen node doesn't support writing");
return -1;
}
return node->write_func(node, offset, length, buffer);
}
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;
}
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) { return 0; }
if (child->flags & VFS_MOUNTPOINT)
{
if (!child->link)
{
kwarnln("Current node's link is null");
return 0;
}
child = child->link;
}
current_node = child;
kfree(buffer);
}
filename += path_section_size;
}
}
int VFS::mkdir(const char* path, const char* name)
{
Node* node = resolve_path(path, vfs_root);
if (!node)
{
kwarnln("Attempting to mkdir in %s, which does not exist", path);
return -ENOENT;
}
if (node->type != VFS_DIRECTORY)
{
kwarnln("Attempting to mkdir in %s, which is not a directory!!", path);
return -ENOTDIR;
}
if (!node->mkdir_func)
{
kwarnln("Chosen node does not support mkdir()");
return -ENOTSUP;
}
if (!node->find_func)
{
kwarnln("Chosen node does not support finddir()");
return -ENOTSUP;
}
if (node->find_func(node, name) != nullptr)
{
kwarnln("Already exists");
return -EEXIST;
}
return node->mkdir_func(node, name);
}
int VFS::mkdir(const char* pathname)
{
char* bstr = strdup(pathname);
char* dstr = strdup(pathname);
char* base = basename(bstr);
char* dir = dirname(dstr);
kdbgln("mkdir(): creating %s in directory %s", base, dir);
int result = mkdir(dir, base);
kfree(bstr);
kfree(dstr);
return result;
}
bool VFS::exists(const char* pathname)
{
return resolve_path(pathname) != nullptr;
}
void VFS::mount(Node* mountpoint, Node* mounted)
{
if (!mountpoint || !mounted) return;
if (mountpoint->flags & VFS_MOUNTPOINT || mounted->flags & VFS_MOUNTPOINT) return;
mountpoint->link = mounted;
mountpoint->flags |= VFS_MOUNTPOINT;
}
void VFS::mount(const char* pathname, Node* mounted)
{
return mount(resolve_path(pathname), mounted);
}
void VFS::unmount(Node* mountpoint)
{
if (!mountpoint) return;
if (!(mountpoint->flags & VFS_MOUNTPOINT)) return;
mountpoint->flags &= ~VFS_MOUNTPOINT;
}