340 lines
8.4 KiB
C++
340 lines
8.4 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;
|
|
}
|
|
}
|
|
|
|
VFS::Node* VFS::resolve_parent(const char* filename, Node* root)
|
|
{
|
|
char* dir = better_dirname(filename);
|
|
auto* result = resolve_path(dir, root);
|
|
kfree(dir);
|
|
return result;
|
|
}
|
|
|
|
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 (!strncmp(name, ".", strlen(name)) || !strncmp(name, "..", strlen(name)))
|
|
{
|
|
kwarnln("Attempted to mkdir . or .., which already exist");
|
|
return -EEXIST;
|
|
}
|
|
if (node->find_func(node, name) != nullptr)
|
|
{
|
|
kwarnln("Already exists");
|
|
return -EEXIST;
|
|
}
|
|
return node->mkdir_func(node, name, 0755);
|
|
}
|
|
|
|
int VFS::do_mkdir(const char* path, const char* name, int uid, int gid, mode_t mode)
|
|
{
|
|
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 (!strncmp(name, ".", strlen(name)) || !strncmp(name, "..", strlen(name)))
|
|
{
|
|
kwarnln("Attempted to mkdir . or .., which already exist");
|
|
return -EEXIST;
|
|
}
|
|
if (node->find_func(node, name) != nullptr)
|
|
{
|
|
kwarnln("Already exists");
|
|
return -EEXIST;
|
|
}
|
|
if (!can_write(node, uid, gid))
|
|
{
|
|
kwarnln("Not enough permissions");
|
|
return -EACCES;
|
|
}
|
|
return node->mkdir_func(node, name, mode);
|
|
}
|
|
|
|
int VFS::mkdir(const char* pathname)
|
|
{
|
|
char* base = better_basename(pathname);
|
|
char* dir = better_dirname(pathname);
|
|
|
|
kdbgln("mkdir(): creating %s in directory %s", base, dir);
|
|
|
|
int result = mkdir(dir, base);
|
|
|
|
kfree(base);
|
|
kfree(dir);
|
|
|
|
return result;
|
|
}
|
|
|
|
int VFS::do_mkdir(const char* pathname, int uid, int gid, mode_t mode)
|
|
{
|
|
char* base = better_basename(pathname);
|
|
char* dir = better_dirname(pathname);
|
|
|
|
kdbgln("mkdir(): creating %s in directory %s", base, dir);
|
|
|
|
int result = do_mkdir(dir, base, uid, gid, mode);
|
|
|
|
kfree(base);
|
|
kfree(dir);
|
|
|
|
return result;
|
|
}
|
|
|
|
Result<VFS::Node*> VFS::create(const char* pathname, mode_t mode, uid_t uid, gid_t gid)
|
|
{
|
|
VFS::Node* parent = VFS::resolve_parent(pathname);
|
|
if (!parent) return {ENOENT};
|
|
if (parent->type != VFS_DIRECTORY)
|
|
{
|
|
kwarnln("Attempting to create %s, parent is not a directory", pathname);
|
|
return {ENOTDIR};
|
|
}
|
|
if (!parent->create_func)
|
|
{
|
|
kwarnln("Chosen node does not support create()");
|
|
return {ENOTSUP}; // FIXME: Probably EROFS.
|
|
}
|
|
if (!parent->find_func)
|
|
{
|
|
kwarnln("Chosen node does not support finddir()");
|
|
return {ENOTSUP};
|
|
}
|
|
if (!can_write(parent, uid, gid)) return {EACCES};
|
|
char* child = better_basename(pathname);
|
|
if (parent->find_func(parent, child))
|
|
{
|
|
kwarnln("Already exists");
|
|
kfree(child);
|
|
return {EEXIST};
|
|
}
|
|
VFS::Node* child_node = parent->create_func(parent, child, mode, uid, gid);
|
|
kfree(child);
|
|
return child_node;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
VFS::Node* VFS::readdir(VFS::Node* dir, long offset)
|
|
{
|
|
if (!dir) return 0;
|
|
if (!dir->readdir_func) return 0;
|
|
return dir->readdir_func(dir, offset);
|
|
}
|
|
|
|
bool VFS::can_execute(VFS::Node* node, int uid, int gid)
|
|
{
|
|
if (uid == node->uid) return node->mode & 0100;
|
|
if (gid == node->gid) return node->mode & 0010;
|
|
return node->mode & 0001;
|
|
}
|
|
|
|
bool VFS::can_write(VFS::Node* node, int uid, int gid)
|
|
{
|
|
if (uid == node->uid) return node->mode & 0200;
|
|
if (gid == node->gid) return node->mode & 0020;
|
|
return node->mode & 0002;
|
|
}
|
|
|
|
bool VFS::can_read(VFS::Node* node, int uid, int gid)
|
|
{
|
|
if (uid == node->uid) return node->mode & 0400;
|
|
if (gid == node->gid) return node->mode & 0040;
|
|
return node->mode & 0004;
|
|
}
|
|
|
|
bool VFS::is_setuid(VFS::Node* node)
|
|
{
|
|
return node->mode & 04000;
|
|
}
|
|
|
|
bool VFS::is_setgid(VFS::Node* node)
|
|
{
|
|
return node->mode & 02000;
|
|
} |