fix(savedata): preserve memory on write failure

This commit is contained in:
Sanjaysrinivas 2026-10-07 14:43:23 +02:00
parent 4764e59c37
commit c4cfb8e23d
2 changed files with 67 additions and 9 deletions

View File

@ -560,8 +560,8 @@ static int setSaveDataMemory2(const SaveDataMemorySet2* set_param) {
}
}
if (n != 0) {
std::fstream f(path, std::ios::binary | std::ios::in | std::ios::out);
if (!f) {
std::vector<char> memory;
if (!read_file_all(path, memory) || memory.size() != size) {
return SAVE_DATA_ERROR_INTERNAL;
}
for (std::uint32_t i = 0; i < n; i++) {
@ -569,11 +569,9 @@ static int setSaveDataMemory2(const SaveDataMemorySet2* set_param) {
if (d.buf_size == 0) {
continue;
}
f.seekp(static_cast<std::streamoff>(d.offset));
f.write(static_cast<const char*>(d.buf), static_cast<std::streamsize>(d.buf_size));
std::memcpy(memory.data() + d.offset, d.buf, d.buf_size);
}
f.flush();
if (!f) {
if (!write_file_replace(path, memory)) {
return SAVE_DATA_ERROR_INTERNAL;
}
}

View File

@ -1,12 +1,19 @@
#include "SceTypes.hpp"
#include <algorithm>
#include <array>
#include <chrono>
#include <cstdio>
#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <iterator>
#include <limits>
#include <string>
#include <vector>
#ifndef _WIN32
#include <csignal>
#include <sys/resource.h>
#endif
extern "C" {
int APS5_VABI sceSaveDataInitialize3(const void*);
@ -14,9 +21,13 @@ int APS5_VABI sceSaveDataTerminate();
int APS5_VABI sceSaveDataSetSaveDataMemory2(const SaveDataMemorySet2*);
}
static void Require(bool value) {
if (!value) std::abort();
static void Check(bool value, int line) {
if (!value) {
std::fprintf(stderr, "Save-data memory check failed at line %d\n", line);
std::abort();
}
}
#define Require(value) Check((value), __LINE__)
static std::vector<char> Read(const std::filesystem::path& path) {
std::ifstream file(path, std::ios::binary);
@ -31,8 +42,14 @@ int main() {
Require(std::filesystem::create_directory(root));
std::filesystem::current_path(root);
const auto path = std::filesystem::path("_sd_mem/u7531/slot0.param");
const auto memoryPath = std::filesystem::path("_sd_mem/u7531/slot0.bin");
std::filesystem::create_directories(path.parent_path());
{ std::ofstream file("_sd_mem/u7531/slot0.bin"); file << "save"; Require(static_cast<bool>(file)); }
const std::vector<char> memoryOriginal(32, 's');
{
std::ofstream file(memoryPath, std::ios::binary);
file.write(memoryOriginal.data(), static_cast<std::streamsize>(memoryOriginal.size()));
Require(static_cast<bool>(file));
}
Require(sceSaveDataInitialize3(nullptr) == 0);
Require(sceSaveDataInitialize3(nullptr) == 0);
Require(sceSaveDataTerminate() == 0);
@ -55,6 +72,49 @@ int main() {
Require(Read(path) == std::vector<char>(bytes, bytes + sizeof(param)));
set.param = nullptr;
Require(sceSaveDataSetSaveDataMemory2(&set) == 0);
std::array<char, 8> first{'a', '\0', 'b', 'c', 'd', 'e', 'f', 'g'};
std::array<char, 8> second{'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o'};
std::array<SaveDataMemoryData, 3> data{{
{first.data(), first.size(), 4},
{second.data(), second.size(), 24},
{nullptr, 0, std::numeric_limits<std::size_t>::max()}
}};
set.data = data.data();
set.data_num = static_cast<std::uint32_t>(data.size());
data[1].offset = memoryOriginal.size();
Require(sceSaveDataSetSaveDataMemory2(&set) == static_cast<int>(0x809F0000u));
Require(Read(memoryPath) == memoryOriginal);
data[1].offset = 24;
const auto memoryTemporary = memoryPath.string() + ".tmp";
Require(std::filesystem::create_directory(memoryTemporary));
Require(sceSaveDataSetSaveDataMemory2(&set) == static_cast<int>(0x809F000Bu));
Require(Read(memoryPath) == memoryOriginal);
Require(!std::filesystem::exists(memoryTemporary));
#ifndef _WIN32
rlimit previousLimit{};
Require(getrlimit(RLIMIT_FSIZE, &previousLimit) == 0);
auto writeLimit = previousLimit;
writeLimit.rlim_cur = 16;
const auto previousHandler = std::signal(SIGXFSZ, SIG_IGN);
Require(previousHandler != SIG_ERR);
Require(setrlimit(RLIMIT_FSIZE, &writeLimit) == 0);
const int writeStatus = sceSaveDataSetSaveDataMemory2(&set);
Require(setrlimit(RLIMIT_FSIZE, &previousLimit) == 0);
Require(std::signal(SIGXFSZ, previousHandler) != SIG_ERR);
Require(writeStatus == static_cast<int>(0x809F000Bu));
Require(Read(memoryPath) == memoryOriginal);
Require(!std::filesystem::exists(memoryTemporary));
#endif
Require(sceSaveDataSetSaveDataMemory2(&set) == 0);
auto memoryExpected = memoryOriginal;
std::copy(first.begin(), first.end(), memoryExpected.begin() + 4);
std::copy(second.begin(), second.end(), memoryExpected.begin() + 24);
Require(Read(memoryPath) == memoryExpected);
first.fill('z');
set.data_num = 0;
Require(sceSaveDataSetSaveDataMemory2(&set) == 0);
std::copy(first.begin(), first.end(), memoryExpected.begin() + 4);
Require(Read(memoryPath) == memoryExpected);
Require(sceSaveDataTerminate() == 0);
std::filesystem::current_path(previous);
std::filesystem::remove_all(root);