Merge pull request #4372 from milescoviello/fix/fflush-read-stream

fix(libc): leave a stream that is reading untouched in fflush on Linux
This commit is contained in:
Dean 2026-10-13 07:22:17 +13:00 committed by GitHub
commit b701bc5d8d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 33 additions and 1 deletions

View File

@ -26,6 +26,10 @@
#include "prx/libc/include/FilesystemError.hpp"
#include "prx/libc/include/GuestArena.hpp"
#ifdef __linux__
#include <stdio_ext.h>
#endif
struct GuestFileMode {
std::string native;
bool writes = false;
@ -303,7 +307,11 @@ int APS5_VABI fputs_nid_postfix(const char* str, FileStream* stream) {
}
int APS5_VABI fflush_nid_postfix(FileStream* stream) {
if (std::fflush(stream ? GetNativeStream(stream) : nullptr) != 0) throw std::runtime_error("fflush: flush failed");
std::FILE* native = stream ? GetNativeStream(stream) : nullptr;
#ifdef __linux__
if (native && !__fwriting(native)) return 0;
#endif
if (std::fflush(native) != 0) throw std::runtime_error("fflush: flush failed");
return 0;
}

View File

@ -53,6 +53,7 @@ int APS5_VABI setvbuf_nid_postfix(FileStream*, char*, int, std::size_t);
void APS5_VABI setbuf_nid_postfix(FileStream*, char*);
FileStream* APS5_VABI fdopen_nid_postfix(int, const char*);
int APS5_VABI fclose_nid_postfix(FileStream*);
int APS5_VABI fflush_nid_postfix(FileStream*);
int APS5_VABI _Getmbcurmax_nid_postfix();
int APS5_VABI ___mb_cur_max_nid_postfix();
void APS5_VABI flockfile_nid_postfix(FileStream*);
@ -280,6 +281,28 @@ static void CheckRewind() {
#endif
}
static void CheckFlushWhileReading() {
const auto filename = "anyps5-flush-read-" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count());
{ std::ofstream seed(filename, std::ios::binary); seed << "abcdef\n"; Require(seed.good()); }
for (const char* mode : {"r", "r+"}) {
FileStream* stream = fopen_nid_postfix(filename.c_str(), mode);
Require(stream != nullptr);
Require(fgetc_nid_postfix(stream) == 'a' && ungetc_nid_postfix('Z', stream) == 'Z');
Require(fflush_nid_postfix(stream) == 0);
char rest[16]{};
const auto count = fread_nid_postfix(rest, 1, sizeof(rest) - 1, stream);
if (count != 7 || std::strcmp(rest, "Zbcdef\n") != 0) {
std::string shown;
for (std::size_t index = 0; index < count; ++index) shown += rest[index] == '\n' ? std::string("\\n") : std::string(1, rest[index]);
std::fprintf(stderr, "fflush of a \"%s\" stream that is reading changed the data read next: \"%s\" (%zu bytes), expected \"Zbcdef\\n\" (7 bytes)\n",
mode, shown.c_str(), count);
std::abort();
}
Require(fclose_nid_postfix(stream) == 0);
}
std::filesystem::remove(filename);
}
static bool CheckBinaryModes() {
const auto directory = "anyps5-byte-stream-" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count());
Require(std::filesystem::create_directory(directory));
@ -371,6 +394,7 @@ static void CheckDescriptorModes(std::FILE* original) {
int main() {
Require(_Getmbcurmax_nid_postfix() == 1 && _Getmbcurmax_nid_postfix() == ___mb_cur_max_nid_postfix());
CheckFlushWhileReading();
Require(fdopen_nid_postfix(-1, "rb") == nullptr && *__error_nid_postfix() == 9);
Require(fdopen_nid_postfix(0, nullptr) == nullptr && *__error_nid_postfix() == 22);
Require(fdopen_nid_postfix(0, "invalid") == nullptr && *__error_nid_postfix() == 22);