#include "vfs.hpp" #include "io-device.hpp" #include "orbis/error/ErrorCode.hpp" #include "orbis/error/SysResult.hpp" #include #include #include static orbis::ErrorCode devfs_stat(orbis::File *file, orbis::Stat *sb, orbis::Thread *thread) { *sb = {}; // TODO return {}; } static orbis::FileOps devfs_ops = { .stat = devfs_stat, }; struct DevFs : IoDevice { std::map, std::less<>> devices; orbis::ErrorCode open(orbis::Ref *file, const char *path, std::uint32_t flags, std::uint32_t mode, orbis::Thread *thread) override { if (path[0] == '\0') { auto result = orbis::knew(); for (auto &[name, dev] : devices) { auto &entry = result->dirEntries.emplace_back(); entry.fileno = result->dirEntries.size(); entry.reclen = sizeof(orbis::Dirent); entry.type = orbis::kDtBlk; entry.namlen = name.size(); std::strncpy(entry.name, name.c_str(), sizeof(entry.name)); } result->ops = &devfs_ops; *file = result; return {}; } std::string_view devPath = path; if (auto it = devices.find(devPath); it != devices.end()) { return it->second->open(file, "", flags, mode, thread); } std::fprintf(stderr, "device %s not exists\n", path); return orbis::ErrorCode::NOENT; } }; struct ProcFs : IoDevice { orbis::ErrorCode open(orbis::Ref *file, const char *path, std::uint32_t flags, std::uint32_t mode, orbis::Thread *thread) override { std::fprintf(stderr, "procfs access: %s\n", path); std::abort(); return orbis::ErrorCode::NOENT; } }; static orbis::shared_mutex gMountMtx; static std::map, std::greater<>> gMountsMap; static orbis::Ref gDevFs; void rx::vfs::fork() { std::lock_guard lock(gMountMtx); // NOTE: do not decrease reference counter, it managed by parent process auto parentDevFs = gDevFs.release(); for (auto &mount : gMountsMap) { mount.second->incRef(); // increase reference for new process } gDevFs = orbis::knew(); gMountsMap["/dev/"] = gDevFs; gMountsMap["/proc/"] = orbis::knew(); for (auto &fs : parentDevFs->devices) { gDevFs->devices[fs.first] = fs.second; } } void rx::vfs::initialize() { gDevFs = orbis::knew(); gMountsMap.emplace("/dev/", gDevFs); gMountsMap.emplace("/proc/", orbis::knew()); } void rx::vfs::deinitialize() { gDevFs = nullptr; gMountsMap.clear(); } void rx::vfs::addDevice(std::string name, IoDevice *device) { std::lock_guard lock(gMountMtx); gDevFs->devices[std::move(name)] = device; } std::pair, std::string> rx::vfs::get(const std::filesystem::path &guestPath) { std::string normalPath = std::filesystem::path(guestPath).lexically_normal(); std::string_view path = normalPath; orbis::Ref device; std::lock_guard lock(gMountMtx); if (gDevFs != nullptr) { std::string_view devPath = "/dev/"; if (path.starts_with(devPath) || path == devPath.substr(0, devPath.size() - 1)) { if (path.size() > devPath.size()) { path.remove_prefix(devPath.size()); } else { path = {}; } return {gDevFs, std::string(path)}; } } for (auto &mount : gMountsMap) { if (!path.starts_with(mount.first)) { if (mount.first.size() - 1 != path.size() || !std::string_view(mount.first).starts_with(path)) { continue; } } device = mount.second; if (path.size() > mount.first.length()) { path.remove_prefix(mount.first.length()); } else { path = {}; } return {mount.second, std::string(path)}; } return {}; } orbis::SysResult rx::vfs::mount(const std::filesystem::path &guestPath, IoDevice *dev) { auto mp = guestPath.lexically_normal().string(); if (!mp.ends_with("/")) { mp += "/"; } std::lock_guard lock(gMountMtx); auto [it, inserted] = gMountsMap.emplace(std::move(mp), dev); if (!inserted) { return orbis::ErrorCode::EXIST; } return {}; } orbis::SysResult rx::vfs::open(std::string_view path, int flags, int mode, orbis::Ref *file, orbis::Thread *thread) { auto [device, devPath] = get(path); if (device == nullptr) { return orbis::ErrorCode::NOENT; } // std::fprintf(stderr, "sys_open %s\n", std::string(path).c_str()); return device->open(file, devPath.c_str(), flags, mode, thread); } bool rx::vfs::exists(std::string_view path, orbis::Thread *thread) { auto [device, devPath] = get(path); if (device == nullptr) { return false; } orbis::Ref file; if (device->open(&file, devPath.c_str(), 0, 0, thread) != orbis::ErrorCode{}) { return false; } return true; } orbis::SysResult rx::vfs::mkdir(std::string_view path, int mode, orbis::Thread *thread) { auto [device, devPath] = get(path); if (device == nullptr) { return orbis::ErrorCode::NOENT; } return device->mkdir(devPath.c_str(), mode, thread); } orbis::SysResult rx::vfs::rmdir(std::string_view path, orbis::Thread *thread) { auto [device, devPath] = get(path); if (device == nullptr) { return orbis::ErrorCode::NOENT; } return device->rmdir(devPath.c_str(), thread); } orbis::SysResult rx::vfs::rename(std::string_view from, std::string_view to, orbis::Thread *thread) { auto [fromDevice, fromDevPath] = get(from); if (fromDevice == nullptr) { return orbis::ErrorCode::NOENT; } auto [toDevice, toDevPath] = get(to); if (toDevice == nullptr) { return orbis::ErrorCode::NOENT; } if (fromDevice != toDevice) { std::fprintf(stderr, "cross fs rename operation: %s -> %s\n", std::string(from).c_str(), std::string(to).c_str()); std::abort(); } return fromDevice->rename(fromDevPath.c_str(), toDevPath.c_str(), thread); } orbis::ErrorCode rx::vfs::unlink(std::string_view path, orbis::Thread *thread) { auto [device, devPath] = get(path); if (device == nullptr) { return orbis::ErrorCode::NOENT; } return device->unlink(devPath.c_str(), false, thread); } orbis::ErrorCode rx::vfs::unlinkAll(std::string_view path, orbis::Thread *thread) { auto [device, devPath] = get(path); if (device == nullptr) { return orbis::ErrorCode::NOENT; } return device->unlink(devPath.c_str(), true, thread); } orbis::ErrorCode rx::vfs::createSymlink(std::string_view target, std::string_view linkPath, orbis::Thread *thread) { auto [fromDevice, fromDevPath] = get(target); if (fromDevice == nullptr) { return orbis::ErrorCode::NOENT; } auto [targetDevice, toDevPath] = get(linkPath); if (targetDevice == nullptr) { return orbis::ErrorCode::NOENT; } if (fromDevice != targetDevice) { std::fprintf(stderr, "cross fs operation: %s -> %s\n", std::string(target).c_str(), std::string(linkPath).c_str()); std::abort(); } return fromDevice->createSymlink(fromDevPath.c_str(), toDevPath.c_str(), thread); }