Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 6 additions & 8 deletions core/libs/prx/libc/src/FileAndHeap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -344,17 +344,15 @@ FileStream* APS5_VABI _ZSt7_FiopenPKcNSt5_IosbIiE9_OpenmodeEi_nid_postfix(const
{In, "r"}, {Out, "w"}, {Out | Trunc, "w"}, {Out | App, "a"},
{In | Binary, "rb"}, {Out | Binary, "wb"}, {Out | Trunc | Binary, "wb"}, {Out | App | Binary, "ab"},
{In | Out, "r+"}, {In | Out | Trunc, "w+"}, {In | Out | App, "a+"},
{In | Out | Binary, "r+b"}, {In | Out | Trunc | Binary, "w+b"}, {In | Out | App | Binary, "a+b"}};
const int open = (mode & (In | Out | App | Trunc | Binary)) | ((mode & Nocreate) ? In : 0) | ((mode & App) ? Out : 0);
{In | Out | Binary, "r+b"}, {In | Out | Trunc | Binary, "w+b"}, {In | Out | App | Binary, "a+b"},
{Out | Noreplace, "wx"}, {Out | Trunc | Noreplace, "wx"}, {In | Out | Trunc | Noreplace, "w+x"},
{Out | Binary | Noreplace, "wbx"}, {Out | Trunc | Binary | Noreplace, "wbx"},
{In | Out | Trunc | Binary | Noreplace, "w+bx"}};
const int open = (mode & (In | Out | App | Trunc | Binary)) | ((mode & Nocreate) ? In : 0) | ((mode & App) ? Out : 0) |
(((mode & Noreplace) && (mode & (Out | App))) ? Noreplace : 0);
const char* openMode = nullptr;
for (const auto& [flags, text] : modes) if (flags == open) openMode = text;
if (!openMode) return nullptr;
if ((mode & Noreplace) && (open & Out)) {
if (auto* existing = fopen_nid_postfix(filename, "r")) {
fclose_nid_postfix(existing);
return nullptr;
}
}
auto* stream = fopen_nid_postfix(filename, openMode);
if (!stream || !(mode & Ate) || fseek_nid_postfix(stream, 0, SEEK_END) == 0) return stream;
fclose_nid_postfix(stream);
Expand Down
63 changes: 61 additions & 2 deletions core/libs/tests/FileStreams.cpp
Original file line number Diff line number Diff line change
@@ -1,22 +1,27 @@
#include "prx/libc/include/FileStream.hpp"
#include "prx/libc/include/general/VabiMacros.hpp"
#include <array>
#include <barrier>
#include <cerrno>
#include <cstdint>
#include <cstring>
#include <filesystem>
#include <iostream>
#include <stdexcept>
#include <string>
#include <thread>
#include <fcntl.h>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <io.h>
#else
#include <unistd.h>
#endif

extern "C" {
FileStream* APS5_VABI fopen_nid_postfix(const char* filename, const char* mode);
FileStream* APS5_VABI _ZSt7_FiopenPKcNSt5_IosbIiE9_OpenmodeEi_nid_postfix(const char*, int, int);
FileStream* APS5_VABI freopen_nid_postfix(const char* filename, const char* mode, FileStream* stream);
int APS5_VABI fclose_nid_postfix(FileStream* stream);
std::size_t APS5_VABI fread_nid_postfix(void* buffer, std::size_t size, std::size_t count, FileStream* stream);
Expand All @@ -31,8 +36,8 @@ int APS5_VABI ferror_nid_postfix(FileStream* stream);
void APS5_VABI clearerr_nid_postfix(FileStream* stream);
}

static void Require(bool condition) {
if (!condition) throw std::runtime_error("File stream check failed");
static void Require(bool condition, const char* message = "File stream check failed") {
if (!condition) throw std::runtime_error(message);
}

template<typename TAction>
Expand All @@ -57,8 +62,62 @@ static void ReplaceWithReadOnlyDescriptor(FileStream* stream, const char* path)
#endif
}

static void CheckExclusiveCppOpen(const std::string& base) {
constexpr int Out = 0x02, Binary = 0x80, Noreplace = 0x40;
const std::string path = base + ".exclusive";
std::remove(path.c_str());
auto* stream = _ZSt7_FiopenPKcNSt5_IosbIiE9_OpenmodeEi_nid_postfix(path.c_str(), Out | Binary | Noreplace, 0);
Require(stream != nullptr);
constexpr char original[] = "keep existing contents";
Require(fwrite_nid_postfix(original, 1, sizeof(original), stream) == sizeof(original));
Require(fclose_nid_postfix(stream) == 0);
#ifdef _WIN32
const auto nativePath = std::filesystem::path(path).wstring();
const auto writer = CreateFileW(nativePath.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
Require(writer != INVALID_HANDLE_VALUE);
auto* refused = _ZSt7_FiopenPKcNSt5_IosbIiE9_OpenmodeEi_nid_postfix(path.c_str(), Out | Binary | Noreplace, 0);
if (refused) fclose_nid_postfix(refused);
Require(CloseHandle(writer) != 0);
Require(refused == nullptr, "C++ noreplace truncated an existing write-shared file");
#endif
stream = fopen_nid_postfix(path.c_str(), "rb");
Require(stream != nullptr);
std::array<char, sizeof(original)> saved{};
Require(fread_nid_postfix(saved.data(), 1, saved.size(), stream) == saved.size());
Require(std::memcmp(saved.data(), original, saved.size()) == 0);
Require(fclose_nid_postfix(stream) == 0 && std::remove(path.c_str()) == 0);
for (const int mode : {0x42, 0x52, 0x53, 0xc2, 0xd2, 0xd3}) {
stream = _ZSt7_FiopenPKcNSt5_IosbIiE9_OpenmodeEi_nid_postfix(path.c_str(), mode | 0x04, 0);
Require(stream != nullptr && ftell_nid_postfix(stream) == 0);
Require(fwrite_nid_postfix(original, 1, sizeof(original), stream) == sizeof(original));
Require(fclose_nid_postfix(stream) == 0);
errno = 0;
Require(_ZSt7_FiopenPKcNSt5_IosbIiE9_OpenmodeEi_nid_postfix(path.c_str(), mode, 0) == nullptr && errno == EEXIST);
stream = fopen_nid_postfix(path.c_str(), "rb");
Require(stream != nullptr && fread_nid_postfix(saved.data(), 1, saved.size(), stream) == saved.size());
Require(std::memcmp(saved.data(), original, saved.size()) == 0);
Require(fclose_nid_postfix(stream) == 0 && std::remove(path.c_str()) == 0);
}
for (int round = 0; round < 12; ++round) {
std::array<FileStream*, 2> created{};
std::barrier start(2);
const auto create = [&](int index) {
start.arrive_and_wait();
created[index] = _ZSt7_FiopenPKcNSt5_IosbIiE9_OpenmodeEi_nid_postfix(path.c_str(), Out | Binary | Noreplace, 0);
};
std::thread first(create, 0), second(create, 1);
first.join();
second.join();
const auto winners = static_cast<int>(created[0] != nullptr) + static_cast<int>(created[1] != nullptr);
for (auto* opened : created) if (opened) Require(fclose_nid_postfix(opened) == 0);
Require(winners == 1 && std::remove(path.c_str()) == 0);
}
}

int main(int argc, char** argv) {
Require(argc == 2);
CheckExclusiveCppOpen(argv[1]);
Require(_Stdout_nid_postfix.GetHandle() == stdout);
Require(_Stderr_nid_postfix.GetHandle() == stderr);
Require(fputs_nid_postfix("stdout object works\n", &_Stdout_nid_postfix) >= 0);
Expand Down
3 changes: 3 additions & 0 deletions core/libs/tests/GuestFiopen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ int main() {
stream = Open(file, In | Noreplace);
Require(stream != nullptr);
fclose_nid_postfix(stream);
stream = Open(file, In | Binary | Noreplace);
Require(stream != nullptr);
fclose_nid_postfix(stream);

Require(Open(missing, Out | Nocreate) == nullptr);
Require(!Exists(missing));
Expand Down
Loading