fix(libc): append through a descriptor opened by fdopen in an append mode on Windows

This commit is contained in:
David 2026-10-12 22:46:41 +04:00 committed by milescoviello
parent 0142215687
commit dbbd6d582c
2 changed files with 52 additions and 0 deletions

View File

@ -87,6 +87,31 @@ static std::FILE* OpenNative(const std::filesystem::path& path, const std::strin
#endif
}
#ifdef _WIN32
static bool AppendThroughDescriptor(int descriptor) {
const auto handle = reinterpret_cast<HANDLE>(::_get_osfhandle(descriptor));
if (handle == INVALID_HANDLE_VALUE) return false;
if (descriptor <= 2 || GetFileType(handle) != FILE_TYPE_DISK) return true;
const int translation = ::_setmode(descriptor, _O_BINARY);
if (translation == -1 || ::_setmode(descriptor, translation) == -1) return false;
HANDLE copy = nullptr;
if (!DuplicateHandle(GetCurrentProcess(), handle, GetCurrentProcess(), &copy, 0, TRUE, DUPLICATE_SAME_ACCESS)) {
errno = EBADF;
return false;
}
const int appending = ::_open_osfhandle(reinterpret_cast<std::intptr_t>(copy), _O_APPEND | translation);
if (appending < 0) {
CloseHandle(copy);
return false;
}
const int moved = ::_dup2(appending, descriptor);
const int error = errno;
::_close(appending);
errno = error;
return moved == 0;
}
#endif
using GuestCompare = int (APS5_VABI *)(const void*, const void*);
static void SwapElements(unsigned char* left, unsigned char* right, size_t size) {
@ -196,6 +221,7 @@ FileStream* APS5_VABI fdopen_nid_postfix(int descriptor, const char* mode) {
const auto release = [](void* pointer) { ::operator delete(pointer); };
std::unique_ptr<void, decltype(release)> storage(::operator new(sizeof(FileStream)), release);
#ifdef _WIN32
if (parsed->append && !AppendThroughDescriptor(descriptor)) return nullptr;
auto* native = ::_fdopen(descriptor, nativeMode.c_str());
#else
auto* native = ::fdopen(descriptor, nativeMode.c_str());

View File

@ -392,6 +392,31 @@ static void CheckDescriptorModes(std::FILE* original) {
#endif
}
static void CheckDescriptorAppend(std::FILE* original) {
std::string expected = "retained";
for (const char* mode : {"a", "ab", "a+", "ae"}) {
const int descriptor = DuplicateDescriptor(original);
#ifdef _WIN32
Require(descriptor >= 0 && _lseek(descriptor, 2, SEEK_SET) == 2);
#else
Require(descriptor >= 0 && ::lseek(descriptor, 2, SEEK_SET) == 2);
#endif
auto* wrapped = fdopen_nid_postfix(descriptor, mode);
Require(wrapped != nullptr && fileno_nid_postfix(wrapped) == descriptor);
Require(fputc_nid_postfix('+', wrapped) == '+' && fflush_nid_postfix(wrapped) == 0);
Require(fseek_nid_postfix(wrapped, 0, SEEK_SET) == 0 && fputc_nid_postfix('+', wrapped) == '+');
Require(fclose_nid_postfix(wrapped) == 0);
expected += "++";
const int reader = DuplicateDescriptor(original);
Require(reader >= 0);
auto* written = fdopen_nid_postfix(reader, "r");
Require(written != nullptr && fseek_nid_postfix(written, 0, SEEK_SET) == 0);
char contents[32]{};
Require(fgets_nid_postfix(contents, sizeof(contents), written) == contents && expected == contents);
Require(fclose_nid_postfix(written) == 0);
}
}
int main() {
Require(_Getmbcurmax_nid_postfix() == 1 && _Getmbcurmax_nid_postfix() == ___mb_cur_max_nid_postfix());
CheckFlushWhileReading();
@ -421,6 +446,7 @@ int main() {
Require(::close(descriptor) == -1);
#endif
CheckDescriptorModes(original);
CheckDescriptorAppend(original);
Require(std::fclose(original) == 0);
char stringOutput[256];
std::memset(stringOutput, '!', sizeof(stringOutput));