#define NOMINMAX #include "io/hdf5/hdf5_atomic_file.h" #include #include #include #include #include #include #include #include "io/hdf5/hdf5_primitives.h" #include "io/hdf5/hdf5_raii.h" namespace fesa::hdf5_internal { namespace { bool AtomicReplace(const std::filesystem::path& candidate_path, const std::filesystem::path& output_path) { const DWORD attributes = GetFileAttributesW(output_path.c_str()); if (attributes != INVALID_FILE_ATTRIBUTES) { return ReplaceFileW(output_path.c_str(), candidate_path.c_str(), nullptr, 0U, nullptr, nullptr) != FALSE; } const DWORD error = GetLastError(); if (error != ERROR_FILE_NOT_FOUND && error != ERROR_PATH_NOT_FOUND) { return false; } return MoveFileExW(candidate_path.c_str(), output_path.c_str(), MOVEFILE_WRITE_THROUGH) != FALSE; } } // namespace CandidateFileGuard::CandidateFileGuard(std::filesystem::path path) : path_{std::move(path)} {} CandidateFileGuard::~CandidateFileGuard() { if (active_) { std::error_code ignored; (void)std::filesystem::remove(path_, ignored); } } void CandidateFileGuard::Release() noexcept { active_ = false; } std::filesystem::path MakeCandidatePath( const std::filesystem::path& output_path) { static std::atomic sequence{0U}; const std::filesystem::path parent = output_path.parent_path(); for (std::size_t attempt = 0U; attempt < 1024U; ++attempt) { const std::wstring filename = L"." + output_path.filename().wstring() + L".tmp." + std::to_wstring(GetCurrentProcessId()) + L"." + std::to_wstring(sequence.fetch_add(1U)); const std::filesystem::path candidate = parent / filename; std::error_code error; const bool exists = std::filesystem::exists(candidate, error); if (error) { throw Hdf5Failure{"Unable to inspect the temporary output path."}; } if (!exists) { return candidate; } } throw Hdf5Failure{"Unable to allocate a unique temporary output path."}; } Status AtomicFinalizeValidatedCandidate( const std::filesystem::path& candidate_path, const std::filesystem::path& output_path, const Status& validation) { if (!validation.IsOk()) { std::error_code ignored; (void)std::filesystem::remove(candidate_path, ignored); return validation; } if (!AtomicReplace(candidate_path, output_path)) { std::error_code ignored; (void)std::filesystem::remove(candidate_path, ignored); return OutputFailure("hdf5-finalization-failure", "The checked temporary HDF5 file could not replace " "the final output."); } return Status::Ok(); } } // namespace fesa::hdf5_internal