Files
AnyPS5/core/libs/tests/GuestKernelSync.cpp
T
kjhq bb1a4cbec4 fix(libkernel): flush only the paths the guest changed in sceKernelSync on Windows
Opening a volume needs administrator rights, so the volume flush skipped
every volume in a normal user session. The libc, libkernel, save data and
app content file functions now record each host file and directory they
change. On Windows sceKernelSync flushes the recorded paths that changed
since the previous call, plus their changed parent directories, through
handles a normal user can open. Linux still calls sync().
2026-10-08 08:27:22 +00:00

74 lines
3.4 KiB
C++

#include "prx/libc/include/General.hpp"
#include <algorithm>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <filesystem>
#include <fstream>
#ifdef _WIN32
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
extern "C" {
int APS5_VABI sceKernelOpen(const char*, int, unsigned short);
int APS5_VABI sceKernelClose(int);
std::int64_t APS5_VABI sceKernelWrite(int, const void*, std::size_t);
int APS5_VABI sceKernelMkdir(const char*, unsigned short);
int APS5_VABI sceKernelRename(const char*, const char*);
int APS5_VABI sceKernelUnlink(const char*);
int APS5_VABI sceKernelChmod_nid_postfix(const char*, std::uint16_t);
void APS5_VABI sceKernelSync();
void* APS5_VABI fopen_nid_postfix(const char*, const char*);
int APS5_VABI fclose_nid_postfix(void*);
}
static void Check(bool value, int line) {
if (!value) {
std::fprintf(stderr, "Kernel sync check failed at line %d\n", line);
std::abort();
}
}
#define Require(value) Check((value), __LINE__)
static bool Recorded(const char* guest) {
const auto paths = WrittenPaths_nid_no_patch();
return std::find(paths.begin(), paths.end(), ResolvePath_nid_no_patch(guest).lexically_normal()) != paths.end();
}
int main() {
std::filesystem::remove_all("kernel_sync_probe");
Require(sceKernelMkdir("kernel_sync_probe", 0777) == 0);
Require(Recorded("kernel_sync_probe"));
std::ofstream("kernel_sync_probe/existing.bin") << "existing";
const int reader = sceKernelOpen("kernel_sync_probe/existing.bin", 0x0, 0);
Require(reader >= 0 && sceKernelClose(reader) == 0);
void* stream = fopen_nid_postfix("kernel_sync_probe/existing.bin", "r");
Require(stream != nullptr && fclose_nid_postfix(stream) == 0);
Require(!Recorded("kernel_sync_probe/existing.bin"));
stream = fopen_nid_postfix("kernel_sync_probe/existing.bin", "a");
Require(stream != nullptr && fclose_nid_postfix(stream) == 0);
Require(Recorded("kernel_sync_probe/existing.bin"));
const int writer = sceKernelOpen("kernel_sync_probe/data.bin", 0x1 | 0x200 | 0x400, 0644);
Require(writer >= 0 && sceKernelWrite(writer, "data", 4) == 4 && sceKernelClose(writer) == 0);
Require(Recorded("kernel_sync_probe/data.bin"));
Require(sceKernelRename("kernel_sync_probe/data.bin", "kernel_sync_probe/moved.bin") == 0);
Require(Recorded("kernel_sync_probe/moved.bin"));
Require(sceKernelUnlink("kernel_sync_probe/moved.bin") == 0);
sceKernelSync();
Require(!Recorded("kernel_sync_probe/data.bin") && !Recorded("kernel_sync_probe/moved.bin"));
sceKernelSync();
const int locked = sceKernelOpen("kernel_sync_probe/read_only.bin", 0x1 | 0x200, 0644);
Require(locked >= 0 && sceKernelWrite(locked, "data", 4) == 4 && sceKernelClose(locked) == 0);
Require(sceKernelChmod_nid_postfix("kernel_sync_probe/read_only.bin", 0444) == 0);
Require(Recorded("kernel_sync_probe/read_only.bin"));
sceKernelSync();
Require(sceKernelChmod_nid_postfix("kernel_sync_probe/read_only.bin", 0644) == 0);
#ifdef _WIN32
const auto exclusive = CreateFileW(ResolvePath_nid_no_patch("kernel_sync_probe/read_only.bin").c_str(), GENERIC_READ, FILE_SHARE_READ,
nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
Require(exclusive != INVALID_HANDLE_VALUE);
sceKernelSync();
CloseHandle(exclusive);
#endif
sceKernelSync();
std::filesystem::remove_all("kernel_sync_probe");
}