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
22 changes: 2 additions & 20 deletions core/libs/prx/libc/src/FileAndHeap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -274,29 +274,11 @@ FileStream* APS5_VABI fopen_nid_postfix(const char* filename, const char* mode)
}
std::filesystem::path fpath;
if (const int error = ResolvePathError_nid_no_patch(filename, fpath)) { errno = error; return nullptr; }
const auto abs_path = fpath.string();
std::unique_ptr<std::FILE, int (*)(std::FILE*)> handle(OpenNative(fpath, parsed->native), std::fclose);
if (!handle) {
const int error = errno;
if (error == ENOENT || (parsed->exclusive && error == EEXIST)) {
errno = error;
return nullptr;
}
const auto reason = std::strerror(error);
std::error_code ec;
std::filesystem::path sibling;
for (const auto& entry : std::filesystem::directory_iterator(fpath.parent_path(), ec)) {
if (ec) break;
if (entry.path().stem() == fpath.stem() && entry.path().extension() != fpath.extension()) {
sibling = std::filesystem::absolute(entry.path());
break;
}
}
if (!sibling.empty()) {
// APS5_LOG_OUT("%s: \"%s\": %s. Sibling: \"%s\"", FOPEN_MSG_NOT_FOUND, abs_path.c_str(), reason, sibling.filename().string().c_str());
return nullptr;
}
throw std::runtime_error(std::string(__func__) + ": " + FOPEN_MSG_OPEN_FAILED + ": \"" + abs_path + "\": " + reason);
errno = error;
return nullptr;
}
// APS5_LOG_OUT("success: \"%s\"", abs_path.c_str());
if (parsed->append) static_cast<void>(std::fseek(handle.get(), 0, SEEK_END));
Expand Down
13 changes: 13 additions & 0 deletions core/libs/tests/FileStreams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,19 @@ int main(int argc, char** argv) {
errno = 0;
Require(fopen_nid_postfix(argv[1], "rb") == nullptr);
Require(errno == ENOENT);
#ifdef _WIN32
const std::string directoryMode = "rb";
constexpr int directoryError = EACCES;
#else
const std::string directoryMode = "wb";
constexpr int directoryError = EISDIR;
#endif
const std::string directoryName = std::string(argv[1]) + ".dir";
Require(std::filesystem::create_directory(directoryName));
errno = 0;
Require(fopen_nid_postfix(directoryName.c_str(), directoryMode.c_str()) == nullptr);
Require(errno == directoryError);
Require(std::filesystem::remove(directoryName));
const std::u8string unicodeName = u8"セーブé.tmp";
const std::string guestName(unicodeName.begin(), unicodeName.end());
const std::filesystem::path hostName(unicodeName);
Expand Down
2 changes: 1 addition & 1 deletion docs/dev/debt/libc.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
- the handler is installed when libc loads and passes other faults to the handler installed before it. A handler installed later has to do the same.
- [sceKernelSync](../../../core/libs/prx/libc/src/General.cpp) (libkernel) flushes only the host files and directories recorded through `RecordWrittenPath_nid_no_patch` (the libc, libkernel, save data and app content file functions) and their changed parent directories. A host write in a library that does not record its path is not flushed by `sceKernelSync`
- [sceKernelSync](../../../core/libs/prx/libc/src/General.cpp) (libkernel) on Windows skips a recorded file it cannot open for writing (read-only, or held open by a handle that denies write sharing), since `FlushFileBuffers` needs a writable handle. On Linux, it skips a recorded file it cannot open for reading. Such a file's data may not reach the disk before a host crash; the guest reads the same data either way
- [`fopen` streams](../../../core/libs/prx/libc/src/FileAndHeap.cpp) (libc) on Windows share delete access, like the descriptors [`sceKernelOpen`](../../../core/libs/prx/libkernel/File/src/Open.cpp) opens, so a file the guest holds open can be removed or renamed. A stream reopened with `freopen` still goes through `_wfreopen`, which does not share delete access
- [`fopen` streams](../../../core/libs/prx/libc/src/FileAndHeap.cpp) (libc) on Windows share delete access, like the descriptors [`sceKernelOpen`](../../../core/libs/prx/libkernel/File/src/Open.cpp) opens, so a file the guest holds open can be removed or renamed. A stream reopened with `freopen` still goes through `_wfreopen`, which does not share delete access. On Windows, opening a directory in a read mode fails with `EACCES`, where FreeBSD opens it; directory streams are not emulated
- The [`_Unwind_Context`](../../../core/libs/prx/libc/include/exceptions/Unwind.hpp) that `_Unwind_Backtrace` passes to its callback starts with a table laid out like LLVM libunwind's `AbstractUnwindCursor` virtual table, because homebrew built with the ps5-payload-dev SDK links libunwind statically and reads the frames with its own `_Unwind_GetIP` (`__unw_get_reg`), which calls through that table. Only register reads and writes (`validReg`, `getReg`, `setReg`, with `UNW_REG_IP` and `UNW_REG_SP`), `validFloatReg` (always false) and `isSignalFrame` are answered; the other slots throw. The layout follows libunwind's `UnwindCursor.hpp`, not the console's own `libSceLibcInternal`, which is not checked
- The Windows libc [exception unwinder](../../../core/libs/prx/libc/src/exception/Unwind.cpp) steps through frames of modules without the project's `.ehmeta` and `.ehfram` tables (system DLLs, and WinLibs GCC 15.2's `libstdc++-6.dll` and `libwinpthread-1.dll`, whose `.eh_frame` sections are empty) with their PE unwind data through `RtlVirtualUnwind`, but does not run Windows exception or termination handlers: it stops at a frame whose unwind data has one, such as a C++ cleanup through `__gxx_personality_seh0` in `libstdc++-6.dll` or a system DLL function like `qsort` in `ucrtbase.dll` (Windows 10 22H2), and the exception is then reported as unhandled
- The libc [SSE4a trap emulation](../../../core/libs/prx/libc/src/specifics/windows/Sse4aEmulation.hpp) on Windows is superseded by `--to-intel` and remains only until the relinked title has been verified without it.
Expand Down
Loading