Skip to content

Commit

Permalink
fs: Actually functional linux case insensitive search
Browse files Browse the repository at this point in the history
  • Loading branch information
raphaelthegreat committed Jul 15, 2024
1 parent 26f8fbf commit dbeed80
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 101 deletions.
4 changes: 2 additions & 2 deletions src/common/io_file.h
Original file line number Diff line number Diff line change
Expand Up @@ -205,9 +205,9 @@ class IOFile final {
return WriteSpan(string);
}

static void WriteBytes(const std::filesystem::path path, std::span<u8> vec) {
static void WriteBytes(const std::filesystem::path path, std::span<const u8> data) {
IOFile out(path, FileAccessMode::Write);
out.Write(vec);
out.Write(data);
}

private:
Expand Down
103 changes: 62 additions & 41 deletions src/core/file_sys/fs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}

Expand All @@ -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};
// 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;
}

// 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();
}

for (auto& pair : m_mnt_pairs) {
// horrible code but it works :D
int find = guest_file.find(pair.guest_path);
if (find != 0) {
// 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;
}
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();
}
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;
}
#endif
return host_path;
}
return "";

// The path was found.
return current_path;
}

int HandleTable::CreateHandle() {
Expand Down
33 changes: 25 additions & 8 deletions src/core/file_sys/fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,45 @@
#include <mutex>
#include <string>
#include <vector>
#include <tsl/robin_map.h>
#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<MntPair> m_mnt_pairs;
std::vector<std::filesystem::path> path_parts;
tsl::robin_map<std::filesystem::path, std::filesystem::path> path_cache;
std::mutex m_mutex;
};

Expand Down
13 changes: 6 additions & 7 deletions src/core/libraries/kernel/file_system.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -165,8 +165,7 @@ int PS4_SYSV_ABI sceKernelUnlink(const char* path) {
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();

std::string host_path = mnt->GetHostFile(path);

const auto host_path = mnt->GetHostPath(path);
if (host_path.empty()) {
return SCE_KERNEL_ERROR_EACCES;
}
Expand Down Expand Up @@ -250,7 +249,7 @@ int PS4_SYSV_ABI sceKernelMkdir(const char* path, u16 mode) {
return SCE_KERNEL_ERROR_EINVAL;
}
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::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;
}
Expand Down Expand Up @@ -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<Core::FileSys::MntPoints>::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);
Expand Down Expand Up @@ -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<Core::FileSys::MntPoints>::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;
}
Expand Down
2 changes: 1 addition & 1 deletion src/core/libraries/kernel/libkernel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ s32 PS4_SYSV_ABI sceKernelLoadStartModule(const char* moduleFileName, size_t arg
}

auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::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<Core::Linker>::Instance();
Expand Down
9 changes: 5 additions & 4 deletions src/core/libraries/libc/libc_stdio.cpp
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -10,11 +10,12 @@ namespace Libraries::LibC {

std::FILE* PS4_SYSV_ABI ps4_fopen(const char* filename, const char* mode) {
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::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;
}
Expand Down
Loading

0 comments on commit dbeed80

Please sign in to comment.