mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-21 03:16:54 +02:00
kernel: implement path-based truncate for replay files
This commit is contained in:
committed by
nmzik
parent
cbe5b62947
commit
56d3ce98f0
@@ -1197,6 +1197,67 @@ int KYTY_SYSV_ABI KernelFstat(int d, FileStat* sb) {
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int KYTY_SYSV_ABI KernelTruncate(const char* path, int64_t length) {
|
||||||
|
PRINT_NAME();
|
||||||
|
if (path == nullptr) {
|
||||||
|
return KERNEL_ERROR_EFAULT;
|
||||||
|
}
|
||||||
|
if (path[0] != '/' || length < 0) {
|
||||||
|
return KERNEL_ERROR_EINVAL;
|
||||||
|
}
|
||||||
|
size_t component_length = 0;
|
||||||
|
for (size_t i = 0; path[i] != '\0'; ++i) {
|
||||||
|
if (i >= 1023) {
|
||||||
|
return KERNEL_ERROR_ENAMETOOLONG;
|
||||||
|
}
|
||||||
|
if (path[i] == '/') {
|
||||||
|
component_length = 0;
|
||||||
|
} else if (++component_length > 255) {
|
||||||
|
return KERNEL_ERROR_ENAMETOOLONG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (IsRandomDevice(path)) {
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
const auto real_name = g_mount_points->ResolvePath(path);
|
||||||
|
if (real_name.empty()) {
|
||||||
|
return KERNEL_ERROR_ENOENT;
|
||||||
|
}
|
||||||
|
if (Common::IsArchivePath(real_name)) {
|
||||||
|
return KERNEL_ERROR_EROFS;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::error_code error;
|
||||||
|
const auto status = std::filesystem::status(real_name, error);
|
||||||
|
if (!error) {
|
||||||
|
if (!std::filesystem::exists(status)) {
|
||||||
|
return KERNEL_ERROR_ENOENT;
|
||||||
|
}
|
||||||
|
if (std::filesystem::is_directory(status)) {
|
||||||
|
return KERNEL_ERROR_EISDIR;
|
||||||
|
}
|
||||||
|
if (std::filesystem::is_regular_file(status)) {
|
||||||
|
std::filesystem::resize_file(real_name, static_cast<uint64_t>(length), error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (error) {
|
||||||
|
switch (static_cast<std::errc>(error.default_error_condition().value())) {
|
||||||
|
case std::errc::no_such_file_or_directory: return KERNEL_ERROR_ENOENT;
|
||||||
|
case std::errc::not_a_directory: return KERNEL_ERROR_ENOTDIR;
|
||||||
|
case std::errc::filename_too_long: return KERNEL_ERROR_ENAMETOOLONG;
|
||||||
|
case std::errc::permission_denied: return KERNEL_ERROR_EACCES;
|
||||||
|
case std::errc::read_only_file_system: return KERNEL_ERROR_EROFS;
|
||||||
|
case std::errc::file_too_large: return KERNEL_ERROR_EFBIG;
|
||||||
|
case std::errc::invalid_argument: return KERNEL_ERROR_EINVAL;
|
||||||
|
case std::errc::no_space_on_device: return KERNEL_ERROR_ENOSPC;
|
||||||
|
default: return KERNEL_ERROR_EIO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOGF("\tTruncate (size = %" PRId64 ") file: %s\n", length,
|
||||||
|
Common::PathToString(real_name).c_str());
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
int KYTY_SYSV_ABI KernelFtruncate(int d, int64_t length) {
|
int KYTY_SYSV_ABI KernelFtruncate(int d, int64_t length) {
|
||||||
PRINT_NAME();
|
PRINT_NAME();
|
||||||
|
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ int64_t KYTY_SYSV_ABI KernelPwritev(int d, const KernelIovec* iov, int iovcnt, i
|
|||||||
int64_t KYTY_SYSV_ABI KernelLseek(int d, int64_t offset, int whence);
|
int64_t KYTY_SYSV_ABI KernelLseek(int d, int64_t offset, int whence);
|
||||||
int KYTY_SYSV_ABI KernelStat(const char* path, FileStat* sb);
|
int KYTY_SYSV_ABI KernelStat(const char* path, FileStat* sb);
|
||||||
int KYTY_SYSV_ABI KernelFstat(int d, FileStat* sb);
|
int KYTY_SYSV_ABI KernelFstat(int d, FileStat* sb);
|
||||||
|
int KYTY_SYSV_ABI KernelTruncate(const char* path, int64_t length);
|
||||||
int KYTY_SYSV_ABI KernelFtruncate(int d, int64_t length);
|
int KYTY_SYSV_ABI KernelFtruncate(int d, int64_t length);
|
||||||
int KYTY_SYSV_ABI KernelUnlink(const char* path);
|
int KYTY_SYSV_ABI KernelUnlink(const char* path);
|
||||||
int KYTY_SYSV_ABI KernelRename(const char* from, const char* to);
|
int KYTY_SYSV_ABI KernelRename(const char* from, const char* to);
|
||||||
|
|||||||
@@ -3118,6 +3118,7 @@ LIB_DEFINE(InitLibKernel_1_FS) {
|
|||||||
LIB_FUNC("eV9wAD2riIA", FileSystem::KernelStat);
|
LIB_FUNC("eV9wAD2riIA", FileSystem::KernelStat);
|
||||||
LIB_FUNC("kBwCPsYX-m4", FileSystem::KernelFstat);
|
LIB_FUNC("kBwCPsYX-m4", FileSystem::KernelFstat);
|
||||||
LIB_FUNC("VW3TVZiM4-E", FileSystem::KernelFtruncate);
|
LIB_FUNC("VW3TVZiM4-E", FileSystem::KernelFtruncate);
|
||||||
|
LIB_FUNC("WlyEA-sLDf0", FileSystem::KernelTruncate);
|
||||||
LIB_FUNC("AUXVxWeJU-A", FileSystem::KernelUnlink);
|
LIB_FUNC("AUXVxWeJU-A", FileSystem::KernelUnlink);
|
||||||
LIB_FUNC("52NcYU9+lEo", FileSystem::KernelRename);
|
LIB_FUNC("52NcYU9+lEo", FileSystem::KernelRename);
|
||||||
LIB_FUNC("taRWhTJFTgE", FileSystem::KernelGetdirentries);
|
LIB_FUNC("taRWhTJFTgE", FileSystem::KernelGetdirentries);
|
||||||
|
|||||||
@@ -332,6 +332,60 @@ void TestSaveOpenVisibility() {
|
|||||||
Check(FileSystem::KernelClose(truncated) == OK, "close truncated save file");
|
Check(FileSystem::KernelClose(truncated) == OK, "close truncated save file");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestSaveTruncate() {
|
||||||
|
namespace Kernel = Libs::LibKernel;
|
||||||
|
Loader::SymbolDatabase symbols;
|
||||||
|
Libs::InitLibKernel_1(&symbols);
|
||||||
|
const auto* symbol = symbols.Find(
|
||||||
|
{"WlyEA-sLDf0", "libkernel", 1, "libkernel", 1, 1, Loader::SymbolType::Func});
|
||||||
|
Check(symbol != nullptr, "sceKernelTruncate export resolves");
|
||||||
|
const auto truncate =
|
||||||
|
reinterpret_cast<int (KYTY_SYSV_ABI *)(const char*, int64_t)>(symbol->vaddr);
|
||||||
|
constexpr char Path[] = "/savedata0/truncate-save.dat";
|
||||||
|
constexpr char Payload[] = "profile data";
|
||||||
|
const int fd = FileSystem::KernelOpen(Path, 0x602, 0777);
|
||||||
|
Check(fd >= 3 && FileSystem::KernelWrite(fd, Payload, sizeof(Payload) - 1) ==
|
||||||
|
sizeof(Payload) - 1,
|
||||||
|
"populate path truncation fixture");
|
||||||
|
FileSystem::FileStat stat {};
|
||||||
|
std::array<char, 8> bytes {};
|
||||||
|
Check(truncate(Path, 3) == OK &&
|
||||||
|
FileSystem::KernelFstat(fd, &stat) == OK && stat.st_size == 3 &&
|
||||||
|
FileSystem::KernelLseek(fd, 0, 1) == sizeof(Payload) - 1 &&
|
||||||
|
FileSystem::KernelPread(fd, bytes.data(), bytes.size(), 0) == 3 &&
|
||||||
|
std::memcmp(bytes.data(), Payload, 3) == 0,
|
||||||
|
"path truncate discards the tail without moving an open descriptor");
|
||||||
|
bytes.fill('x');
|
||||||
|
Check(truncate(Path, bytes.size()) == OK &&
|
||||||
|
FileSystem::KernelFstat(fd, &stat) == OK && stat.st_size == bytes.size() &&
|
||||||
|
FileSystem::KernelPread(fd, bytes.data(), bytes.size(), 0) == bytes.size() &&
|
||||||
|
std::memcmp(bytes.data(), Payload, 3) == 0 &&
|
||||||
|
std::all_of(bytes.begin() + 3, bytes.end(), [](char byte) { return byte == 0; }) &&
|
||||||
|
FileSystem::KernelLseek(fd, 0, 1) == sizeof(Payload) - 1,
|
||||||
|
"path truncate extends with zero bytes and preserves the descriptor position");
|
||||||
|
Check(truncate(Path, -1) == Kernel::KERNEL_ERROR_EINVAL &&
|
||||||
|
truncate(nullptr, 0) == Kernel::KERNEL_ERROR_EFAULT &&
|
||||||
|
truncate("truncate-save.dat", 0) == Kernel::KERNEL_ERROR_EINVAL &&
|
||||||
|
truncate("/savedata0/missing-save.dat", 0) == Kernel::KERNEL_ERROR_ENOENT &&
|
||||||
|
truncate("/savedata0", 0) == Kernel::KERNEL_ERROR_EISDIR &&
|
||||||
|
truncate("/savedata0/truncate-save.dat/child", 0) == Kernel::KERNEL_ERROR_ENOTDIR &&
|
||||||
|
FileSystem::KernelFstat(fd, &stat) == OK && stat.st_size == bytes.size() &&
|
||||||
|
FileSystem::KernelLseek(fd, 0, 1) == sizeof(Payload) - 1,
|
||||||
|
"path truncate errors preserve file size and descriptor position");
|
||||||
|
Check(truncate("/dev/urandom", 0) == OK,
|
||||||
|
"path truncate is a no-op for a nonregular nondirectory file");
|
||||||
|
std::string long_path = "/savedata0";
|
||||||
|
long_path.resize(1024, '/');
|
||||||
|
Check(truncate(long_path.c_str(), 0) == Kernel::KERNEL_ERROR_ENAMETOOLONG,
|
||||||
|
"path truncate rejects a pathname beyond the guest length limit");
|
||||||
|
long_path.pop_back();
|
||||||
|
Check(truncate(long_path.c_str(), 0) == Kernel::KERNEL_ERROR_EISDIR &&
|
||||||
|
truncate((std::string("/savedata0/") + std::string(256, 'a')).c_str(), 0) ==
|
||||||
|
Kernel::KERNEL_ERROR_ENAMETOOLONG,
|
||||||
|
"path truncate permits the pathname length limit and bounds each component");
|
||||||
|
Check(FileSystem::KernelClose(fd) == OK, "close path truncation fixture");
|
||||||
|
}
|
||||||
|
|
||||||
void TestAioBatches() {
|
void TestAioBatches() {
|
||||||
namespace Kernel = Libs::LibKernel;
|
namespace Kernel = Libs::LibKernel;
|
||||||
Loader::SymbolDatabase symbols;
|
Loader::SymbolDatabase symbols;
|
||||||
@@ -699,6 +753,12 @@ void CheckArchiveMount(const std::filesystem::path &root) {
|
|||||||
FileSystem::KernelFtruncate(fd, 0) ==
|
FileSystem::KernelFtruncate(fd, 0) ==
|
||||||
Libs::LibKernel::KERNEL_ERROR_EBADF,
|
Libs::LibKernel::KERNEL_ERROR_EBADF,
|
||||||
"archive read-only descriptors reject write and truncate");
|
"archive read-only descriptors reject write and truncate");
|
||||||
|
Check(FileSystem::KernelTruncate(GuestMember, 0) ==
|
||||||
|
Libs::LibKernel::KERNEL_ERROR_EROFS &&
|
||||||
|
FileSystem::KernelFstat(fd, &descriptor_stat) == OK &&
|
||||||
|
descriptor_stat.st_size == payload.size() &&
|
||||||
|
FileSystem::KernelLseek(fd, 0, 1) == payload.size(),
|
||||||
|
"path truncate rejects a read-only archive and preserves its member");
|
||||||
|
|
||||||
const auto unicode_guest = std::string("/app0/") + std::string(ArchiveTests::UnicodeFilename);
|
const auto unicode_guest = std::string("/app0/") + std::string(ArchiveTests::UnicodeFilename);
|
||||||
const int unicode = FileSystem::KernelOpen(unicode_guest.c_str(), 0, 0);
|
const int unicode = FileSystem::KernelOpen(unicode_guest.c_str(), 0, 0);
|
||||||
@@ -1908,6 +1968,7 @@ int main(int, char**) {
|
|||||||
CheckAprPaths(temporary.Path());
|
CheckAprPaths(temporary.Path());
|
||||||
FileSystem::Mount(temporary.Path(), "/savedata0");
|
FileSystem::Mount(temporary.Path(), "/savedata0");
|
||||||
TestSaveOpenVisibility();
|
TestSaveOpenVisibility();
|
||||||
|
TestSaveTruncate();
|
||||||
TestAioBatches();
|
TestAioBatches();
|
||||||
CheckSaveRename(temporary.Path(), "first-save");
|
CheckSaveRename(temporary.Path(), "first-save");
|
||||||
CheckSaveRename(temporary.Path(), "replacement-save");
|
CheckSaveRename(temporary.Path(), "replacement-save");
|
||||||
|
|||||||
Reference in New Issue
Block a user