#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::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; }