Merge pull request #4640 from MarcZX100/codex/win-host-standard-handles-4631
fix(libc): preserve host standard handles on Windows redirects
This commit is contained in:
commit
4fe3123627
|
|
@ -13,6 +13,8 @@
|
|||
#ifdef _WIN32
|
||||
#include <io.h>
|
||||
#include "prx/libc/include/FileLock.hpp"
|
||||
extern "C" void* HostStandardHandle_nid_no_patch(int descriptor);
|
||||
extern "C" void KeepStandardHandle_nid_no_patch(int descriptor, void* handle);
|
||||
#endif
|
||||
|
||||
struct GuestFilePrefix {
|
||||
|
|
@ -81,8 +83,11 @@ public:
|
|||
#ifdef _WIN32
|
||||
std::wstring wideMode;
|
||||
for (const char character : std::string_view(mode)) wideMode.push_back(static_cast<unsigned char>(character));
|
||||
GuestFileLockForget_nid_no_patch(::_fileno(previous));
|
||||
const int replaced = previous ? _fileno(previous) : -1;
|
||||
auto* const standard = HostStandardHandle_nid_no_patch(replaced);
|
||||
GuestFileLockForget_nid_no_patch(replaced);
|
||||
_handle = ::_wfreopen(filename.c_str(), wideMode.c_str(), previous);
|
||||
KeepStandardHandle_nid_no_patch(replaced, standard);
|
||||
#else
|
||||
if (::fileno(previous) == 2) KeepHostStandardError_nid_no_patch();
|
||||
_handle = std::freopen(filename.c_str(), mode, previous);
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@ extern "C" void SyncWrittenPaths_nid_no_patch();
|
|||
extern "C" void BlockPathAlias_nid_no_patch(const char* guestPrefix);
|
||||
#ifdef _WIN32
|
||||
extern "C" int WindowsOpenError_nid_no_patch(unsigned long error);
|
||||
extern "C" void* HostStandardHandle_nid_no_patch(int descriptor);
|
||||
extern "C" void KeepStandardHandle_nid_no_patch(int descriptor, void* handle);
|
||||
#endif
|
||||
|
||||
#define APS5_INVALID_ARG_EX throw std::invalid_argument(std::string(__func__) + ": invalid argument")
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <io.h>
|
||||
#else
|
||||
#include <ctime>
|
||||
#include <fcntl.h>
|
||||
|
|
@ -475,6 +476,28 @@ extern "C" int WindowsOpenError_nid_no_patch(unsigned long error) {
|
|||
return EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" _invalid_parameter_handler _set_thread_local_invalid_parameter_handler(_invalid_parameter_handler);
|
||||
static DWORD StandardHandleId(int descriptor) {
|
||||
return descriptor == 0 ? STD_INPUT_HANDLE : descriptor == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE;
|
||||
}
|
||||
extern "C" void* HostStandardHandle_nid_no_patch(int descriptor) {
|
||||
if (descriptor < 0 || descriptor > 2) return nullptr;
|
||||
const HANDLE current = GetStdHandle(StandardHandleId(descriptor));
|
||||
if (current == nullptr || current == INVALID_HANDLE_VALUE) return nullptr;
|
||||
const auto previous = _set_thread_local_invalid_parameter_handler([](const wchar_t*, const wchar_t*, const wchar_t*, unsigned int, std::uintptr_t) {});
|
||||
const auto owned = ::_get_osfhandle(descriptor);
|
||||
_set_thread_local_invalid_parameter_handler(previous);
|
||||
if (reinterpret_cast<HANDLE>(owned) != current) return current;
|
||||
HANDLE kept = nullptr;
|
||||
if (!DuplicateHandle(GetCurrentProcess(), current, GetCurrentProcess(), &kept, 0, TRUE, DUPLICATE_SAME_ACCESS))
|
||||
throw std::runtime_error("Cannot keep the host standard handle of descriptor " + std::to_string(descriptor));
|
||||
return kept;
|
||||
}
|
||||
extern "C" void KeepStandardHandle_nid_no_patch(int descriptor, void* handle) {
|
||||
if (handle != nullptr && !SetStdHandle(StandardHandleId(descriptor), handle))
|
||||
throw std::runtime_error("Cannot keep the host standard handle of descriptor " + std::to_string(descriptor));
|
||||
}
|
||||
#endif
|
||||
|
||||
extern "C" void NotImplemented_nid_no_patch(const char* funcName) {
|
||||
|
|
|
|||
|
|
@ -579,7 +579,10 @@ int APS5_VABI dup2_nid_postfix(int from, int to) {
|
|||
if (WithoutParameterHandler([from] { return ::_get_osfhandle(from) == -1 ? -1 : 0; }) != 0) return PosixFailure(GUEST_EBADF);
|
||||
if (from == to) return to;
|
||||
if (to == 2) PreserveHostStandardError();
|
||||
if (WithoutParameterHandler([from, to] { return ::_dup2(from, to); }) != 0) return PosixFailure(SceErrorFromErrno(errno) & 0xffff);
|
||||
auto* const standard = HostStandardHandle_nid_no_patch(to);
|
||||
const int duplicated = WithoutParameterHandler([from, to] { return ::_dup2(from, to); });
|
||||
KeepStandardHandle_nid_no_patch(to, standard);
|
||||
if (duplicated != 0) return PosixFailure(SceErrorFromErrno(errno) & 0xffff);
|
||||
EqueueDescriptorClosed(to);
|
||||
File::ForgetDirectoryDescriptor(to);
|
||||
File::DuplicateDirectoryDescriptor(from, to);
|
||||
|
|
|
|||
|
|
@ -278,6 +278,34 @@ int main() {
|
|||
std::rewind(hostCapture);
|
||||
Require(std::fread(debugText, 1, sizeof(debugText), hostCapture) == 15 && std::memcmp(debugText, "[debug:1] kept\n", 15) == 0);
|
||||
Require(std::fclose(hostCapture) == 0);
|
||||
#ifdef _WIN32
|
||||
for (const bool reopen : {false, true}) {
|
||||
auto* hostCapture = std::tmpfile();
|
||||
const int hostError = ::_dup(2);
|
||||
Require(hostCapture != nullptr && hostError >= 0);
|
||||
int redirect[2] = {-1, -1};
|
||||
Require(reopen || pipe_nid_postfix(redirect) == 0);
|
||||
const bool captured = ::_dup2(::_fileno(hostCapture), 2) == 0 && SetStdHandle(STD_ERROR_HANDLE, reinterpret_cast<HANDLE>(::_get_osfhandle(2)));
|
||||
const bool redirected = reopen ? freopen_nid_postfix("stderr_probe.txt", "w", &_Stderr_nid_postfix) == &_Stderr_nid_postfix : dup2_nid_postfix(redirect[1], 2) == 2;
|
||||
const HANDLE kept = GetStdHandle(STD_ERROR_HANDLE);
|
||||
DWORD count = 0;
|
||||
const bool separate = kept != reinterpret_cast<HANDLE>(::_get_osfhandle(2)) && WriteFile(kept, "kept", 4, &count, nullptr) && count == 4;
|
||||
const bool written = write_nid_postfix(2, "G", 1) == 1;
|
||||
const bool restored = ::_dup2(hostError, 2) == 0 && ::_close(hostError) == 0 && SetStdHandle(STD_ERROR_HANDLE, reinterpret_cast<HANDLE>(::_get_osfhandle(2)));
|
||||
Require(captured && redirected && separate && written && restored && CloseHandle(kept));
|
||||
std::rewind(hostCapture);
|
||||
char keptText[8]{};
|
||||
Require(std::fread(keptText, 1, sizeof(keptText), hostCapture) == 4 && std::memcmp(keptText, "kept", 4) == 0);
|
||||
Require(std::fclose(hostCapture) == 0);
|
||||
char guestText = 0;
|
||||
if (reopen) {
|
||||
Require(std::filesystem::file_size("stderr_probe.txt") == 1 && std::filesystem::remove("stderr_probe.txt"));
|
||||
} else {
|
||||
Require(close_nid_postfix(redirect[1]) == 0 && read_nid_postfix(redirect[0], &guestText, 1) == 1 && guestText == 'G');
|
||||
Require(close_nid_postfix(redirect[0]) == 0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
const auto root = std::filesystem::path("anyps5-filesystem-test-" +
|
||||
std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()));
|
||||
Require(mkdir_nid_postfix(root.string().c_str(), 0700) == 0);
|
||||
|
|
|
|||
Loading…
Reference in New Issue