mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-19 18:36:53 +02:00
kernel: implement vectored writes for profile index saves
This commit is contained in:
committed by
nmzik
parent
56d3ce98f0
commit
54abbf48f2
@@ -21,6 +21,7 @@
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include <random>
|
||||
#include <system_error>
|
||||
#include <vector>
|
||||
@@ -956,12 +957,10 @@ int64_t KYTY_SYSV_ABI KernelPwrite(int d, const void* buf, size_t nbytes, int64_
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
int64_t KYTY_SYSV_ABI KernelPwritev(int d, const KernelIovec* iov, int iovcnt, int64_t offset) {
|
||||
PRINT_NAME();
|
||||
|
||||
static int64_t Writev(int d, const KernelIovec* iov, int iovcnt, std::optional<int64_t> offset) {
|
||||
std::vector<KernelIovec> buffers;
|
||||
size_t total = 0;
|
||||
const int error = ValidateIovecs(iov, iovcnt, offset, &buffers, &total);
|
||||
const int error = ValidateIovecs(iov, iovcnt, offset.value_or(0), &buffers, &total);
|
||||
if (error != OK) {
|
||||
return error;
|
||||
}
|
||||
@@ -991,11 +990,16 @@ int64_t KYTY_SYSV_ABI KernelPwritev(int d, const KernelIovec* iov, int iovcnt, i
|
||||
}
|
||||
|
||||
const auto position = file->f.Tell();
|
||||
const auto target = (file->append ? file->f.Size() : static_cast<uint64_t>(offset));
|
||||
auto target = position;
|
||||
if (file->append) {
|
||||
target = file->f.Size();
|
||||
} else if (offset) {
|
||||
target = static_cast<uint64_t>(*offset);
|
||||
}
|
||||
if (target > static_cast<uint64_t>(INT64_MAX) - total) {
|
||||
return KERNEL_ERROR_EFBIG;
|
||||
}
|
||||
if (!file->f.Seek(target)) {
|
||||
if ((offset || file->append) && !file->f.Seek(target)) {
|
||||
return KERNEL_ERROR_EIO;
|
||||
}
|
||||
|
||||
@@ -1012,16 +1016,26 @@ int64_t KYTY_SYSV_ABI KernelPwritev(int d, const KernelIovec* iov, int iovcnt, i
|
||||
}
|
||||
}
|
||||
const bool flushed = !file->sync_writes || file->f.Flush();
|
||||
const bool restored = file->f.Seek(position);
|
||||
const bool restored = !offset || file->f.Seek(position);
|
||||
if (!flushed || !restored) {
|
||||
return KERNEL_ERROR_EIO;
|
||||
}
|
||||
|
||||
LOGF("\tWritev %" PRId64 " bytes (pos = %" PRId64 ", iovcnt = %d) to: %s\n", bytes_written,
|
||||
offset, iovcnt, Common::PathToString(file->real_name).c_str());
|
||||
LOGF("\tWritev %" PRId64 " bytes (pos = %" PRIu64 ", iovcnt = %d) to: %s\n", bytes_written,
|
||||
target, iovcnt, Common::PathToString(file->real_name).c_str());
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
int64_t KYTY_SYSV_ABI KernelWritev(int d, const KernelIovec* iov, int iovcnt) {
|
||||
PRINT_NAME();
|
||||
return Writev(d, iov, iovcnt, std::nullopt);
|
||||
}
|
||||
|
||||
int64_t KYTY_SYSV_ABI KernelPwritev(int d, const KernelIovec* iov, int iovcnt, int64_t offset) {
|
||||
PRINT_NAME();
|
||||
return Writev(d, iov, iovcnt, offset);
|
||||
}
|
||||
|
||||
int64_t KYTY_SYSV_ABI KernelLseek(int d, int64_t offset, int whence) {
|
||||
PRINT_NAME();
|
||||
|
||||
|
||||
@@ -64,6 +64,7 @@ int64_t KYTY_SYSV_ABI KernelRead(int d, void* buf, size_t nbytes);
|
||||
int64_t KYTY_SYSV_ABI KernelPread(int d, void* buf, size_t nbytes, int64_t offset);
|
||||
int64_t KYTY_SYSV_ABI KernelPreadv(int d, const KernelIovec* iov, int iovcnt, int64_t offset);
|
||||
int64_t KYTY_SYSV_ABI KernelWrite(int d, const void* buf, size_t nbytes);
|
||||
int64_t KYTY_SYSV_ABI KernelWritev(int d, const KernelIovec* iov, int iovcnt);
|
||||
int64_t KYTY_SYSV_ABI KernelPwrite(int d, const void* buf, size_t nbytes, int64_t offset);
|
||||
int64_t KYTY_SYSV_ABI KernelPwritev(int d, const KernelIovec* iov, int iovcnt, int64_t offset);
|
||||
int64_t KYTY_SYSV_ABI KernelLseek(int d, int64_t offset, int whence);
|
||||
|
||||
@@ -3111,6 +3111,7 @@ LIB_DEFINE(InitLibKernel_1_FS) {
|
||||
LIB_FUNC("UK2Tl2DWUns", FileSystem::KernelClose);
|
||||
LIB_FUNC("Cg4srZ6TKbU", FileSystem::KernelRead);
|
||||
LIB_FUNC("4wSze92BhLI", FileSystem::KernelWrite);
|
||||
LIB_FUNC("kAt6VDbHmro", FileSystem::KernelWritev);
|
||||
LIB_FUNC("+r3rMFwItV4", FileSystem::KernelPread);
|
||||
LIB_FUNC("yTj62I7kw4s", FileSystem::KernelPreadv);
|
||||
LIB_FUNC("nKWi-N2HBV4", FileSystem::KernelPwrite);
|
||||
|
||||
@@ -386,6 +386,82 @@ void TestSaveTruncate() {
|
||||
Check(FileSystem::KernelClose(fd) == OK, "close path truncation fixture");
|
||||
}
|
||||
|
||||
void TestSaveWritev() {
|
||||
namespace Kernel = Libs::LibKernel;
|
||||
Loader::SymbolDatabase symbols;
|
||||
Libs::InitLibKernel_1(&symbols);
|
||||
const auto resolve = [&](const char* nid) {
|
||||
const auto* symbol = symbols.Find(
|
||||
{nid, "libkernel", 1, "libkernel", 1, 1, Loader::SymbolType::Func});
|
||||
Check(symbol != nullptr, "vectored write export resolves");
|
||||
return symbol->vaddr;
|
||||
};
|
||||
const auto writev = reinterpret_cast<int64_t (KYTY_SYSV_ABI *)(
|
||||
int, const FileSystem::KernelIovec*, int)>(resolve("kAt6VDbHmro"));
|
||||
const auto pwritev = reinterpret_cast<int64_t (KYTY_SYSV_ABI *)(
|
||||
int, const FileSystem::KernelIovec*, int, int64_t)>(resolve("mBd4AfLP+u8"));
|
||||
constexpr char Path[] = "/savedata0/vector-save.dat";
|
||||
char first[] = "abc";
|
||||
char second[] = "DE";
|
||||
const std::array<FileSystem::KernelIovec, 3> buffers {{
|
||||
{first, 3}, {nullptr, 0}, {second, 2}}};
|
||||
const int fd = FileSystem::KernelOpen(Path, 0x602, 0777);
|
||||
Check(fd >= 3 && FileSystem::KernelWrite(fd, "_", 1) == 1 &&
|
||||
writev(fd, buffers.data(), buffers.size()) == 5 &&
|
||||
FileSystem::KernelLseek(fd, 0, 1) == 6,
|
||||
"writev writes consecutive segments and advances the descriptor position");
|
||||
std::array<char, 6> bytes {};
|
||||
Check(FileSystem::KernelPread(fd, bytes.data(), bytes.size(), 0) == bytes.size() &&
|
||||
std::memcmp(bytes.data(), "_abcDE", bytes.size()) == 0,
|
||||
"writev preserves the prefix and writes every nonempty segment in order");
|
||||
char replacement[] = "12";
|
||||
const std::array<FileSystem::KernelIovec, 2> patch {{
|
||||
{replacement, 1}, {replacement + 1, 1}}};
|
||||
Check(pwritev(fd, patch.data(), patch.size(), 1) == 2 &&
|
||||
FileSystem::KernelLseek(fd, 0, 1) == 6 &&
|
||||
FileSystem::KernelPread(fd, bytes.data(), bytes.size(), 0) == bytes.size() &&
|
||||
std::memcmp(bytes.data(), "_12cDE", bytes.size()) == 0,
|
||||
"pwritev shares segment ordering while preserving the descriptor position");
|
||||
|
||||
const std::array<FileSystem::KernelIovec, 2> invalid {{
|
||||
{first, 1}, {nullptr, 1}}};
|
||||
const std::array<FileSystem::KernelIovec, 2> oversized {{
|
||||
{first, 1}, {second, static_cast<size_t>(std::numeric_limits<int>::max())}}};
|
||||
Check(writev(fd, nullptr, 1) == Kernel::KERNEL_ERROR_EFAULT &&
|
||||
writev(fd, invalid.data(), invalid.size()) == Kernel::KERNEL_ERROR_EFAULT &&
|
||||
writev(fd, buffers.data(), -1) == Kernel::KERNEL_ERROR_EINVAL &&
|
||||
writev(fd, buffers.data(), 1025) == Kernel::KERNEL_ERROR_EINVAL &&
|
||||
writev(fd, oversized.data(), oversized.size()) == Kernel::KERNEL_ERROR_EINVAL &&
|
||||
pwritev(fd, patch.data(), patch.size(), -1) == Kernel::KERNEL_ERROR_EINVAL &&
|
||||
writev(-1, buffers.data(), buffers.size()) == Kernel::KERNEL_ERROR_EBADF &&
|
||||
writev(fd, nullptr, 0) == 0 &&
|
||||
FileSystem::KernelLseek(fd, 0, 1) == 6 &&
|
||||
FileSystem::KernelPread(fd, bytes.data(), bytes.size(), 0) == bytes.size() &&
|
||||
std::memcmp(bytes.data(), "_12cDE", bytes.size()) == 0,
|
||||
"invalid vectors fail before writing any segment or moving the descriptor");
|
||||
const int reader = FileSystem::KernelOpen(Path, 0, 0);
|
||||
Check(reader >= 3 && writev(reader, buffers.data(), buffers.size()) ==
|
||||
Kernel::KERNEL_ERROR_EBADF &&
|
||||
FileSystem::KernelLseek(reader, 0, 1) == 0 &&
|
||||
FileSystem::KernelClose(reader) == OK,
|
||||
"writev rejects a read-only descriptor without moving it");
|
||||
|
||||
const int append = FileSystem::KernelOpen(Path, 0xa, 0);
|
||||
Check(append >= 3 && FileSystem::KernelLseek(append, 1, 0) == 1 &&
|
||||
writev(append, buffers.data(), buffers.size()) == 5 &&
|
||||
FileSystem::KernelLseek(append, 0, 1) == 11 &&
|
||||
FileSystem::KernelLseek(append, 1, 0) == 1 &&
|
||||
pwritev(append, patch.data(), patch.size(), 0) == 2 &&
|
||||
FileSystem::KernelLseek(append, 0, 1) == 1,
|
||||
"append vectored writes target EOF while positional writes preserve the cursor");
|
||||
std::array<char, 13> appended {};
|
||||
Check(FileSystem::KernelPread(fd, appended.data(), appended.size(), 0) == appended.size() &&
|
||||
std::memcmp(appended.data(), "_12cDEabcDE12", appended.size()) == 0,
|
||||
"append writes retain the original contents and ordered segments");
|
||||
Check(FileSystem::KernelClose(append) == OK && FileSystem::KernelClose(fd) == OK,
|
||||
"close vectored write fixtures");
|
||||
}
|
||||
|
||||
void TestAioBatches() {
|
||||
namespace Kernel = Libs::LibKernel;
|
||||
Loader::SymbolDatabase symbols;
|
||||
@@ -1969,6 +2045,7 @@ int main(int, char**) {
|
||||
FileSystem::Mount(temporary.Path(), "/savedata0");
|
||||
TestSaveOpenVisibility();
|
||||
TestSaveTruncate();
|
||||
TestSaveWritev();
|
||||
TestAioBatches();
|
||||
CheckSaveRename(temporary.Path(), "first-save");
|
||||
CheckSaveRename(temporary.Path(), "replacement-save");
|
||||
|
||||
Reference in New Issue
Block a user