From dbeed80e3b6248703c20aa14fe39725f6ca1d404 Mon Sep 17 00:00:00 2001 From: IndecisiveTurtle <47210458+raphaelthegreat@users.noreply.github.com> Date: Mon, 15 Jul 2024 03:37:30 +0300 Subject: [PATCH] fs: Actually functional linux case insensitive search --- src/common/io_file.h | 4 +- src/core/file_sys/fs.cpp | 111 +++++++++++++--------- src/core/file_sys/fs.h | 33 +++++-- src/core/libraries/kernel/file_system.cpp | 13 ++- src/core/libraries/kernel/libkernel.cpp | 2 +- src/core/libraries/libc/libc_stdio.cpp | 9 +- src/core/libraries/save_data/savedata.cpp | 81 ++++++++-------- 7 files changed, 145 insertions(+), 108 deletions(-) diff --git a/src/common/io_file.h b/src/common/io_file.h index 7cb3246cd..2c3df3f69 100644 --- a/src/common/io_file.h +++ b/src/common/io_file.h @@ -205,9 +205,9 @@ public: return WriteSpan(string); } - static void WriteBytes(const std::filesystem::path path, std::span vec) { + static void WriteBytes(const std::filesystem::path path, std::span data) { IOFile out(path, FileAccessMode::Write); - out.Write(vec); + out.Write(data); } private: diff --git a/src/core/file_sys/fs.cpp b/src/core/file_sys/fs.cpp index e06a4b6ea..f009dc2d2 100644 --- a/src/core/file_sys/fs.cpp +++ b/src/core/file_sys/fs.cpp @@ -11,18 +11,12 @@ constexpr int RESERVED_HANDLES = 3; // First 3 handles are stdin,stdout,stderr void MntPoints::Mount(const std::filesystem::path& host_folder, const std::string& guest_folder) { std::scoped_lock lock{m_mutex}; - - MntPair pair; - pair.host_path = host_folder.string(); - std::replace(pair.host_path.begin(), pair.host_path.end(), '\\', '/'); - pair.guest_path = guest_folder; - - m_mnt_pairs.push_back(pair); + m_mnt_pairs.emplace_back(host_folder, guest_folder); } void MntPoints::Unmount(const std::filesystem::path& host_folder, const std::string& guest_folder) { auto it = std::remove_if(m_mnt_pairs.begin(), m_mnt_pairs.end(), - [&](const MntPair& pair) { return pair.guest_path == guest_folder; }); + [&](const MntPair& pair) { return pair.mount == guest_folder; }); m_mnt_pairs.erase(it, m_mnt_pairs.end()); } @@ -31,47 +25,74 @@ void MntPoints::UnmountAll() { m_mnt_pairs.clear(); } -std::string MntPoints::GetHostDirectory(const std::string& guest_directory) { - std::scoped_lock lock{m_mutex}; - for (auto& pair : m_mnt_pairs) { - // horrible code but it works :D - int find = guest_directory.find(pair.guest_path); - if (find == 0) { - std::string npath = - guest_directory.substr(pair.guest_path.size(), guest_directory.size() - 1); - std::replace(pair.host_path.begin(), pair.host_path.end(), '\\', '/'); - return pair.host_path + npath; - } +std::filesystem::path MntPoints::GetHostPath(const std::string& guest_directory) { + const MntPair* mount = GetMount(guest_directory); + if (!mount) { + return guest_directory; } - return ""; -} -std::string MntPoints::GetHostFile(const std::string& guest_file) { - std::scoped_lock lock{m_mutex}; - - for (auto& pair : m_mnt_pairs) { - // horrible code but it works :D - int find = guest_file.find(pair.guest_path); - if (find != 0) { - continue; - } - std::string npath = guest_file.substr(pair.guest_path.size(), guest_file.size() - 1); - const auto host_path = pair.host_path + npath; -#ifndef _WIN64 - const std::filesystem::path path{host_path}; - if (!std::filesystem::exists(path)) { - const auto filename = Common::ToLower(path.filename()); - for (const auto& file : std::filesystem::directory_iterator(path.parent_path())) { - const auto exist_filename = Common::ToLower(file.path().filename()); - if (filename == exist_filename) { - return file.path(); - } - } - } -#endif + // Remove device (e.g /app0) from path to retrieve relative path. + const u32 pos = mount->mount.size() + 1; + const auto rel_path = std::string_view(guest_directory).substr(pos); + const auto host_path = mount->host_path / rel_path; + if (!NeedsCaseInsensiveSearch || std::filesystem::exists(host_path)) { return host_path; } - return ""; + + // If the path does not exist attempt to verify this. + // Retrieve parent path until we find one that exists. + path_parts.clear(); + auto current_path = host_path; + while (!std::filesystem::exists(current_path)) { + // We have probably cached this if it's a folder. + if (auto it = path_cache.find(current_path); it != path_cache.end()) { + current_path = it->second; + break; + } + path_parts.emplace_back(current_path.filename()); + current_path = current_path.parent_path(); + } + + // We have found an anchor. Traverse parts we recoded and see if they + // exist in filesystem but in different case. + auto guest_path = current_path; + while (!path_parts.empty()) { + const auto& part = path_parts.back(); + const auto add_match = [&](const auto& host_part) { + current_path += host_part; + guest_path += part; + path_cache[guest_path] = current_path; + path_parts.pop_back(); + }; + + // Can happen when the mismatch is in upper folder. + if (std::filesystem::exists(current_path / part)) { + add_match(part); + continue; + } + const auto part_low = Common::ToLower(part.string()); + bool found_match = false; + for (const auto& path : std::filesystem::directory_iterator(current_path)) { + const auto candidate = path.path().filename(); + const auto filename = Common::ToLower(candidate); + // Check if a filename matches in case insensitive manner. + if (filename != part_low) { + continue; + } + // We found a match, record the actual path in the cache. + add_match(candidate); + found_match = true; + break; + } + if (!found_match) { + // Opening the guest path will surely fail but at least gives + // a better error message than the empty path. + return guest_directory; + } + } + + // The path was found. + return current_path; } int HandleTable::CreateHandle() { diff --git a/src/core/file_sys/fs.h b/src/core/file_sys/fs.h index b31931d1f..10fd10c44 100644 --- a/src/core/file_sys/fs.h +++ b/src/core/file_sys/fs.h @@ -7,28 +7,45 @@ #include #include #include +#include #include "common/io_file.h" namespace Core::FileSys { class MntPoints { +#ifdef _WIN64 + static constexpr bool NeedsCaseInsensiveSearch = false; +#else + static constexpr bool NeedsCaseInsensiveSearch = true; +#endif public: struct MntPair { - std::string host_path; - std::string guest_path; // e.g /app0/ + std::filesystem::path host_path; + std::string mount; // e.g /app0/ }; - MntPoints() = default; - virtual ~MntPoints() = default; + explicit MntPoints() = default; + ~MntPoints() = default; - void Mount(const std::filesystem::path& host_folder, const std::string& guest_folder); - void Unmount(const std::filesystem::path& host_folder, const std::string& guest_folder); + void Mount(const std::filesystem::path& host_folder, + const std::string& guest_folder); + void Unmount(const std::filesystem::path& host_folder, + const std::string& guest_folder); void UnmountAll(); - std::string GetHostDirectory(const std::string& guest_directory); - std::string GetHostFile(const std::string& guest_file); + + std::filesystem::path GetHostPath(const std::string& guest_directory); + + const MntPair* GetMount(const std::string& guest_path) { + const auto it = std::ranges::find_if(m_mnt_pairs, [&](const auto& mount) { + return guest_path.starts_with(mount.mount); + }); + return it == m_mnt_pairs.end() ? nullptr : &*it; + } private: std::vector m_mnt_pairs; + std::vector path_parts; + tsl::robin_map path_cache; std::mutex m_mutex; }; diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index eb6248d0c..be173320b 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -58,7 +58,7 @@ int PS4_SYSV_ABI sceKernelOpen(const char* path, int flags, u16 mode) { if (directory) { file->is_directory = true; file->m_guest_name = path; - file->m_host_name = mnt->GetHostDirectory(file->m_guest_name); + file->m_host_name = mnt->GetHostPath(file->m_guest_name); if (!std::filesystem::is_directory(file->m_host_name)) { // directory doesn't exist h->DeleteHandle(handle); return ORBIS_KERNEL_ERROR_ENOTDIR; @@ -72,7 +72,7 @@ int PS4_SYSV_ABI sceKernelOpen(const char* path, int flags, u16 mode) { } } else { file->m_guest_name = path; - file->m_host_name = mnt->GetHostFile(file->m_guest_name); + file->m_host_name = mnt->GetHostPath(file->m_guest_name); int e = 0; if (read) { e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Read); @@ -165,8 +165,7 @@ int PS4_SYSV_ABI sceKernelUnlink(const char* path) { auto* h = Common::Singleton::Instance(); auto* mnt = Common::Singleton::Instance(); - std::string host_path = mnt->GetHostFile(path); - + const auto host_path = mnt->GetHostPath(path); if (host_path.empty()) { return SCE_KERNEL_ERROR_EACCES; } @@ -250,7 +249,7 @@ int PS4_SYSV_ABI sceKernelMkdir(const char* path, u16 mode) { return SCE_KERNEL_ERROR_EINVAL; } auto* mnt = Common::Singleton::Instance(); - std::string dir_name = mnt->GetHostFile(path); + const auto dir_name = mnt->GetHostPath(path); if (std::filesystem::exists(dir_name)) { return SCE_KERNEL_ERROR_EEXIST; } @@ -279,7 +278,7 @@ int PS4_SYSV_ABI posix_mkdir(const char* path, u16 mode) { int PS4_SYSV_ABI sceKernelStat(const char* path, OrbisKernelStat* sb) { LOG_INFO(Kernel_Fs, "(PARTIAL) path = {}", path); auto* mnt = Common::Singleton::Instance(); - const auto& path_name = mnt->GetHostFile(path); + const auto path_name = mnt->GetHostPath(path); std::memset(sb, 0, sizeof(OrbisKernelStat)); const bool is_dir = std::filesystem::is_directory(path_name); const bool is_file = std::filesystem::is_regular_file(path_name); @@ -314,7 +313,7 @@ int PS4_SYSV_ABI posix_stat(const char* path, OrbisKernelStat* sb) { int PS4_SYSV_ABI sceKernelCheckReachability(const char* path) { auto* mnt = Common::Singleton::Instance(); - std::string path_name = mnt->GetHostFile(path); + const auto path_name = mnt->GetHostPath(path); if (!std::filesystem::exists(path_name)) { return SCE_KERNEL_ERROR_ENOENT; } diff --git a/src/core/libraries/kernel/libkernel.cpp b/src/core/libraries/kernel/libkernel.cpp index 9f57ff531..c9073029e 100644 --- a/src/core/libraries/kernel/libkernel.cpp +++ b/src/core/libraries/kernel/libkernel.cpp @@ -222,7 +222,7 @@ s32 PS4_SYSV_ABI sceKernelLoadStartModule(const char* moduleFileName, size_t arg } auto* mnt = Common::Singleton::Instance(); - const auto path = mnt->GetHostFile(moduleFileName); + const auto path = mnt->GetHostPath(moduleFileName); // Load PRX module and relocate any modules that import it. auto* linker = Common::Singleton::Instance(); diff --git a/src/core/libraries/libc/libc_stdio.cpp b/src/core/libraries/libc/libc_stdio.cpp index b1e946762..72337b83b 100644 --- a/src/core/libraries/libc/libc_stdio.cpp +++ b/src/core/libraries/libc/libc_stdio.cpp @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "common/assert.h" +#include "common/logging/log.h" #include "common/singleton.h" #include "core/file_sys/fs.h" #include "core/libraries/libc/libc_stdio.h" @@ -10,11 +10,12 @@ namespace Libraries::LibC { std::FILE* PS4_SYSV_ABI ps4_fopen(const char* filename, const char* mode) { auto* mnt = Common::Singleton::Instance(); - FILE* f = std::fopen(mnt->GetHostFile(filename).c_str(), mode); + const auto host_path = mnt->GetHostPath(filename); + FILE* f = std::fopen(host_path.c_str(), mode); if (f != nullptr) { - LOG_INFO(Lib_LibC, "fopen = {}", mnt->GetHostFile(filename).c_str()); + LOG_INFO(Lib_LibC, "fopen = {}", host_path.native()); } else { - LOG_INFO(Lib_LibC, "fopen can't open = {}", mnt->GetHostFile(filename).c_str()); + LOG_INFO(Lib_LibC, "fopen can't open = {}", host_path.native()); } return f; } diff --git a/src/core/libraries/save_data/savedata.cpp b/src/core/libraries/save_data/savedata.cpp index 7d12ed812..2a2855cc8 100644 --- a/src/core/libraries/save_data/savedata.cpp +++ b/src/core/libraries/save_data/savedata.cpp @@ -15,7 +15,8 @@ #include "error_codes.h" namespace Libraries::SaveData { -static std::string g_mount_point = "/savedata0"; // temp mount point (todo) + +static constexpr std::string_view g_mount_point = "/savedata0"; // temp mount point (todo) std::string game_serial; int PS4_SYSV_ABI sceSaveDataAbort() { @@ -50,11 +51,11 @@ int PS4_SYSV_ABI sceSaveDataChangeInternal() { int PS4_SYSV_ABI sceSaveDataCheckBackupData(const OrbisSaveDataCheckBackupData* check) { auto* mnt = Common::Singleton::Instance(); - std::string mount_dir = mnt->GetHostFile(check->dirName->data); + const auto mount_dir = mnt->GetHostPath(check->dirName->data); if (!std::filesystem::exists(mount_dir)) { return ORBIS_SAVE_DATA_ERROR_NOT_FOUND; } - LOG_INFO(Lib_SaveData, "called = {}", mount_dir); + LOG_INFO(Lib_SaveData, "called = {}", mount_dir.native()); return ORBIS_OK; } @@ -317,14 +318,14 @@ int PS4_SYSV_ABI sceSaveDataGetSaveDataMemory2(OrbisSaveDataMemoryGet2* getParam return false; } file.Seek(getParam->data->offset); - size_t nbytes = file.ReadRaw(getParam->data->buf, getParam->data->bufSize); + file.ReadRaw(getParam->data->buf, getParam->data->bufSize); LOG_INFO(Lib_SaveData, "called: bufSize = {}, offset = {}", getParam->data->bufSize, getParam->data->offset); } if (getParam->param != nullptr) { - Common::FS::IOFile file1(mount_dir / "param.txt", Common::FS::FileAccessMode::Read); - size_t nbytes = file1.ReadRaw(getParam->param, sizeof(OrbisSaveDataParam)); + Common::FS::IOFile file(mount_dir / "param.txt", Common::FS::FileAccessMode::Read); + file.ReadRaw(getParam->param, sizeof(OrbisSaveDataParam)); } return ORBIS_OK; @@ -394,13 +395,13 @@ int PS4_SYSV_ABI sceSaveDataIsMounted() { int PS4_SYSV_ABI sceSaveDataLoadIcon(const OrbisSaveDataMountPoint* mountPoint, OrbisSaveDataIcon* icon) { auto* mnt = Common::Singleton::Instance(); - std::string mount_dir = mnt->GetHostFile(mountPoint->data); - LOG_INFO(Lib_SaveData, "called: dir = {}", mount_dir); + const auto mount_dir = mnt->GetHostPath(mountPoint->data); + LOG_INFO(Lib_SaveData, "called: dir = {}", mount_dir.native()); if (icon != nullptr) { - Common::FS::IOFile file(mount_dir + "/save_data.png", Common::FS::FileAccessMode::Read); + Common::FS::IOFile file(mount_dir / "save_data.png", Common::FS::FileAccessMode::Read); icon->bufSize = file.GetSize(); - size_t nbytes = file.ReadRaw(icon->buf, icon->bufSize); + file.ReadRaw(icon->buf, icon->bufSize); } return ORBIS_OK; } @@ -409,6 +410,7 @@ s32 saveDataMount(u32 user_id, char* dir_name, u32 mount_mode, OrbisSaveDataMountResult* mount_result) { const auto& mount_dir = Common::FS::GetUserPath(Common::FS::PathType::SaveDataDir) / std::to_string(user_id) / game_serial / dir_name; + auto* mnt = Common::Singleton::Instance(); switch (mount_mode) { case ORBIS_SAVE_DATA_MOUNT_MODE_RDONLY: case ORBIS_SAVE_DATA_MOUNT_MODE_RDWR: @@ -417,9 +419,8 @@ s32 saveDataMount(u32 user_id, char* dir_name, u32 mount_mode, if (!std::filesystem::exists(mount_dir)) { return ORBIS_SAVE_DATA_ERROR_NOT_FOUND; } - auto* mnt = Common::Singleton::Instance(); mount_result->mount_status = 0; - std::strncpy(mount_result->mount_point.data, g_mount_point.c_str(), 16); + g_mount_point.copy(mount_result->mount_point.data, 16); mnt->Mount(mount_dir, mount_result->mount_point.data); } break; case ORBIS_SAVE_DATA_MOUNT_MODE_CREATE: @@ -431,16 +432,15 @@ s32 saveDataMount(u32 user_id, char* dir_name, u32 mount_mode, ORBIS_SAVE_DATA_MOUNT_MODE_COPY_ICON: case ORBIS_SAVE_DATA_MOUNT_MODE_CREATE | ORBIS_SAVE_DATA_MOUNT_MODE_DESTRUCT_OFF | ORBIS_SAVE_DATA_MOUNT_MODE_COPY_ICON: { - auto* mnt = Common::Singleton::Instance(); if (std::filesystem::exists(mount_dir)) { - std::strncpy(mount_result->mount_point.data, g_mount_point.c_str(), 16); + g_mount_point.copy(mount_result->mount_point.data, 16); mnt->Mount(mount_dir, mount_result->mount_point.data); mount_result->required_blocks = 0; mount_result->mount_status = 0; return ORBIS_SAVE_DATA_ERROR_EXISTS; } if (std::filesystem::create_directories(mount_dir)) { - std::strncpy(mount_result->mount_point.data, g_mount_point.c_str(), 16); + g_mount_point.copy(mount_result->mount_point.data, 16); mnt->Mount(mount_dir, mount_result->mount_point.data); mount_result->mount_status = 1; } @@ -451,8 +451,7 @@ s32 saveDataMount(u32 user_id, char* dir_name, u32 mount_mode, if (!std::filesystem::exists(mount_dir)) { std::filesystem::create_directories(mount_dir); } - auto* mnt = Common::Singleton::Instance(); - std::strncpy(mount_result->mount_point.data, g_mount_point.c_str(), 16); + g_mount_point.copy(mount_result->mount_point.data, 16); mnt->Mount(mount_dir, mount_result->mount_point.data); mount_result->mount_status = 1; } break; @@ -534,12 +533,12 @@ int PS4_SYSV_ABI sceSaveDataRestoreLoadSaveDataMemory() { int PS4_SYSV_ABI sceSaveDataSaveIcon(const OrbisSaveDataMountPoint* mountPoint, const OrbisSaveDataIcon* icon) { auto* mnt = Common::Singleton::Instance(); - std::string mount_dir = mnt->GetHostFile(mountPoint->data); - LOG_INFO(Lib_SaveData, "called = {}", mount_dir); + const auto mount_dir = mnt->GetHostPath(mountPoint->data); + LOG_INFO(Lib_SaveData, "called = {}", mount_dir.native()); - if (icon != nullptr) { - Common::FS::IOFile file(mount_dir + "/save_data.png", Common::FS::FileAccessMode::Write); - file.WriteRaw((void*)icon->buf, icon->bufSize); + if (icon != nullptr) { + Common::FS::IOFile file(mount_dir / "save_data.png", Common::FS::FileAccessMode::Write); + file.WriteRaw(icon->buf, icon->bufSize); } return ORBIS_OK; } @@ -558,12 +557,13 @@ int PS4_SYSV_ABI sceSaveDataSetParam(const OrbisSaveDataMountPoint* mountPoint, OrbisSaveDataParamType paramType, const void* paramBuf, size_t paramBufSize) { auto* mnt = Common::Singleton::Instance(); - std::string mount_dir = mnt->GetHostFile(mountPoint->data); - LOG_INFO(Lib_SaveData, "called = {}, mountPoint->data = {}", mount_dir, mountPoint->data); + const auto mount_dir = mnt->GetHostPath(mountPoint->data); + LOG_INFO(Lib_SaveData, "called = {}, mountPoint->data = {}", mount_dir.native(), + mountPoint->data); if (paramBuf != nullptr) { - Common::FS::IOFile file(mount_dir + "/param.txt", Common::FS::FileAccessMode::Write); - file.WriteRaw((void*)paramBuf, paramBufSize); + Common::FS::IOFile file(mount_dir / "param.txt", Common::FS::FileAccessMode::Write); + file.WriteRaw(paramBuf, paramBufSize); } return ORBIS_OK; } @@ -711,24 +711,23 @@ int PS4_SYSV_ABI sceSaveDataUmountSys() { int PS4_SYSV_ABI sceSaveDataUmountWithBackup(const OrbisSaveDataMountPoint* mountPoint) { LOG_ERROR(Lib_SaveData, "called = {}", std::string(mountPoint->data)); auto* mnt = Common::Singleton::Instance(); - std::string mount_dir = mnt->GetHostFile(mountPoint->data); + const auto mount_dir = mnt->GetHostPath(mountPoint->data); if (!std::filesystem::exists(mount_dir)) { return ORBIS_SAVE_DATA_ERROR_NOT_FOUND; - } else { - std::filesystem::path mnt_dir(mount_dir); - std::filesystem::create_directories(mnt_dir.parent_path() / "backup"); - - for (const auto& entry : std::filesystem::recursive_directory_iterator(mnt_dir)) { - const auto& path = entry.path(); - std::filesystem::path target_path = mnt_dir.parent_path() / "backup"; - - if (std::filesystem::is_regular_file(path)) { - std::filesystem::copy(path, target_path, - std::filesystem::copy_options::overwrite_existing); - } - } - mnt->Unmount(mount_dir, mountPoint->data); } + + std::filesystem::create_directories(mount_dir.parent_path() / "backup"); + + for (const auto& entry : std::filesystem::recursive_directory_iterator(mount_dir)) { + const auto& path = entry.path(); + const auto target_path = mount_dir.parent_path() / "backup"; + if (std::filesystem::is_regular_file(path)) { + std::filesystem::copy(path, target_path, + std::filesystem::copy_options::overwrite_existing); + } + } + + mnt->Unmount(mount_dir, mountPoint->data); return ORBIS_OK; }