From 60e11486f4d5318d5d274492f9822cc3f05196bf Mon Sep 17 00:00:00 2001 From: DH Date: Sun, 29 Oct 2023 12:30:37 +0300 Subject: [PATCH] [rpcsx-os] Initial sys_rename, sys_mkdir, sys_rmdir implementation vfs: implement overlapped mounts --- .../include/orbis/thread/ProcessOps.hpp | 3 +- orbis-kernel/src/sys/sys_vfs.cpp | 3 + rpcsx-os/io-device.cpp | 113 +++++++++++++- rpcsx-os/io-device.hpp | 9 ++ rpcsx-os/main.cpp | 48 +++--- rpcsx-os/ops.cpp | 16 ++ rpcsx-os/vfs.cpp | 147 +++++++++++++----- rpcsx-os/vfs.hpp | 4 + 8 files changed, 280 insertions(+), 63 deletions(-) diff --git a/orbis-kernel/include/orbis/thread/ProcessOps.hpp b/orbis-kernel/include/orbis/thread/ProcessOps.hpp index 7de169906..86f408c0c 100644 --- a/orbis-kernel/include/orbis/thread/ProcessOps.hpp +++ b/orbis-kernel/include/orbis/thread/ProcessOps.hpp @@ -41,7 +41,8 @@ struct ProcessOps { SysResult (*shm_open)(Thread *thread, const char *path, sint flags, sint mode, Ref *file); SysResult (*mkdir)(Thread *thread, ptr path, sint mode); - SysResult (*rmdir)(Thread *thread, ptr path) = nullptr; + SysResult (*rmdir)(Thread *thread, ptr path); + SysResult (*rename)(Thread *thread, ptr from, ptr to); SysResult (*blockpool_open)(Thread *thread, Ref *file); SysResult (*blockpool_map)(Thread *thread, caddr_t addr, size_t len, sint prot, sint flags); diff --git a/orbis-kernel/src/sys/sys_vfs.cpp b/orbis-kernel/src/sys/sys_vfs.cpp index fd6007eb4..4a00bad20 100644 --- a/orbis-kernel/src/sys/sys_vfs.cpp +++ b/orbis-kernel/src/sys/sys_vfs.cpp @@ -285,6 +285,9 @@ orbis::SysResult orbis::sys_freebsd6_truncate(Thread *thread, ptr path, orbis::SysResult orbis::sys_fsync(Thread *thread, sint fd) { return {}; } orbis::SysResult orbis::sys_rename(Thread *thread, ptr from, ptr to) { + if (auto rename = thread->tproc->ops->rename) { + return rename(thread, from, to); + } return ErrorCode::NOSYS; } orbis::SysResult orbis::sys_renameat(Thread *thread, sint oldfd, ptr old, diff --git a/rpcsx-os/io-device.cpp b/rpcsx-os/io-device.cpp index 365e1e517..568f4eafd 100644 --- a/rpcsx-os/io-device.cpp +++ b/rpcsx-os/io-device.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -45,8 +46,98 @@ struct HostFsDevice : IoDevice { orbis::ErrorCode open(orbis::Ref *file, const char *path, std::uint32_t flags, std::uint32_t mode, orbis::Thread *thread) override; + orbis::ErrorCode mkdir(const char *path, int mode, orbis::Thread *thread) override; + orbis::ErrorCode rmdir(const char *path, orbis::Thread *thread) override; + orbis::ErrorCode rename(const char *from, const char *to, orbis::Thread *thread) override; }; +static orbis::ErrorCode convertErrc(std::errc errc) { + if (errc == std::errc{}) { + return{}; + } + + switch (errc) { + case std::errc::address_family_not_supported: return orbis::ErrorCode::AFNOSUPPORT; + case std::errc::address_in_use: return orbis::ErrorCode::ADDRINUSE; + case std::errc::address_not_available: return orbis::ErrorCode::ADDRNOTAVAIL; + case std::errc::already_connected: return orbis::ErrorCode::ISCONN; + case std::errc::argument_out_of_domain: return orbis::ErrorCode::DOM; + case std::errc::bad_address: return orbis::ErrorCode::FAULT; + case std::errc::bad_file_descriptor: return orbis::ErrorCode::BADF; + case std::errc::bad_message: return orbis::ErrorCode::BADMSG; + case std::errc::broken_pipe: return orbis::ErrorCode::PIPE; + case std::errc::connection_aborted: return orbis::ErrorCode::CONNABORTED; + case std::errc::connection_already_in_progress: return orbis::ErrorCode::ALREADY; + case std::errc::connection_refused: return orbis::ErrorCode::CONNREFUSED; + case std::errc::connection_reset: return orbis::ErrorCode::CONNRESET; + case std::errc::cross_device_link: return orbis::ErrorCode::XDEV; + case std::errc::destination_address_required: return orbis::ErrorCode::DESTADDRREQ; + case std::errc::device_or_resource_busy: return orbis::ErrorCode::BUSY; + case std::errc::directory_not_empty: return orbis::ErrorCode::NOTEMPTY; + case std::errc::executable_format_error: return orbis::ErrorCode::NOEXEC; + case std::errc::file_exists: return orbis::ErrorCode::EXIST; + case std::errc::file_too_large: return orbis::ErrorCode::FBIG; + case std::errc::filename_too_long: return orbis::ErrorCode::NAMETOOLONG; + case std::errc::function_not_supported: return orbis::ErrorCode::NOSYS; + case std::errc::host_unreachable: return orbis::ErrorCode::HOSTUNREACH; + case std::errc::identifier_removed: return orbis::ErrorCode::IDRM; + case std::errc::illegal_byte_sequence: return orbis::ErrorCode::ILSEQ; + case std::errc::inappropriate_io_control_operation: return orbis::ErrorCode::NOTTY; + case std::errc::interrupted: return orbis::ErrorCode::INTR; + case std::errc::invalid_argument: return orbis::ErrorCode::INVAL; + case std::errc::invalid_seek: return orbis::ErrorCode::SPIPE; + case std::errc::io_error: return orbis::ErrorCode::IO; + case std::errc::is_a_directory: return orbis::ErrorCode::ISDIR; + case std::errc::message_size: return orbis::ErrorCode::MSGSIZE; + case std::errc::network_down: return orbis::ErrorCode::NETDOWN; + case std::errc::network_reset: return orbis::ErrorCode::NETRESET; + case std::errc::network_unreachable: return orbis::ErrorCode::NETUNREACH; + case std::errc::no_buffer_space: return orbis::ErrorCode::NOBUFS; + case std::errc::no_child_process: return orbis::ErrorCode::CHILD; + case std::errc::no_link: return orbis::ErrorCode::NOLINK; + case std::errc::no_lock_available: return orbis::ErrorCode::NOLCK; + case std::errc::no_message: return orbis::ErrorCode::NOMSG; + case std::errc::no_protocol_option: return orbis::ErrorCode::NOPROTOOPT; + case std::errc::no_space_on_device: return orbis::ErrorCode::NOSPC; + case std::errc::no_such_device_or_address: return orbis::ErrorCode::NXIO; + case std::errc::no_such_device: return orbis::ErrorCode::NODEV; + case std::errc::no_such_file_or_directory: return orbis::ErrorCode::NOENT; + case std::errc::no_such_process: return orbis::ErrorCode::SRCH; + case std::errc::not_a_directory: return orbis::ErrorCode::NOTDIR; + case std::errc::not_a_socket: return orbis::ErrorCode::NOTSOCK; + case std::errc::not_connected: return orbis::ErrorCode::NOTCONN; + case std::errc::not_enough_memory: return orbis::ErrorCode::NOMEM; + case std::errc::not_supported: return orbis::ErrorCode::NOTSUP; + case std::errc::operation_canceled: return orbis::ErrorCode::CANCELED; + case std::errc::operation_in_progress: return orbis::ErrorCode::INPROGRESS; + case std::errc::operation_not_permitted: return orbis::ErrorCode::PERM; + case std::errc::operation_would_block: return orbis::ErrorCode::WOULDBLOCK; + case std::errc::permission_denied: return orbis::ErrorCode::ACCES; + case std::errc::protocol_error: return orbis::ErrorCode::PROTO; + case std::errc::protocol_not_supported: return orbis::ErrorCode::PROTONOSUPPORT; + case std::errc::read_only_file_system: return orbis::ErrorCode::ROFS; + case std::errc::resource_deadlock_would_occur: return orbis::ErrorCode::DEADLK; + case std::errc::result_out_of_range: return orbis::ErrorCode::RANGE; + case std::errc::text_file_busy: return orbis::ErrorCode::TXTBSY; + case std::errc::timed_out: return orbis::ErrorCode::TIMEDOUT; + case std::errc::too_many_files_open_in_system: return orbis::ErrorCode::NFILE; + case std::errc::too_many_files_open: return orbis::ErrorCode::MFILE; + case std::errc::too_many_links: return orbis::ErrorCode::MLINK; + case std::errc::too_many_symbolic_link_levels: return orbis::ErrorCode::LOOP; + case std::errc::value_too_large: return orbis::ErrorCode::OVERFLOW; + case std::errc::wrong_protocol_type: return orbis::ErrorCode::PROTOTYPE; + default: + return orbis::ErrorCode::FAULT; + } +} + +static orbis::ErrorCode convertErrorCode(const std::error_code &code) { + if (!code) { + return{}; + } + return convertErrc(static_cast(code.value())); +} + static orbis::ErrorCode convertErrno() { switch (auto error = errno) { case EPERM: @@ -254,6 +345,10 @@ static const orbis::FileOps socketOps = { }; IoDevice *createHostIoDevice(orbis::kstring hostPath) { + while (hostPath.size() > 1 && hostPath.ends_with("/")) { + hostPath.resize(hostPath.size() - 1); + } + return orbis::knew(std::move(hostPath)); } @@ -329,7 +424,7 @@ orbis::ErrorCode HostFsDevice::open(orbis::Ref *file, if (hostFd < 0) { auto error = convertErrno(); - ORBIS_LOG_ERROR("host_open failed", realPath, error); + ORBIS_LOG_ERROR("host_open failed", path, realPath, error); return error; } @@ -366,6 +461,22 @@ orbis::ErrorCode HostFsDevice::open(orbis::Ref *file, return {}; } +orbis::ErrorCode HostFsDevice::mkdir(const char *path, int mode, orbis::Thread *thread) { + std::error_code ec; + std::filesystem::create_directories(hostPath + "/" + path, ec); + return convertErrorCode(ec); +} +orbis::ErrorCode HostFsDevice::rmdir(const char *path, orbis::Thread *thread) { + std::error_code ec; + std::filesystem::remove(hostPath + "/" + path, ec); + return convertErrorCode(ec); +} +orbis::ErrorCode HostFsDevice::rename(const char *from, const char *to, orbis::Thread *thread) { + std::error_code ec; + std::filesystem::rename(hostPath + "/" + from, hostPath + "/" + to, ec); + return convertErrorCode(ec); +} + orbis::File *createHostFile(int hostFd, orbis::Ref device) { auto newFile = orbis::knew(); newFile->hostFd = hostFd; diff --git a/rpcsx-os/io-device.hpp b/rpcsx-os/io-device.hpp index 2b118f61b..4f877c21a 100644 --- a/rpcsx-os/io-device.hpp +++ b/rpcsx-os/io-device.hpp @@ -30,6 +30,15 @@ struct IoDevice : orbis::RcBase { virtual orbis::ErrorCode unlink(const char *path, orbis::Thread *thread) { return orbis::ErrorCode::NOTSUP; } + virtual orbis::ErrorCode mkdir(const char *path, int mode, orbis::Thread *thread) { + return orbis::ErrorCode::NOTSUP; + } + virtual orbis::ErrorCode rmdir(const char *path, orbis::Thread *thread) { + return orbis::ErrorCode::NOTSUP; + } + virtual orbis::ErrorCode rename(const char *from, const char *to, orbis::Thread *thread) { + return orbis::ErrorCode::NOTSUP; + } }; IoDevice *createHostIoDevice(orbis::kstring hostPath); diff --git a/rpcsx-os/main.cpp b/rpcsx-os/main.cpp index e1992f462..1d5b40549 100644 --- a/rpcsx-os/main.cpp +++ b/rpcsx-os/main.cpp @@ -322,30 +322,30 @@ static int ps4Exec(orbis::Thread *mainThread, auto dmem1 = createDmemCharacterDevice(1); orbis::g_context.dmemDevice = dmem1; - rx::vfs::mount("/dev/dmem0", createDmemCharacterDevice(0)); - rx::vfs::mount("/dev/npdrm", createNpdrmCharacterDevice()); - rx::vfs::mount("/dev/icc_configuration", createIccConfigurationCharacterDevice()); - rx::vfs::mount("/dev/console", createConsoleCharacterDevice()); - rx::vfs::mount("/dev/camera", createCameraCharacterDevice()); - rx::vfs::mount("/dev/dmem1", dmem1); - rx::vfs::mount("/dev/dmem2", createDmemCharacterDevice(2)); - rx::vfs::mount("/dev/stdout", createFdWrapDevice(STDOUT_FILENO)); - rx::vfs::mount("/dev/stderr", createFdWrapDevice(STDERR_FILENO)); - rx::vfs::mount("/dev/stdin", createFdWrapDevice(STDIN_FILENO)); - rx::vfs::mount("/dev/zero", createZeroCharacterDevice()); - rx::vfs::mount("/dev/null", createNullCharacterDevice()); - rx::vfs::mount("/dev/dipsw", createDipswCharacterDevice()); - rx::vfs::mount("/dev/dce", createDceCharacterDevice()); - rx::vfs::mount("/dev/hmd_cmd", createHmdCmdCharacterDevice()); - rx::vfs::mount("/dev/hmd_snsr", createHmdSnsrCharacterDevice()); - rx::vfs::mount("/dev/hmd_3da", createHmd3daCharacterDevice()); - rx::vfs::mount("/dev/hmd_dist", createHmdMmapCharacterDevice()); - rx::vfs::mount("/dev/hid", createHidCharacterDevice()); - rx::vfs::mount("/dev/gc", createGcCharacterDevice()); - rx::vfs::mount("/dev/rng", createRngCharacterDevice()); - rx::vfs::mount("/dev/sbl_srv", createSblSrvCharacterDevice()); - rx::vfs::mount("/dev/ajm", createAjmCharacterDevice()); - rx::vfs::mount("/dev/urandom", createUrandomCharacterDevice()); + rx::vfs::addDevice("dmem0", createDmemCharacterDevice(0)); + rx::vfs::addDevice("npdrm", createNpdrmCharacterDevice()); + rx::vfs::addDevice("icc_configuration", createIccConfigurationCharacterDevice()); + rx::vfs::addDevice("console", createConsoleCharacterDevice()); + rx::vfs::addDevice("camera", createCameraCharacterDevice()); + rx::vfs::addDevice("dmem1", dmem1); + rx::vfs::addDevice("dmem2", createDmemCharacterDevice(2)); + rx::vfs::addDevice("stdout", createFdWrapDevice(STDOUT_FILENO)); + rx::vfs::addDevice("stderr", createFdWrapDevice(STDERR_FILENO)); + rx::vfs::addDevice("stdin", createFdWrapDevice(STDIN_FILENO)); + rx::vfs::addDevice("zero", createZeroCharacterDevice()); + rx::vfs::addDevice("null", createNullCharacterDevice()); + rx::vfs::addDevice("dipsw", createDipswCharacterDevice()); + rx::vfs::addDevice("dce", createDceCharacterDevice()); + rx::vfs::addDevice("hmd_cmd", createHmdCmdCharacterDevice()); + rx::vfs::addDevice("hmd_snsr", createHmdSnsrCharacterDevice()); + rx::vfs::addDevice("hmd_3da", createHmd3daCharacterDevice()); + rx::vfs::addDevice("hmd_dist", createHmdMmapCharacterDevice()); + rx::vfs::addDevice("hid", createHidCharacterDevice()); + rx::vfs::addDevice("gc", createGcCharacterDevice()); + rx::vfs::addDevice("rng", createRngCharacterDevice()); + rx::vfs::addDevice("sbl_srv", createSblSrvCharacterDevice()); + rx::vfs::addDevice("ajm", createAjmCharacterDevice()); + rx::vfs::addDevice("urandom", createUrandomCharacterDevice()); orbis::Ref stdinFile; orbis::Ref stdoutFile; diff --git a/rpcsx-os/ops.cpp b/rpcsx-os/ops.cpp index ef468de3a..77837a870 100644 --- a/rpcsx-os/ops.cpp +++ b/rpcsx-os/ops.cpp @@ -274,6 +274,19 @@ orbis::SysResult shm_open(orbis::Thread *thread, const char *path, return dev->open(file, path, flags, mode, thread); } +orbis::SysResult mkdir(Thread *thread, ptr path, sint mode) { + ORBIS_LOG_TODO(__FUNCTION__, path, mode); + return rx::vfs::mkdir(path, mode, thread); +} +orbis::SysResult rmdir(Thread *thread, ptr path) { + ORBIS_LOG_TODO(__FUNCTION__, path); + return rx::vfs::rmdir(path, thread); +} +orbis::SysResult rename(Thread *thread, ptr from, ptr to) { + ORBIS_LOG_TODO(__FUNCTION__, from, to); + return rx::vfs::rename(from, to, thread); +} + orbis::SysResult blockpool_open(orbis::Thread *thread, orbis::Ref *file) { auto dev = static_cast(orbis::g_context.blockpoolDevice.get()); @@ -633,6 +646,9 @@ ProcessOps rx::procOpsTable = { .query_memory_protection = query_memory_protection, .open = open, .shm_open = shm_open, + .mkdir = mkdir, + .rmdir = rmdir, + .rename = rename, .blockpool_open = blockpool_open, .blockpool_map = blockpool_map, .blockpool_unmap = blockpool_unmap, diff --git a/rpcsx-os/vfs.cpp b/rpcsx-os/vfs.cpp index 5ffa33851..db6ce7d68 100644 --- a/rpcsx-os/vfs.cpp +++ b/rpcsx-os/vfs.cpp @@ -6,17 +6,80 @@ #include "orbis/error/SysResult.hpp" #include #include +#include #include -static std::map> sMountsMap; +static std::map, std::greater<>> sMountsMap; -void rx::vfs::initialize() {} -void rx::vfs::deinitialize() { sMountsMap.clear(); } +struct DevFs : IoDevice { + std::map> devices; + + orbis::ErrorCode open(orbis::Ref *file, const char *path, + std::uint32_t flags, std::uint32_t mode, + orbis::Thread *thread) override { + if (auto it = devices.find(path); it != devices.end()) { + return it->second->open(file, path, flags, mode, thread); + } + + std::fprintf(stderr, "device %s not exists\n", path); + return orbis::ErrorCode::NOENT; + } +}; +static orbis::Ref sDevFs; + +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; + } +}; + +void rx::vfs::initialize() { + sDevFs = orbis::knew(); + sMountsMap.emplace("/dev/", sDevFs); + sMountsMap.emplace("/proc/", orbis::knew()); +} + +void rx::vfs::deinitialize() { + sDevFs = nullptr; + sMountsMap.clear(); +} + +void rx::vfs::addDevice(std::string name, IoDevice *device) { + sDevFs->devices[std::move(name)] = device; +} + +static std::pair, std::string> +get(const std::filesystem::path &guestPath) { + std::string normalPath = std::filesystem::path(guestPath).lexically_normal(); + std::string_view path = normalPath; + orbis::Ref device; + std::string_view prefix; + + for (auto &mount : sMountsMap) { + if (!path.starts_with(mount.first)) { + continue; + } + + device = mount.second; + path.remove_prefix(mount.first.length()); + return {mount.second, std::string(path)}; + } + + return {}; +} orbis::SysResult rx::vfs::mount(const std::filesystem::path &guestPath, IoDevice *dev) { - auto [it, inserted] = - sMountsMap.emplace(guestPath.lexically_normal().string(), dev); + auto mp = guestPath.lexically_normal().string(); + if (!mp.ends_with("/")) { + mp += "/"; + } + + auto [it, inserted] = sMountsMap.emplace(std::move(mp), dev); if (!inserted) { return orbis::ErrorCode::EXIST; @@ -28,37 +91,47 @@ orbis::SysResult rx::vfs::mount(const std::filesystem::path &guestPath, orbis::SysResult rx::vfs::open(std::string_view path, int flags, int mode, orbis::Ref *file, orbis::Thread *thread) { - orbis::Ref device; - bool isCharacterDevice = path.starts_with("/dev/"); - - for (auto &mount : sMountsMap) { - if (!path.starts_with(mount.first)) { - continue; - } - - path.remove_prefix(mount.first.length()); - device = mount.second; - break; + auto [device, devPath] = get(path); + if (device == nullptr) { + return orbis::ErrorCode::NOENT; } - - if (isCharacterDevice && device != nullptr) { - if (!path.empty()) { - std::fprintf(stderr, - "vfs::open: access to character device subentry '%s' (%s)\n", - path.data(), std::string(path).c_str()); - return orbis::ErrorCode::NOENT; - } - } - - if (device != nullptr) { - return (orbis::ErrorCode)device->open(file, std::string(path).c_str(), - flags, mode, thread); - } - - if (isCharacterDevice) { - std::fprintf(stderr, "vfs::open: character device '%s' not found.\n", - std::string(path).c_str()); - } - - return orbis::ErrorCode::NOENT; + return device->open(file, devPath.c_str(), flags, mode, thread); +} + +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); } diff --git a/rpcsx-os/vfs.hpp b/rpcsx-os/vfs.hpp index b326027f2..6ba69a805 100644 --- a/rpcsx-os/vfs.hpp +++ b/rpcsx-os/vfs.hpp @@ -10,7 +10,11 @@ struct IoDevice; namespace rx::vfs { void initialize(); void deinitialize(); +void addDevice(std::string name, IoDevice *device); orbis::SysResult mount(const std::filesystem::path &guestPath, IoDevice *dev); orbis::SysResult open(std::string_view path, int flags, int mode, orbis::Ref *file, orbis::Thread *thread); +orbis::SysResult mkdir(std::string_view path, int mode, orbis::Thread *thread); +orbis::SysResult rmdir(std::string_view path, orbis::Thread *thread); +orbis::SysResult rename(std::string_view from, std::string_view to, orbis::Thread *thread); } // namespace rx::vfs