fix(libc): append through a descriptor opened by fdopen in an append mode on Windows
This commit is contained in:
parent
0142215687
commit
dbbd6d582c
|
|
@ -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(), ©, 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());
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
Loading…
Reference in New Issue