fix(server/game): write saves through a temp file
This commit is contained in:
1 parent
c16c95db94
commit
cc7ec21ab9
2 files changed
+37
-8
No files matched your search
+27
-8
@@ -175,15 +175,34 @@ std::string readFile(const std::string& path, bool* ok) {
|
||||
}
|
||||
|
||||
bool writeFile(const std::string& path, std::string_view data) {
|
||||
std::filesystem::path p(path);
|
||||
if (p.has_parent_path()) {
|
||||
std::error_code ec;
|
||||
std::filesystem::create_directories(p.parent_path(), ec);
|
||||
std::filesystem::path target(path);
|
||||
std::error_code ec;
|
||||
if (target.has_parent_path()) std::filesystem::create_directories(target.parent_path(), ec);
|
||||
|
||||
auto writeTo = [data](const std::filesystem::path& file) {
|
||||
std::ofstream out(file, std::ios::binary | std::ios::trunc);
|
||||
if (!out) return false;
|
||||
out.write(data.data(), static_cast<std::streamsize>(data.size()));
|
||||
out.close();
|
||||
return !out.fail();
|
||||
};
|
||||
|
||||
// Written beside the target and moved over it in one step, so a crash mid-write leaves
|
||||
// the old file whole instead of cut short.
|
||||
std::filesystem::path temp = target;
|
||||
temp += ".tmp";
|
||||
if (writeTo(temp)) {
|
||||
#ifdef _WIN32
|
||||
if (MoveFileExW(temp.c_str(), target.c_str(), MOVEFILE_REPLACE_EXISTING)) return true;
|
||||
#else
|
||||
std::filesystem::rename(temp, target, ec);
|
||||
if (!ec) return true;
|
||||
#endif
|
||||
}
|
||||
std::ofstream out(path, std::ios::binary | std::ios::trunc);
|
||||
if (!out) return false;
|
||||
out.write(data.data(), static_cast<std::streamsize>(data.size()));
|
||||
return out.good();
|
||||
std::filesystem::remove(temp, ec);
|
||||
// Something is holding the target in a way that blocks the move; a direct write still
|
||||
// beats dropping the save.
|
||||
return writeTo(target);
|
||||
}
|
||||
|
||||
std::string executableDir() {
|
||||
|
||||
@@ -311,6 +311,15 @@ void testLogFormat() {
|
||||
"the banner names its rows");
|
||||
}
|
||||
|
||||
void testSafeWrite() {
|
||||
const std::string path = "build/test-safe-write.json";
|
||||
check(util::writeFile(path, "first"), "a file writes");
|
||||
check(util::writeFile(path, "second, and longer"), "and overwrites");
|
||||
bool ok = false;
|
||||
check(util::readFile(path, &ok) == "second, and longer" && ok, "with the new content whole");
|
||||
check(!util::fileExists(path + ".tmp"), "leaving no temp file behind");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main() {
|
||||
@@ -324,6 +333,7 @@ int main() {
|
||||
testLineupCmdIds();
|
||||
testIdleTimeout();
|
||||
testLogFormat();
|
||||
testSafeWrite();
|
||||
testFillPresence();
|
||||
testLargeMessage();
|
||||
testAgainstProtoc();
|
||||
|
||||
Reference in new issue
Block a user