Skip to content
Closed
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
8 changes: 8 additions & 0 deletions core/libs/prx/libc/include/FileStream.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@ class FileStream {
return _handle;
}

bool IsStandard() const {
return _handle == stdin || _handle == stdout || _handle == stderr;
}

bool IsClosed() const {
return _handle == nullptr;
}

bool IsDynamic() const {
return _dynamic;
}
Expand Down
11 changes: 9 additions & 2 deletions core/libs/prx/libc/src/FileAndHeap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -239,9 +239,16 @@ FileStream* APS5_VABI fdopen_nid_postfix(int descriptor, const char* mode) {

FileStream* APS5_VABI freopen_nid_postfix(const char* filename, const char* mode, FileStream* stream) {
if (!stream || !mode) { errno = 22; return nullptr; }
if (!filename) { errno = 45; return nullptr; } // Mode-only reopening is not supported.
const auto parsed = ParseFileMode(mode);
if (!parsed) { errno = 22; return nullptr; }
if (!parsed) {
if (!stream->IsClosed() && !stream->IsStandard()) {
stream->Close();
if (stream->IsDynamic()) delete stream;
}
errno = 22;
return nullptr;
}
if (!filename) { errno = 45; return nullptr; }
try {
std::filesystem::path path;
if (*filename) {
Expand Down
24 changes: 24 additions & 0 deletions core/libs/tests/GuestStandardStreams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,30 @@ int main() {
Require(freopen_nid_postfix(filename.c_str(), "rb", &failed) == nullptr);
Require(*__error_nid_postfix() == 2);
Require(failed.GuestState().flags == 0 && failed.GuestState().descriptor == -1);
FileStream invalidMode(std::tmpfile());
Require(freopen_nid_postfix(filename.c_str(), "zz", &invalidMode) == nullptr && *__error_nid_postfix() == 22);
Require(invalidMode.GuestState().flags == 0 && invalidMode.GuestState().descriptor == -1);
FileStream closedInvalidMode(std::tmpfile());
closedInvalidMode.Close();
Require(freopen_nid_postfix(filename.c_str(), "zz", &closedInvalidMode) == nullptr && *__error_nid_postfix() == 22);
for (FileStream* standard : {&_Stdout_nid_postfix, &_Stderr_nid_postfix}) {
Require(freopen_nid_postfix(filename.c_str(), "zz", standard) == nullptr && *__error_nid_postfix() == 22);
Require(standard->GetHandle() != nullptr && fflush_nid_postfix(standard) == 0 && ferror_nid_postfix(standard) == 0);
}
Require(freopen_nid_postfix(filename.c_str(), "zz", __stdinp_nid_postfix) == nullptr && *__error_nid_postfix() == 22);
Require(__stdinp_nid_postfix->GetHandle() == stdin);
{
std::ofstream(filename, std::ios::binary).put('x');
for (int index = 0; index < 10000; ++index) {
FileStream* opened = fopen_nid_postfix(filename.c_str(), "rb");
Require(opened != nullptr);
Require(freopen_nid_postfix(filename.c_str(), "zz", opened) == nullptr && *__error_nid_postfix() == 22);
}
Require(std::filesystem::remove(filename));
}
FileStream invalidModeWithoutPath(std::tmpfile());
Require(freopen_nid_postfix(nullptr, "zz", &invalidModeWithoutPath) == nullptr && *__error_nid_postfix() == 22);
Require(invalidModeWithoutPath.GuestState().flags == 0 && invalidModeWithoutPath.GuestState().descriptor == -1);
FileStream locked(std::tmpfile());
flockfile_nid_postfix(&locked);
flockfile_nid_postfix(&locked);
Expand Down
1 change: 1 addition & 0 deletions docs/dev/debt/libc.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
- 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.
- [getifaddrs](../../../core/libs/prx/libc/src/Interfaces.cpp) (libc) lists the host's IPv4 and IPv6 addresses in FreeBSD's `ifaddrs` and sockaddr layouts. FreeBSD's per-interface AF_LINK entries (MAC, index, MTU) and their `if_data` are not produced, so interfaces without an address are omitted. Names are the host's (Linux `lo`, `eth0`; Windows NDIS names such as `ethernet_32769`, or `if<index>` when one does not fit 15 bytes), not the console's. A Linux point-to-point address without a peer loses `IFF_POINTOPOINT`, and an IPv4 broadcast address the host omits is computed from the netmask. On Windows the flags are only `IFF_UP`, `IFF_RUNNING`, `IFF_LOOPBACK`, `IFF_BROADCAST` and `IFF_MULTICAST`, derived from the adapter: PPP and tunnel adapters get neither `IFF_BROADCAST` nor `IFF_POINTOPOINT` because their peer address is not reported, and an address whose prefix length the host reports as unspecified gets a host mask.
- [gethostname](../../../core/libs/prx/libc/src/Hostname.cpp) (libc) reports the fixed guest name `AnyPS5` without querying the host machine name. Buffer copying, null queries and `ENAMETOOLONG` follow [FreeBSD gethostname](https://github.com/freebsd/freebsd-src/blob/main/lib/libc/gen/gethostname.c); the console's configurable hostname and its integration with `sysctl` and name resolution are unverified and not implemented.
- [freopen](../../../core/libs/prx/libc/src/FileAndHeap.cpp) (libc) does not reopen a stream with a null path to change its mode: it fails with ENOTSUP and leaves the stream as it was, where FreeBSD reopens the same file with the new mode. With an invalid mode, `freopen` closes a stream the guest opened but leaves stdin, stdout and stderr open and usable and fails with EINVAL, where FreeBSD closes them too
- [wcsrtombs_s](../../../core/libs/prx/libc/src/Multibyte.cpp) (libc) follows the console's `libc.prx`, but a runtime-constraint violation only returns its error: the default handler there writes the message and a newline to `stderr` before returning, which is not done here. Conversion uses the C locale of `wcrtomb` (`MB_CUR_MAX` 1), so the path where a multibyte character does not fit the remaining space is not reached
- [The narrow printf family](../../../core/libs/prx/libc/src/Formatting.cpp) (libc) converts 16-bit guest wide arguments to UTF-8 on both platforms, independently of the host locale. It does not use the guest's single-byte `wcrtomb`; the console's treatment of invalid UTF-16 and locale-specific encoding remains unverified. Linux retains host formatting for formats without wide arguments or `%m`, so a narrow `%p` and a NaN there still follow the host (glibc prints a null pointer as `(nil)`, where FreeBSD prints `0x0`, and a NaN with its sign, `-nan`, and with the `+` and space flags, where FreeBSD prints `nan`); argument positions above 99 (FreeBSD's `NL_ARGMAX`) throw when a format requires the guest formatter
- [collate<char>::_Getcat](../../../core/libs/prx/libc/src/LocaleSupport.cpp) (libc) builds the facet for the C locale only and throws for any other locale name. The console's `libc.prx` stores pointers to its collation state tables at offsets 0x10 and 0x18 of the facet; ours leaves them null, as only the facet's own functions read them. `do_transform` throws for input with embedded null characters, which the console drops depending on the output buffer size. Its result capacity (`length | 15`) and the over-aligned storage of results of 4080 characters or more follow the console's `libc.prx` code and are not checked on hardware.
Expand Down
Loading