Core: Simulate write-only file access with read-write access (#3360)

* Swap write access mode for read write

Opening with access mode w will erase the opened file. We do not want this.

* Create mode

Opening with write access was previously the only way to create a file through open, so add a separate FileAccessMode that uses the write access mode to create files.

* Update file_system.cpp

Remove a hack added to posix_rename to bypass the file clearing behaviors of FileAccessMode::Write

* Check access mode in read functions

Write-only files cause the EBADF return on the various read functions. Now that we're opening files differently, properly handling this is necessary.

* Separate appends into proper modes

Fixes a potential regression from one of my prior PRs, and ensures the Write | Append flag combo also behaves properly in read-related functions.

* Move IsWriteOnly check after device/socket reads

file->f is only valid for files, so checking this before checking for sockets/devices will cause access violations.

* Fix issues

Now that Write is identical to ReadWrite, internal uses of Write need to be swapped to my new Create mode

* Fix remaining uses of FileAccessMode write to create files

Missed these before.

* Fix rebase

* Add stubbed get_authinfo (#3722)

* mostly stubbed get_authinfo

* Return value observed on console if get_authinfo was called for the current thread, esrch otherwise

---------

Co-authored-by: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com>
Co-authored-by: georgemoralis <giorgosmrls@gmail.com>
This commit is contained in:
Stephen Miller
2025-11-04 02:57:26 -06:00
committed by GitHub
parent 08fe66a97f
commit 683e5f3b04
16 changed files with 78 additions and 43 deletions

View File

@@ -40,28 +40,30 @@ namespace {
switch (mode) {
case FileAccessMode::Read:
return L"rb";
case FileAccessMode::Write:
return L"wb";
case FileAccessMode::Append:
return L"ab";
case FileAccessMode::Write:
case FileAccessMode::ReadWrite:
return L"r+b";
case FileAccessMode::ReadAppend:
return L"a+b";
case FileAccessMode::Create:
return L"wb";
}
break;
case FileType::TextFile:
switch (mode) {
case FileAccessMode::Read:
return L"r";
case FileAccessMode::Write:
return L"w";
case FileAccessMode::Append:
return L"a";
case FileAccessMode::Write:
case FileAccessMode::ReadWrite:
return L"r+";
case FileAccessMode::ReadAppend:
return L"a+";
case FileAccessMode::Create:
return L"w";
}
break;
}
@@ -91,28 +93,30 @@ namespace {
switch (mode) {
case FileAccessMode::Read:
return "rb";
case FileAccessMode::Write:
return "wb";
case FileAccessMode::Append:
return "ab";
case FileAccessMode::Write:
case FileAccessMode::ReadWrite:
return "r+b";
case FileAccessMode::ReadAppend:
return "a+b";
case FileAccessMode::Create:
return "wb";
}
break;
case FileType::TextFile:
switch (mode) {
case FileAccessMode::Read:
return "r";
case FileAccessMode::Write:
return "w";
case FileAccessMode::Append:
return "a";
case FileAccessMode::Write:
case FileAccessMode::ReadWrite:
return "r+";
case FileAccessMode::ReadAppend:
return "a+";
case FileAccessMode::Create:
return "w";
}
break;
}

View File

@@ -21,9 +21,8 @@ enum class FileAccessMode {
*/
Read = 1 << 0,
/**
* If the file at path exists, the existing contents of the file are erased.
* The empty file is then opened for writing.
* If the file at path does not exist, it creates and opens a new empty file for writing.
* If the file at path exists, it opens the file for writing.
* If the file at path does not exist, it fails to open the file.
*/
Write = 1 << 1,
/**
@@ -42,6 +41,12 @@ enum class FileAccessMode {
* reading and appending.
*/
ReadAppend = Read | Append,
/**
* If the file at path exists, the existing contents of the file are erased.
* The empty file is then opened for writing.
* If the file at path does not exist, it creates and opens a new empty file for writing.
*/
Create = 1 << 3,
};
DECLARE_ENUM_FLAG_OPERATORS(FileAccessMode);
@@ -102,6 +107,11 @@ public:
return file != nullptr;
}
bool IsWriteOnly() const {
return file_access_mode == FileAccessMode::Append ||
file_access_mode == FileAccessMode::Write;
}
uintptr_t GetFileMapping();
int Open(const std::filesystem::path& path, FileAccessMode mode,
@@ -210,7 +220,7 @@ public:
}
static size_t WriteBytes(const std::filesystem::path path, const auto& data) {
IOFile out(path, FileAccessMode::Write);
IOFile out(path, FileAccessMode::Create);
return out.Write(data);
}
std::FILE* file = nullptr;

View File

@@ -62,7 +62,7 @@ private:
class FileBackend {
public:
explicit FileBackend(const std::filesystem::path& filename, bool should_append = false)
: file{filename, should_append ? FS::FileAccessMode::Append : FS::FileAccessMode::Write,
: file{filename, should_append ? FS::FileAccessMode::Append : FS::FileAccessMode::Create,
FS::FileType::TextFile} {}
~FileBackend() = default;

View File

@@ -152,7 +152,7 @@ inline std::string RunDisassembler(const std::string& disassembler_cli, const T&
}
} else {
cli.replace(pos, src_arg.size(), "\"" + bin_path.string() + "\"");
Common::FS::IOFile file(bin_path, Common::FS::FileAccessMode::Write);
Common::FS::IOFile file(bin_path, Common::FS::FileAccessMode::Create);
file.Write(shader_code);
file.Close();

View File

@@ -123,7 +123,7 @@ void FrameDumpViewer::Draw() {
const auto fname = fmt::format("{:%F %H-%M-%S} {}_{}_{}.bin", now_time,
magic_enum::enum_name(selected_queue_type),
selected_submit_num, selected_queue_num2);
Common::FS::IOFile file(fname, Common::FS::FileAccessMode::Write);
Common::FS::IOFile file(fname, Common::FS::FileAccessMode::Create);
const auto& data = frame_dump->queues[selected_cmd].data;
if (file.IsOpen()) {
DebugState.ShowDebugMessage(fmt::format("Dumping cmd as {}", fname));

View File

@@ -99,7 +99,7 @@ bool PSF::Open(const std::vector<u8>& psf_buffer) {
}
bool PSF::Encode(const std::filesystem::path& filepath) const {
Common::FS::IOFile file(filepath, Common::FS::FileAccessMode::Write);
Common::FS::IOFile file(filepath, Common::FS::FileAccessMode::Create);
if (!file.IsOpen()) {
return false;
}

View File

@@ -140,7 +140,7 @@ s32 PS4_SYSV_ABI open(const char* raw_path, s32 flags, u16 mode) {
return -1;
}
// Create a file if it doesn't exist
Common::FS::IOFile out(file->m_host_name, Common::FS::FileAccessMode::Write);
Common::FS::IOFile out(file->m_host_name, Common::FS::FileAccessMode::Create);
}
} else if (!exists) {
// If we're not creating a file, and it doesn't exist, return ENOENT
@@ -205,22 +205,30 @@ s32 PS4_SYSV_ABI open(const char* raw_path, s32 flags, u16 mode) {
}
if (read) {
// Read only
// Open exclusively for reading
e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Read);
} else if (read_only) {
// Can't open files with write/read-write access in a read only directory
h->DeleteHandle(handle);
*__Error() = POSIX_EROFS;
return -1;
} else if (append) {
// Append can be specified with rdwr or write, but we treat it as a separate mode.
e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Append);
} else if (write) {
// Write only
e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Write);
if (append) {
// Open exclusively for appending
e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Append);
} else {
// Open exclusively for writing
e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Write);
}
} else if (rdwr) {
// Read and write
e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::ReadWrite);
if (append) {
// Open for reading and appending
e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::ReadAppend);
} else {
// Open for reading and writing
e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::ReadWrite);
}
}
}
@@ -354,6 +362,12 @@ s64 PS4_SYSV_ABI readv(s32 fd, const OrbisKernelIovec* iov, s32 iovcnt) {
}
return result;
}
if (file->f.IsWriteOnly()) {
*__Error() = POSIX_EBADF;
return -1;
}
s64 total_read = 0;
for (s32 i = 0; i < iovcnt; i++) {
total_read += ReadFile(file->f, iov[i].iov_base, iov[i].iov_len);
@@ -509,6 +523,12 @@ s64 PS4_SYSV_ABI read(s32 fd, void* buf, u64 nbytes) {
// Socket functions handle errnos internally.
return file->socket->ReceivePacket(buf, nbytes, 0, nullptr, 0);
}
if (file->f.IsWriteOnly()) {
*__Error() = POSIX_EBADF;
return -1;
}
return ReadFile(file->f, buf, nbytes);
}
@@ -801,11 +821,7 @@ s32 PS4_SYSV_ABI posix_rename(const char* from, const char* to) {
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
auto file = h->GetFile(src_path);
if (file) {
// We need to force ReadWrite if the file had Write access before
// Otherwise f.Open will clear the file contents.
auto access_mode = file->f.GetAccessMode() == Common::FS::FileAccessMode::Write
? Common::FS::FileAccessMode::ReadWrite
: file->f.GetAccessMode();
auto access_mode = file->f.GetAccessMode();
file->f.Close();
std::filesystem::remove(src_path);
file->f.Open(dst_path, access_mode);
@@ -855,6 +871,11 @@ s64 PS4_SYSV_ABI posix_preadv(s32 fd, OrbisKernelIovec* iov, s32 iovcnt, s64 off
return result;
}
if (file->f.IsWriteOnly()) {
*__Error() = POSIX_EBADF;
return -1;
}
const s64 pos = file->f.Tell();
SCOPE_EXIT {
file->f.Seek(pos);

View File

@@ -180,7 +180,7 @@ void SaveInstance::SetupAndMount(bool read_only, bool copy_icon, bool ignore_cor
}
if (!ignore_corrupt && !read_only) {
Common::FS::IOFile f(corrupt_file_path, Common::FS::FileAccessMode::Write);
Common::FS::IOFile f(corrupt_file_path, Common::FS::FileAccessMode::Create);
f.Close();
}

View File

@@ -59,7 +59,7 @@ void PersistMemory(u32 slot_id, bool lock) {
while (n++ < 10) {
try {
IOFile f;
int r = f.Open(memoryPath, Common::FS::FileAccessMode::Write);
int r = f.Open(memoryPath, Common::FS::FileAccessMode::Create);
if (f.IsOpen()) {
f.WriteRaw<u8>(data.memory_cache.data(), data.memory_cache.size());
f.Close();
@@ -148,7 +148,7 @@ void SetIcon(u32 slot_id, void* buf, size_t buf_size) {
fs::copy_file(src_icon, icon_path);
}
} else {
IOFile file(icon_path, Common::FS::FileAccessMode::Write);
IOFile file(icon_path, Common::FS::FileAccessMode::Create);
file.WriteRaw<u8>(buf, buf_size);
file.Close();
}

View File

@@ -1389,7 +1389,7 @@ Error PS4_SYSV_ABI sceSaveDataSaveIcon(const OrbisSaveDataMountPoint* mountPoint
}
try {
const Common::FS::IOFile file(path, Common::FS::FileAccessMode::Write);
const Common::FS::IOFile file(path, Common::FS::FileAccessMode::Create);
file.WriteRaw<u8>(icon->buf, std::min(icon->bufSize, icon->dataSize));
} catch (const fs::filesystem_error& e) {
LOG_ERROR(Lib_SaveData, "Failed to load icon: {}", e.what());

View File

@@ -502,19 +502,19 @@ bool Elf::IsSharedLib() {
}
void Elf::ElfHeaderDebugDump(const std::filesystem::path& file_name) {
Common::FS::IOFile f{file_name, Common::FS::FileAccessMode::Write,
Common::FS::IOFile f{file_name, Common::FS::FileAccessMode::Create,
Common::FS::FileType::TextFile};
f.WriteString(ElfHeaderStr());
}
void Elf::SelfHeaderDebugDump(const std::filesystem::path& file_name) {
Common::FS::IOFile f{file_name, Common::FS::FileAccessMode::Write,
Common::FS::IOFile f{file_name, Common::FS::FileAccessMode::Create,
Common::FS::FileType::TextFile};
f.WriteString(SElfHeaderStr());
}
void Elf::SelfSegHeaderDebugDump(const std::filesystem::path& file_name) {
Common::FS::IOFile f{file_name, Common::FS::FileAccessMode::Write,
Common::FS::IOFile f{file_name, Common::FS::FileAccessMode::Create,
Common::FS::FileType::TextFile};
for (u16 i = 0; i < m_self.segment_count; i++) {
f.WriteString(SELFSegHeader(i));
@@ -522,7 +522,7 @@ void Elf::SelfSegHeaderDebugDump(const std::filesystem::path& file_name) {
}
void Elf::PHeaderDebugDump(const std::filesystem::path& file_name) {
Common::FS::IOFile f{file_name, Common::FS::FileAccessMode::Write,
Common::FS::IOFile f{file_name, Common::FS::FileAccessMode::Create,
Common::FS::FileType::TextFile};
if (m_elf_header.e_phentsize > 0) {
for (u16 i = 0; i < m_elf_header.e_phnum; i++) {

View File

@@ -32,7 +32,7 @@ const SymbolRecord* SymbolsResolver::FindSymbol(const SymbolResolver& s) const {
}
void SymbolsResolver::DebugDump(const std::filesystem::path& file_name) {
Common::FS::IOFile f{file_name, Common::FS::FileAccessMode::Write,
Common::FS::IOFile f{file_name, Common::FS::FileAccessMode::Create,
Common::FS::FileType::TextFile};
for (const auto& symbol : m_symbols) {
const auto ids = Common::SplitString(symbol.name, '#');

View File

@@ -559,7 +559,7 @@ void Translator::EmitFetch(const GcnInst& inst) {
std::filesystem::create_directories(dump_dir);
}
const auto filename = fmt::format("vs_{:#018x}.fetch.bin", info.pgm_hash);
const auto file = IOFile{dump_dir / filename, FileAccessMode::Write};
const auto file = IOFile{dump_dir / filename, FileAccessMode::Create};
file.WriteRaw<u8>(fetch_data->code, fetch_data->size);
}

View File

@@ -39,7 +39,7 @@ static void DumpSrtProgram(const Shader::Info& info, const u8* code, size_t code
std::filesystem::create_directories(dump_dir);
}
const auto filename = fmt::format("{}_{:#018x}.srtprogram.txt", info.stage, info.pgm_hash);
const auto file = IOFile{dump_dir / filename, FileAccessMode::Write, FileType::TextFile};
const auto file = IOFile{dump_dir / filename, FileAccessMode::Create, FileType::TextFile};
u64 address = reinterpret_cast<u64>(code);
u64 code_end = address + codesize;

View File

@@ -28,7 +28,7 @@ void DumpProgram(const Program& program, const Info& info, const std::string& ty
}
const auto ir_filename =
fmt::format("{}_{:#018x}.{}irprogram.txt", info.stage, info.pgm_hash, type);
const auto ir_file = IOFile{dump_dir / ir_filename, FileAccessMode::Write, FileType::TextFile};
const auto ir_file = IOFile{dump_dir / ir_filename, FileAccessMode::Create, FileType::TextFile};
size_t index{0};
std::map<const IR::Inst*, size_t> inst_to_index;
@@ -46,7 +46,7 @@ void DumpProgram(const Program& program, const Info& info, const std::string& ty
const auto asl_filename = fmt::format("{}_{:#018x}.{}asl.txt", info.stage, info.pgm_hash, type);
const auto asl_file =
IOFile{dump_dir / asl_filename, FileAccessMode::Write, FileType::TextFile};
IOFile{dump_dir / asl_filename, FileAccessMode::Create, FileType::TextFile};
for (const auto& node : program.syntax_list) {
std::string s = IR::DumpASLNode(node, block_to_index, inst_to_index) + '\n';

View File

@@ -632,7 +632,7 @@ void PipelineCache::DumpShader(std::span<const u32> code, u64 hash, Shader::Stag
std::filesystem::create_directories(dump_dir);
}
const auto filename = fmt::format("{}.{}", GetShaderName(stage, hash, perm_idx), ext);
const auto file = IOFile{dump_dir / filename, FileAccessMode::Write};
const auto file = IOFile{dump_dir / filename, FileAccessMode::Create};
file.WriteSpan(code);
}