mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
Move old data/profiles to .bak before writing
The hypothesis for this patch is that the operating system has not actually finished writing the file to disk when moving the tmp file into place. The move operation is atomic, but the write is not, even when flushed or using unbuffered IO. So we reorder these operations, make the old save .bak atomically and write the new save in place. I doubt saving this backup will actually be useful given the frequency of saves in the game, but at the very least it leaves _some_ backup in place in the event of failure.
This commit is contained in:
parent
c44790749d
commit
53832edafc
2 changed files with 36 additions and 23 deletions
|
|
@ -287,15 +287,28 @@ void srb2::save_ng_gamedata()
|
|||
|
||||
std::string gamedataname_s {gamedatafilename};
|
||||
fs::path savepath {fmt::format("{}/{}", srb2home, gamedataname_s)};
|
||||
int random_number = rand();
|
||||
fs::path tmpsavepath {fmt::format("{}/{}_{}.tmp", srb2home, gamedataname_s, random_number)};
|
||||
fs::path baksavepath {fmt::format("{}/{}.bak", srb2home, gamedataname_s)};
|
||||
|
||||
json ngdata_json = ng;
|
||||
|
||||
|
||||
if (fs::exists(savepath))
|
||||
{
|
||||
try
|
||||
{
|
||||
std::string tmpsavepathstring = tmpsavepath.string();
|
||||
srb2::io::FileStream file {tmpsavepathstring, srb2::io::FileStreamMode::kWrite};
|
||||
fs::rename(savepath, baksavepath);
|
||||
}
|
||||
catch (const fs::filesystem_error& ex)
|
||||
{
|
||||
CONS_Alert(CONS_ERROR, "Failed to record backup save. Not attempting to save. %s\n", ex.what());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
std::string savepathstring = savepath.string();
|
||||
srb2::io::FileStream file {savepathstring, srb2::io::FileStreamMode::kWrite};
|
||||
|
||||
// The header is necessary to validate during loading.
|
||||
srb2::io::write(static_cast<uint32_t>(GD_VERSION_MAJOR), file); // major
|
||||
|
|
@ -308,21 +321,11 @@ void srb2::save_ng_gamedata()
|
|||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
CONS_Alert(CONS_ERROR, "NG Gamedata save failed: %s\n", ex.what());
|
||||
CONS_Alert(CONS_ERROR, "NG Gamedata save failed. Check directory for a ringdata.dat.bak. %s\n", ex.what());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
CONS_Alert(CONS_ERROR, "NG Gamedata save failed\n");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Now that the save is written successfully, move it over the old save
|
||||
fs::rename(tmpsavepath, savepath);
|
||||
}
|
||||
catch (const fs::filesystem_error& ex)
|
||||
{
|
||||
CONS_Alert(CONS_ERROR, "NG Gamedata save succeeded but did not replace old save successfully: %s\n", ex.what());
|
||||
CONS_Alert(CONS_ERROR, "NG Gamedata save failed. Check directory for a ringdata.dat.bak.\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -319,12 +319,24 @@ void PR_SaveProfiles(void)
|
|||
std::vector<uint8_t> ubjson = json::to_ubjson(ng);
|
||||
|
||||
std::string realpath = fmt::format("{}/{}", srb2home, PROFILESFILE);
|
||||
int random_number = rand();
|
||||
std::string tmppath = fmt::format("{}_{}.tmp", realpath, random_number);
|
||||
std::string bakpath = fmt::format("{}.bak", realpath);
|
||||
|
||||
if (fs::exists(realpath))
|
||||
{
|
||||
try
|
||||
{
|
||||
fs::rename(realpath, bakpath);
|
||||
}
|
||||
catch (const fs::filesystem_error& ex)
|
||||
{
|
||||
CONS_Alert(CONS_ERROR, "Failed to record profiles backup. Not attempting to save profiles. %s\n", ex.what());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
io::FileStream file {tmppath, io::FileStreamMode::kWrite};
|
||||
io::FileStream file {realpath, io::FileStreamMode::kWrite};
|
||||
|
||||
io::write(static_cast<uint32_t>(0x52494E47), file, io::Endian::kBE); // "RING"
|
||||
io::write(static_cast<uint32_t>(0x5052464C), file, io::Endian::kBE); // "PRFL"
|
||||
|
|
@ -334,16 +346,14 @@ void PR_SaveProfiles(void)
|
|||
io::write(static_cast<uint8_t>(0), file); // reserved4
|
||||
io::write_exact(file, tcb::as_bytes(tcb::make_span(ubjson)));
|
||||
file.close();
|
||||
|
||||
fs::rename(tmppath, realpath);
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
I_Error("Couldn't save profiles. Are you out of Disk space / playing in a protected folder?\n\nException: %s", ex.what());
|
||||
I_Error("Couldn't save profiles. Are you out of Disk space / playing in a protected folder? Check directory for a ringprofiles.prf.bak if the profiles file is corrupt.\n\nException: %s", ex.what());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
I_Error("Couldn't save profiles. Are you out of Disk space / playing in a protected folder?");
|
||||
I_Error("Couldn't save profiles. Are you out of Disk space / playing in a protected folder? Check directory for a ringprofiles.prf.bak if the profiles file is corrupt.");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue