Make WriteFile replace rather than overwrite

This preserves old file if writing the new one fails for some reason.
This commit is contained in:
Tamás Bálint Misius 2023-01-19 16:51:23 +01:00
parent 163203b321
commit a7d8ecc6e3
No known key found for this signature in database
GPG Key ID: 5B472A12F6ECA9F2
3 changed files with 41 additions and 8 deletions

View File

@ -1,5 +1,6 @@
#include "Platform.h"
#include "resource.h"
#include "tpt-rand.h"
#include <memory>
#include <cstring>
#include <fstream>
@ -186,9 +187,14 @@ bool RemoveFile(ByteString filename)
#endif
}
bool RenameFile(ByteString filename, ByteString newFilename)
bool RenameFile(ByteString filename, ByteString newFilename, bool replace)
{
#ifdef WIN
if (replace)
{
// TODO: we rely on errno but errors from this are available through GetLastError(); fix
return MoveFileExW(WinWiden(filename).c_str(), WinWiden(newFilename).c_str(), MOVEFILE_REPLACE_EXISTING);
}
return _wrename(WinWiden(filename).c_str(), WinWiden(newFilename).c_str()) == 0;
#else
return rename(filename.c_str(), newFilename.c_str()) == 0;
@ -335,16 +341,43 @@ bool ReadFile(std::vector<char> &fileData, ByteString filename)
return true;
}
bool WriteFile(std::vector<char> fileData, ByteString filename, bool replaceAtomically)
bool WriteFile(const std::vector<char> &fileData, ByteString filename)
{
// TODO: replaceAtomically
std::ofstream f(filename, std::ios::binary);
if (f) f.write(&fileData[0], fileData.size());
auto replace = FileExists(filename);
auto writeFileName = filename;
if (replace)
{
while (true)
{
writeFileName = ByteString::Build(filename, ".temp.", random_gen() % 100000);
if (!FileExists(writeFileName))
{
break;
}
}
}
std::ofstream f(writeFileName, std::ios::binary);
if (f)
{
f.write(&fileData[0], fileData.size());
}
if (!f)
{
std::cerr << "WriteFile: " << filename << ": " << strerror(errno) << std::endl;
if (replace)
{
RemoveFile(writeFileName);
}
return false;
}
if (replace)
{
if (!RenameFile(writeFileName, filename, true))
{
RemoveFile(writeFileName);
return false;
}
}
return true;
}

View File

@ -21,7 +21,7 @@ namespace Platform
* @return true on success
*/
bool RemoveFile(ByteString filename);
bool RenameFile(ByteString filename, ByteString newFilename);
bool RenameFile(ByteString filename, ByteString newFilename, bool replace = false);
/**
* @return true on success
@ -35,7 +35,7 @@ namespace Platform
std::vector<ByteString> DirectorySearch(ByteString directory, ByteString search, std::vector<ByteString> extensions);
bool ReadFile(std::vector<char> &fileData, ByteString filename);
bool WriteFile(std::vector<char> fileData, ByteString filename, bool replaceAtomically = false); // TODO: Revisit call sites, remove default.
bool WriteFile(const std::vector<char> &fileData, ByteString filename);
ByteString WinNarrow(const std::wstring &source);
std::wstring WinWiden(const ByteString &source);

View File

@ -47,7 +47,7 @@ void Prefs::Write()
Json::StreamWriterBuilder wbuilder;
wbuilder["indentation"] = "\t";
ByteString data = Json::writeString(wbuilder, root);
if (!Platform::WriteFile(std::vector<char>(data.begin(), data.end()), path, true))
if (!Platform::WriteFile(std::vector<char>(data.begin(), data.end()), path))
{
return;
}