Save config to temp file and move into place

Fixes KartKrew/Kart#1020 by not trying to write over ringconfig until
we have successfully written the whole config out, then atomically
replacing the old config with the new one using C++17 FS.
This commit is contained in:
Eidolon 2024-02-17 15:40:58 -06:00
parent 796e2b9517
commit ad0ced4da0

View file

@ -20,6 +20,8 @@
#pragma GCC diagnostic ignored "-Wclobbered" #pragma GCC diagnostic ignored "-Wclobbered"
#endif #endif
#include <filesystem>
#include <unistd.h> #include <unistd.h>
#endif #endif
@ -673,7 +675,7 @@ void M_FirstLoadConfig(void)
void M_SaveConfig(const char *filename) void M_SaveConfig(const char *filename)
{ {
FILE *f; FILE *f;
char *filepath; char tmppath[2048];
// make sure not to write back the config until it's been correctly loaded // make sure not to write back the config until it's been correctly loaded
if (!gameconfig_loaded) if (!gameconfig_loaded)
@ -691,17 +693,23 @@ void M_SaveConfig(const char *filename)
// append srb2home to beginning of filename // append srb2home to beginning of filename
// but check if srb2home isn't already there, first // but check if srb2home isn't already there, first
if (!strstr(filename, srb2home)) if (!strstr(filename, srb2home))
filepath = va(pandf,srb2home, filename); {
else sprintf(tmppath, "%s" PATHSEP "%s.tmp", srb2home, filename);
filepath = Z_StrDup(filename); }
f = fopen(filepath, "w");
// change it only if valid
if (f)
strcpy(configfile, filepath);
else else
{ {
CONS_Alert(CONS_ERROR, M_GetText("Couldn't save game config file %s\n"), filepath); sprintf(tmppath, "%s", filename);
}
f = fopen(tmppath, "w");
// change it only if valid
if (f)
{
strcpy(configfile, tmppath);
}
else
{
CONS_Alert(CONS_ERROR, M_GetText("Couldn't save game config file %s\n"), tmppath);
return; return;
} }
} }
@ -713,7 +721,9 @@ void M_SaveConfig(const char *filename)
return; return;
} }
f = fopen(configfile, "w"); sprintf(tmppath, "%s.tmp", configfile);
f = fopen(tmppath, "w");
if (!f) if (!f)
{ {
CONS_Alert(CONS_ERROR, M_GetText("Couldn't save game config file %s\n"), configfile); CONS_Alert(CONS_ERROR, M_GetText("Couldn't save game config file %s\n"), configfile);
@ -738,6 +748,24 @@ void M_SaveConfig(const char *filename)
} }
fclose(f); fclose(f);
{
// Atomically replace the old config once the new one has been written.
namespace fs = std::filesystem;
fs::path tmp{tmppath};
fs::path real{configfile};
try
{
fs::rename(tmp, real);
}
catch (const fs::filesystem_error& ex)
{
CONS_Alert(CONS_ERROR, M_GetText("Failed to move temp config file to real destination\n"));
}
}
} }
// ========================================================================== // ==========================================================================