Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions core/libs/prx/libkernel/File/src/Open.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,17 @@ static int NativeUnlink(const std::filesystem::path& p) {
errno = error;
return -1;
}
static std::filesystem::path NativeMetadataPath(const std::filesystem::path& path, std::error_code& error) {
const auto& input = path.native();
if (input.starts_with(L"\\\\?\\") || input.starts_with(L"\\\\.\\")) return path;
auto absolute = std::filesystem::absolute(path, error);
if (error) return {};
absolute = absolute.lexically_normal().make_preferred();
const auto& native = absolute.native();
if (native.size() < MAX_PATH || native.starts_with(L"\\\\?\\") || native.starts_with(L"\\\\.\\")) return absolute;
return native.starts_with(L"\\\\") ? std::filesystem::path(L"\\\\?\\UNC\\" + native.substr(2))
: std::filesystem::path(L"\\\\?\\" + native);
}
static int MapFlags(int sceFlags) {
int f = 0;
const int acc = sceFlags & SCE_KERNEL_O_ACCMODE;
Expand Down Expand Up @@ -332,15 +343,20 @@ int APS5_VABI sceKernelStat(const char* path, FileStat* sb) {
*sb = archived;
return 0;
}
const auto status = std::filesystem::status(native, error);
auto metadataPath = native;
#ifdef _WIN32
metadataPath = NativeMetadataPath(native, error);
if (error) return SceErrorFromErrno(FilesystemError(error));
#endif
const auto status = std::filesystem::status(metadataPath, error);
if (error == std::errc::not_a_directory) return SceErrorFromErrno(GuestEnotdir);
if (!std::filesystem::exists(status)) {
return SceErrorFromErrno(2);
}
const std::string_view guestPath(path);
if (!guestPath.empty() && guestPath.back() == '/' && !std::filesystem::is_directory(status)) return SceErrorFromErrno(GuestEnotdir);
if (sb == nullptr) return SceErrorFromErrno(GuestEfault);
File::FillFileStat(native, sb);
File::FillFileStat(metadataPath, sb);
return 0;
}

Expand Down
52 changes: 52 additions & 0 deletions core/libs/tests/GuestFilesystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,55 @@ static void TestRelinkedModuleNameCase() {
std::filesystem::remove_all(root);
}

#ifdef _WIN32
static std::filesystem::path ExtendedPath(const std::filesystem::path& path) {
const auto& native = path.native();
if (native.starts_with(L"\\\\?\\") || native.starts_with(L"\\\\.\\")) return path;
return native.starts_with(L"\\\\") ? std::filesystem::path(L"\\\\?\\UNC\\" + native.substr(2))
: std::filesystem::path(L"\\\\?\\" + native);
}

static void TestLongHostPathStat() {
auto root = std::filesystem::absolute(std::filesystem::temp_directory_path()).lexically_normal();
if (root.filename().empty()) root = root.parent_path();
root /= L"anyps5-stat-long-" + std::to_wstring(std::chrono::steady_clock::now().time_since_epoch().count());
auto deep = root;
while (deep.native().size() < MAX_PATH + 24) deep /= std::wstring(40, L'd');
Require(::CreateDirectoryW(ExtendedPath(root).c_str(), nullptr) != 0);
auto directory = root;
for (const auto& component : std::filesystem::path(deep).lexically_relative(root)) {
directory /= component;
Require(::CreateDirectoryW(ExtendedPath(directory).c_str(), nullptr) != 0);
}
const auto file = deep / L"payload.bin";
const auto handle = ::CreateFileW(ExtendedPath(file).c_str(), GENERIC_WRITE, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
Require(handle != INVALID_HANDLE_VALUE);
DWORD written = 0;
const bool wrote = ::WriteFile(handle, "metadata", 8, &written, nullptr) != 0 && written == 8;
::CloseHandle(handle);
Require(wrote);
const auto host = deep.string();
AddPathAlias_nid_no_patch("/long-stat-host", host.c_str());
FileStat status{};
Require(ResolvePath_nid_no_patch("/long-stat-host/payload.bin").native().size() > MAX_PATH);
Require(sceKernelStat("/long-stat-host/payload.bin", &status) == 0 && status.st_size == 8);
Require(sceKernelStat("/long-stat-host/missing.bin", &status) == static_cast<int>(0x80020002u));
Require(sceKernelStat("/long-stat-host/", &status) == 0 && (status.st_mode & 0170000) == 0040000);
RemovePathAlias_nid_no_patch("/long-stat-host");
const auto extendedHost = ExtendedPath(deep).string();
AddPathAlias_nid_no_patch("/long-stat-extended", extendedHost.c_str());
Require(ResolvePath_nid_no_patch("/long-stat-extended/payload.bin").native().starts_with(L"\\\\?\\"));
Require(sceKernelStat("/long-stat-extended/payload.bin", &status) == 0 && status.st_size == 8);
Require(sceKernelStat("/long-stat-extended/missing.bin", &status) == static_cast<int>(0x80020002u));
RemovePathAlias_nid_no_patch("/long-stat-extended");
::DeleteFileW(ExtendedPath(file).c_str());
for (auto path = deep; ; path = path.parent_path()) {
::RemoveDirectoryW(ExtendedPath(path).c_str());
if (path == root) break;
}
}
#endif

#ifdef __linux__
static int DirectoryOpens(int watch) {
alignas(inotify_event) char buffer[4096];
Expand Down Expand Up @@ -290,6 +339,9 @@ int main() {
TestParentTraversal();
TestRelinkedModuleNames();
TestRelinkedModuleNameCase();
#ifdef _WIN32
TestLongHostPathStat();
#endif
#ifdef __linux__
TestMissingPathListsDirectoryOnce();
#endif
Expand Down
Loading