feat(libc): reopen guest streams while preserving identity

This commit is contained in:
SamG-Coder 2026-09-25 07:05:07 +10:00
parent 2920c7f04c
commit dd0debe967
3 changed files with 57 additions and 0 deletions

View File

@ -65,6 +65,20 @@ public:
}
GuestFilePrefix& GuestState() { return _guest; }
bool Reopen(const char* filename, const char* mode) {
auto* previous = GetHandle();
_guest = {};
_handle = std::freopen(filename, mode, previous);
if (!_handle) return false;
_guest.flags = 0x10;
#ifdef _WIN32
const int descriptor = _fileno(_handle);
#else
const int descriptor = ::fileno(_handle);
#endif
_guest.descriptor = descriptor >= 0 && descriptor <= 32767 ? static_cast<std::int16_t>(descriptor) : -1;
return true;
}
void SyncStatus() {
_guest.readRemaining = 0;
_guest.writeRemaining = 0;

View File

@ -16,6 +16,25 @@ extern "C" {
[[noreturn]] void APS5_VABI _ZSt11_Xbad_allocv_nid_postfix();
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 char* supported[] = {"r", "w", "a", "rb", "wb", "ab", "r+", "w+", "a+",
"rb+", "wb+", "ab+", "r+b", "w+b", "a+b"};
bool valid = false;
for (const auto* candidate : supported) if (std::strcmp(mode, candidate) == 0) valid = true;
if (!valid) { errno = 22; return nullptr; }
try {
const auto path = *filename ? ResolvePath_nid_no_patch(filename).string() : std::string{};
if (stream->Reopen(path.c_str(), mode)) return stream;
const int error = errno;
if (stream->IsDynamic()) delete stream;
errno = error;
return nullptr;
} catch (const std::bad_alloc&) { errno = 12; return nullptr; }
catch (const std::filesystem::filesystem_error&) { errno = 5; return nullptr; }
}
FileStream* APS5_VABI fopen_nid_postfix(const char* filename, const char* mode) {
if (!filename || !mode) throw std::runtime_error(std::string(__func__) + ": " + FOPEN_MSG_NULL_ARG);
const std::filesystem::path fpath = ResolvePath_nid_no_patch(filename);

View File

@ -4,7 +4,11 @@
#include <cstring>
#include <cstdarg>
#include <cstdint>
#include <filesystem>
#include <fstream>
#include <chrono>
extern "C" {
FileStream* APS5_VABI freopen_nid_postfix(const char*, const char*, FileStream*);
int APS5_VABI fseeko_nid_postfix(FileStream*, std::int64_t, int);
std::int64_t APS5_VABI ftello_nid_postfix(FileStream*);
int APS5_VABI fseek_nid_postfix(FileStream*, std::int64_t, int);
@ -102,4 +106,24 @@ int main() {
Require(fseeko_nid_postfix(&positioned, 0, SEEK_SET) == 0);
Require(!feof_nid_postfix(&positioned));
positioned.Close();
const auto filename = "anyps5-reopen-test-" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count());
FileStream redirected(std::tmpfile());
Require(fgetc_nid_postfix(&redirected) == EOF && feof_nid_postfix(&redirected));
Require(freopen_nid_postfix(filename.c_str(), "w+b", &redirected) == &redirected);
Require(!feof_nid_postfix(&redirected));
Require(fputc_nid_postfix('R', &redirected) == 'R');
Require(fseeko_nid_postfix(&redirected, 0, SEEK_SET) == 0);
Require(fgetc_nid_postfix(&redirected) == 'R');
Require(freopen_nid_postfix(filename.c_str(), "ab", &redirected) == &redirected);
Require(fputc_nid_postfix('S', &redirected) == 'S');
Require(freopen_nid_postfix(nullptr, "r", &redirected) == nullptr && *__error_nid_postfix() == 45);
redirected.Close();
{ std::ifstream input(filename, std::ios::binary); std::string contents; std::getline(input, contents);
Require(contents == "RS"); }
Require(std::filesystem::remove(filename));
FileStream failed(std::tmpfile());
Require(freopen_nid_postfix(filename.c_str(), "rb", &failed) == nullptr);
Require(*__error_nid_postfix() == 2);
Require(failed.GuestState().flags == 0 && failed.GuestState().descriptor == -1);
}