AnyPS5/core/libs/prx/libc/src/RealPath.cpp

146 lines
4.7 KiB
C++

#include "prx/libc/include/ApplicationHeap.hpp"
#include "prx/libc/include/General.hpp"
#include "prx/libc/include/general/VabiMacros.hpp"
#include <cerrno>
#include <cstddef>
#include <cstring>
#include <filesystem>
#include <new>
#include <string>
#include <string_view>
#include <system_error>
extern "C" char* APS5_VABI getcwd_nid_postfix(char* buffer, std::size_t size);
namespace {
constexpr std::size_t PathMax = 1024;
constexpr std::string_view Separators = "/\\";
constexpr int NoEntry = 2;
constexpr int InputOutput = 5;
constexpr int NoMemory = 12;
constexpr int PermissionDenied = 13;
constexpr int NotDirectory = 20;
constexpr int InvalidArgument = 22;
constexpr int TooManyLinks = 62;
constexpr int NameTooLong = 63;
int StatusError(const std::error_code& error) {
if (error == std::errc::no_such_file_or_directory) return NoEntry;
if (error == std::errc::not_a_directory) return NotDirectory;
if (error == std::errc::permission_denied) return PermissionDenied;
if (error == std::errc::filename_too_long) return NameTooLong;
if (error == std::errc::too_many_symbolic_link_levels) return TooManyLinks;
if (error == std::errc::not_enough_memory) return NoMemory;
return InputOutput;
}
void LeaveTrouble(char* resolved, const std::string& trouble) {
if (resolved == nullptr) return;
const auto length = trouble.size() < PathMax ? trouble.size() : PathMax - 1;
std::memcpy(resolved, trouble.data(), length);
resolved[length] = '\0';
}
void DropLastComponent(std::string& result) {
if (result.size() <= 1) return;
const auto slash = result.rfind('/');
result.erase(slash == 0 ? 1 : slash);
}
bool IsSeparator(char character) { return Separators.find(character) != std::string_view::npos; }
bool ResolveGuest(const char* path, std::string& result, int& error) {
std::string_view left(path);
if (IsSeparator(left.front())) {
result = "/";
left.remove_prefix(1);
} else {
char current[PathMax];
if (getcwd_nid_postfix(current, sizeof(current)) == nullptr) {
result = ".";
error = errno;
return false;
}
result = current;
}
if (left.size() >= PathMax || result.size() >= PathMax) {
error = NameTooLong;
return false;
}
while (!left.empty()) {
const auto separator = left.find_first_of(Separators);
const auto token = left.substr(0, separator);
const bool moreFollows = separator != std::string_view::npos;
left = moreFollows ? left.substr(separator + 1) : std::string_view();
if (result.size() > 1 && result.size() + 1 >= PathMax) {
error = NameTooLong;
return false;
}
if (token.empty() || token == ".") continue;
if (token == "..") {
DropLastComponent(result);
continue;
}
std::string candidate = result;
if (candidate.size() > 1) candidate += '/';
candidate += token;
result = candidate;
if (candidate.size() >= PathMax) {
error = NameTooLong;
return false;
}
std::error_code code;
const auto status = std::filesystem::status(ResolvePath_nid_no_patch(candidate.c_str()), code);
if (code && code != std::errc::no_such_file_or_directory && code != std::errc::not_a_directory) {
error = StatusError(code);
return false;
}
if (!std::filesystem::exists(status)) {
error = NoEntry;
return false;
}
if (moreFollows && !std::filesystem::is_directory(status)) {
error = NotDirectory;
return false;
}
}
return true;
}
}
extern "C" char* APS5_VABI realpath_nid_postfix(const char* path, char* resolved) {
if (path == nullptr) {
errno = InvalidArgument;
return nullptr;
}
if (*path == '\0') {
errno = NoEntry;
return nullptr;
}
std::string result;
int error = 0;
try {
if (!ResolveGuest(path, result, error)) {
LeaveTrouble(resolved, result);
errno = error;
return nullptr;
}
if (resolved == nullptr) {
resolved = static_cast<char*>(ApplicationHeapAllocate_nid_no_patch(PathMax));
if (resolved == nullptr) {
errno = NoMemory;
return nullptr;
}
}
std::memcpy(resolved, result.c_str(), result.size() + 1);
return resolved;
} catch (const std::bad_alloc&) {
errno = NoMemory;
return nullptr;
} catch (const std::filesystem::filesystem_error& failure) {
LeaveTrouble(resolved, result);
errno = StatusError(failure.code());
return nullptr;
}
}