mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-11 10:06:47 +00:00
Atomic IFC file writes to prevent corruption on interrupted save (#4797)
file.write() streamed directly onto the target path, so a crash mid-write left a truncated file with dangling STEP references. Serialize to a temp file in the same directory, then atomically rename it onto the target. - New IfcUtil::path::atomic_rename_file: std::rename on POSIX, MoveFileExW with MOVEFILE_REPLACE_EXISTING on Windows. Unlike rename_file it never unlinks the destination first, so there is no window where it goes missing. - Fully in C++/swig (per aothms), so the FILE_NAME header is untouched: it comes from the model header, not the output path (verified empirically). - Temp lives next to the target so the rename stays on one filesystem. - Stream is closed before the rename (Windows cannot move an open file). - On any write error the temp is removed and the original target is intact. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
committed by
Thomas Krijnen
parent
b2d58d0b81
commit
4776bd7639
@@ -353,6 +353,17 @@ IFC_PARSE_API bool IfcUtil::path::rename_file(const std::string& old_filename, c
|
||||
return success;
|
||||
}
|
||||
|
||||
IFC_PARSE_API bool IfcUtil::path::atomic_rename_file(const std::string& old_filename, const std::string& new_filename) {
|
||||
std::wstring old_filename_w = from_utf8(old_filename);
|
||||
std::wstring new_filename_w = from_utf8(new_filename);
|
||||
// MOVEFILE_REPLACE_EXISTING makes the replace atomic on NTFS (no unlink
|
||||
// of the destination first). MOVEFILE_WRITE_THROUGH waits until the move
|
||||
// is flushed to disk before returning.
|
||||
const bool success = !!MoveFileExW(old_filename_w.c_str(), new_filename_w.c_str(),
|
||||
MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH);
|
||||
return success;
|
||||
}
|
||||
|
||||
IFC_PARSE_API bool IfcUtil::path::delete_file(const std::string& filename) {
|
||||
std::wstring filename_w = from_utf8(filename);
|
||||
const bool success = !!DeleteFileW(filename_w.c_str());
|
||||
@@ -368,6 +379,12 @@ IFC_PARSE_API bool IfcUtil::path::rename_file(const std::string& old_filename, c
|
||||
return std::rename(old_filename.c_str(), new_filename.c_str()) == 0;
|
||||
}
|
||||
|
||||
IFC_PARSE_API bool IfcUtil::path::atomic_rename_file(const std::string& old_filename, const std::string& new_filename) {
|
||||
// POSIX rename() atomically replaces an existing destination on the same
|
||||
// filesystem, so there is no window in which new_filename is missing.
|
||||
return std::rename(old_filename.c_str(), new_filename.c_str()) == 0;
|
||||
}
|
||||
|
||||
IFC_PARSE_API bool IfcUtil::path::delete_file(const std::string& filename) {
|
||||
return std::remove(filename.c_str()) != 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user