mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-03-15 22:21:25 +00:00
cellSaveData: add auto maintenance routine in Emu.Init()
This routine: 1) Removes junk backup directories 2) Fixes interrupted save data process in edge case This case can happen if emu terminates between two atomic renames. Also use directory renaming technique for delete op. Also rewrite recreate operation to be part of atomic process.
This commit is contained in:
parent
297016aba3
commit
f841b47b6b
@ -630,7 +630,7 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v
|
||||
}
|
||||
}
|
||||
|
||||
auto delete_save = [&](const std::string& del_path)
|
||||
auto delete_save = [&]()
|
||||
{
|
||||
strcpy_trunc(doneGet->dirName, save_entries[selected].dirName);
|
||||
doneGet->hddFreeSizeKB = 40 * 1024 * 1024 - 1; // Read explanation in cellHddGameCheck
|
||||
@ -638,31 +638,38 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v
|
||||
doneGet->excResult = CELL_OK;
|
||||
std::memset(doneGet->reserved, 0, sizeof(doneGet->reserved));
|
||||
|
||||
const fs::dir _dir{del_path};
|
||||
const std::string old_path = base_dir + ".backup_" + save_entries[selected].dirName + "/";
|
||||
const std::string del_path = base_dir + save_entries[selected].dirName + "/";
|
||||
|
||||
const fs::dir _dir(del_path);
|
||||
|
||||
for (auto&& file : _dir)
|
||||
{
|
||||
if (!file.is_directory)
|
||||
{
|
||||
doneGet->sizeKB += static_cast<s32>(::align(file.size, 4096));
|
||||
|
||||
if (!fs::remove_file(del_path + file.name))
|
||||
{
|
||||
doneGet->excResult = CELL_SAVEDATA_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!_dir)
|
||||
if (_dir)
|
||||
{
|
||||
// Remove old backup
|
||||
fs::remove_all(old_path);
|
||||
|
||||
// Remove savedata by renaming
|
||||
if (!vfs::host::rename(del_path, old_path, false))
|
||||
{
|
||||
fmt::throw_exception("Failed to move directory %s (%s)", del_path, fs::g_tls_error);
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
fs::remove_all(old_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
doneGet->excResult = CELL_SAVEDATA_ERROR_NODATA;
|
||||
}
|
||||
|
||||
if (!doneGet->excResult && !fs::remove_dir(del_path))
|
||||
{
|
||||
doneGet->excResult = CELL_SAVEDATA_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
funcDone(ppu, result, doneGet);
|
||||
};
|
||||
|
||||
@ -701,7 +708,7 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v
|
||||
|
||||
if (operation == SAVEDATA_OP_LIST_DELETE)
|
||||
{
|
||||
delete_save(base_dir + save_entries[selected].dirName + '/');
|
||||
delete_save();
|
||||
|
||||
if (result->result < 0)
|
||||
{
|
||||
@ -777,7 +784,7 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v
|
||||
|
||||
if (operation == SAVEDATA_OP_FIXED_DELETE)
|
||||
{
|
||||
delete_save(base_dir + save_entries[selected].dirName + '/');
|
||||
delete_save();
|
||||
|
||||
if (result->result < 0)
|
||||
{
|
||||
@ -813,6 +820,7 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v
|
||||
|
||||
psf::registry psf = psf::load_object(fs::file(dir_path + "PARAM.SFO"));
|
||||
bool has_modified = false;
|
||||
bool recreated = false;
|
||||
|
||||
lv2_sleep(ppu, 250);
|
||||
|
||||
@ -1044,15 +1052,21 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v
|
||||
return {CELL_SAVEDATA_ERROR_PARAM, "50"};
|
||||
}
|
||||
|
||||
// TODO: Only delete data, not owner info
|
||||
for (const auto& entry : fs::dir(dir_path))
|
||||
// Clear secure file info
|
||||
for (auto it = psf.cbegin(), end = psf.cend(); it != end;)
|
||||
{
|
||||
if (!entry.is_directory)
|
||||
{
|
||||
fs::remove_file(dir_path + entry.name);
|
||||
}
|
||||
if (it->first[0] == '*')
|
||||
it = psf.erase(it);
|
||||
else
|
||||
it++;
|
||||
}
|
||||
|
||||
// Clear order info
|
||||
blist.clear();
|
||||
|
||||
// Set to not load files
|
||||
has_modified = true;
|
||||
recreated = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1081,7 +1095,7 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v
|
||||
// First, preload all files (TODO: beware of possible lag, although it should be insignificant)
|
||||
for (auto&& entry : fs::dir(dir_path))
|
||||
{
|
||||
if (!entry.is_directory)
|
||||
if (!recreated && !entry.is_directory)
|
||||
{
|
||||
// Read file into a vector and make a memory file
|
||||
all_times.emplace(entry.name, std::make_pair(entry.atime, entry.mtime));
|
||||
@ -1319,10 +1333,10 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v
|
||||
}
|
||||
|
||||
// Remove old backup
|
||||
fs::remove_all(old_path, false);
|
||||
fs::remove_all(old_path);
|
||||
|
||||
// Backup old savedata
|
||||
if (!vfs::host::rename(dir_path, old_path, true))
|
||||
if (!vfs::host::rename(dir_path, old_path, false))
|
||||
{
|
||||
fmt::throw_exception("Failed to move directory %s (%s)", dir_path, fs::g_tls_error);
|
||||
}
|
||||
|
@ -355,6 +355,8 @@ void Emulator::Init()
|
||||
}
|
||||
};
|
||||
|
||||
const std::string save_path = dev_hdd0 + "home/" + m_usr + "/savedata/";
|
||||
|
||||
if (g_cfg.vfs.init_dirs)
|
||||
{
|
||||
make_path_verbose(dev_hdd0);
|
||||
@ -367,7 +369,7 @@ void Emulator::Init()
|
||||
make_path_verbose(dev_hdd0 + "home/");
|
||||
make_path_verbose(dev_hdd0 + "home/" + m_usr + "/");
|
||||
make_path_verbose(dev_hdd0 + "home/" + m_usr + "/exdata/");
|
||||
make_path_verbose(dev_hdd0 + "home/" + m_usr + "/savedata/");
|
||||
make_path_verbose(save_path);
|
||||
make_path_verbose(dev_hdd0 + "home/" + m_usr + "/trophy/");
|
||||
|
||||
if (!fs::write_file(dev_hdd0 + "home/" + m_usr + "/localusername", fs::create + fs::excl + fs::write, "User"s))
|
||||
@ -385,6 +387,40 @@ void Emulator::Init()
|
||||
make_path_verbose(dev_hdd1 + "game/");
|
||||
}
|
||||
|
||||
// Fixup savedata
|
||||
for (const auto& entry : fs::dir(save_path))
|
||||
{
|
||||
if (entry.is_directory && entry.name.compare(0, 8, ".backup_", 8) == 0)
|
||||
{
|
||||
const std::string desired = entry.name.substr(8);
|
||||
const std::string pending = save_path + ".working_" + desired;
|
||||
|
||||
if (fs::is_dir(pending))
|
||||
{
|
||||
// Finalize interrupted saving
|
||||
if (!fs::rename(pending, save_path + desired, false))
|
||||
{
|
||||
LOG_FATAL(GENERAL, "Failed to fix save data: %s (%s)", pending, fs::g_tls_error);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_SUCCESS(GENERAL, "Fixed save data: %s", desired);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove pending backup data
|
||||
if (!fs::remove_all(save_path + entry.name))
|
||||
{
|
||||
LOG_FATAL(GENERAL, "Failed to remove save data backup: %s%s (%s)", save_path, entry.name, fs::g_tls_error);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_SUCCESS(GENERAL, "Removed save data backup: %s%s", save_path, entry.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
make_path_verbose(fs::get_cache_dir() + "shaderlog/");
|
||||
make_path_verbose(fs::get_config_dir() + "captures/");
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user