diff options
| author | Stefan Boberg <[email protected]> | 2021-09-25 21:21:16 +0200 |
|---|---|---|
| committer | Stefan Boberg <[email protected]> | 2021-09-25 21:21:16 +0200 |
| commit | e96d3f99687cdd57d6d2b342b465928c72e84c27 (patch) | |
| tree | 0f34da12a34c7dddac09eca6281e1d29e815182e | |
| parent | Ensure FILE_RENAME_INFO structure allocation is freed also if FileCasStrategy... (diff) | |
| download | zen-e96d3f99687cdd57d6d2b342b465928c72e84c27.tar.xz zen-e96d3f99687cdd57d6d2b342b465928c72e84c27.zip | |
Added TemporaryFile implementation, provides a simple abstraction around temporary files
| -rw-r--r-- | zenstore/basicfile.cpp | 84 | ||||
| -rw-r--r-- | zenstore/include/zenstore/basicfile.h | 38 |
2 files changed, 119 insertions, 3 deletions
diff --git a/zenstore/basicfile.cpp b/zenstore/basicfile.cpp index fe54184cf..233efa11c 100644 --- a/zenstore/basicfile.cpp +++ b/zenstore/basicfile.cpp @@ -36,11 +36,16 @@ void BasicFile::Open(std::filesystem::path FileName, bool IsCreate, std::error_code& Ec) { const DWORD dwCreationDisposition = IsCreate ? CREATE_ALWAYS : OPEN_EXISTING; - const DWORD dwDesiredAccess = GENERIC_READ | GENERIC_WRITE; + DWORD dwDesiredAccess = GENERIC_READ | GENERIC_WRITE; const DWORD dwShareMode = FILE_SHARE_READ; const DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL; HANDLE hTemplateFile = nullptr; + if (IsCreate) + { + dwDesiredAccess |= DELETE; + } + HANDLE FileHandle = CreateFile(FileName.c_str(), dwDesiredAccess, dwShareMode, @@ -63,6 +68,7 @@ BasicFile::Close() if (m_FileHandle) { ::CloseHandle(m_FileHandle); + m_FileHandle = nullptr; } } @@ -158,6 +164,51 @@ BasicFile::FileSize() return uint64_t(liFileSize.QuadPart); } +////////////////////////////////////////////////////////////////////////// + +TemporaryFile::~TemporaryFile() +{ + Close(); +} + +void +TemporaryFile::Close() +{ + if (m_FileHandle) + { + // Mark file for deletion when final handle is closed + + FILE_DISPOSITION_INFO Fdi{.DeleteFile = TRUE}; + + SetFileInformationByHandle(m_FileHandle, FileDispositionInfo, &Fdi, sizeof Fdi); + + BasicFile::Close(); + } +} + +void +TemporaryFile::CreateTemporary(std::filesystem::path TempDirName, std::error_code& Ec) +{ + StringBuilder<64> TempName; + Oid::NewOid().ToString(TempName); + + m_TempPath = TempDirName / TempName.c_str(); + + const bool IsCreate = true; + + Open(m_TempPath, IsCreate, Ec); +} + +void +TemporaryFile::MoveTemporaryIntoPlace(std::filesystem::path FinalFileName, std::error_code& Ec) +{ + // We intentionally call the base class Close() since otherwise we'll end up + // deleting the temporary file + BasicFile::Close(); + + std::filesystem::rename(m_TempPath, FinalFileName, Ec); +} + #if ZEN_WITH_TESTS TEST_CASE("BasicFile") @@ -171,6 +222,37 @@ TEST_CASE("BasicFile") CHECK(File1.FileSize() == 4); } +TEST_CASE("TemporaryFile") +{ + ScopedCurrentDirectoryChange _; + + SUBCASE("DeleteOnClose") + { + TemporaryFile TmpFile; + std::error_code Ec; + TmpFile.CreateTemporary(std::filesystem::current_path(), Ec); + CHECK(!Ec); + CHECK(std::filesystem::exists(TmpFile.GetPath())); + TmpFile.Close(); + CHECK(std::filesystem::exists(TmpFile.GetPath()) == false); + } + + SUBCASE("MoveIntoPlace") + { + TemporaryFile TmpFile; + std::error_code Ec; + TmpFile.CreateTemporary(std::filesystem::current_path(), Ec); + CHECK(!Ec); + std::filesystem::path TempPath = TmpFile.GetPath(); + std::filesystem::path FinalPath = std::filesystem::current_path() / "final"; + CHECK(std::filesystem::exists(TempPath)); + TmpFile.MoveTemporaryIntoPlace(FinalPath, Ec); + CHECK(!Ec); + CHECK(std::filesystem::exists(TempPath) == false); + CHECK(std::filesystem::exists(FinalPath)); + } +} + void basicfile_forcelink() { diff --git a/zenstore/include/zenstore/basicfile.h b/zenstore/include/zenstore/basicfile.h index d4d65b366..8a11ea98a 100644 --- a/zenstore/include/zenstore/basicfile.h +++ b/zenstore/include/zenstore/basicfile.h @@ -26,6 +26,10 @@ class BasicFile public: BasicFile() = default; ~BasicFile(); + + BasicFile(const BasicFile&) = delete; + BasicFile& operator=(const BasicFile&) = delete; + void Open(std::filesystem::path FileName, bool IsCreate); void Open(std::filesystem::path FileName, bool IsCreate, std::error_code& Ec); void Close(); @@ -35,13 +39,43 @@ public: void Write(const void* Data, uint64_t Size, uint64_t FileOffset); void Flush(); uint64_t FileSize(); - void* Handle() { return m_FileHandle; } IoBuffer ReadAll(); -private: + inline void* Handle() { return m_FileHandle; } + +protected: void* m_FileHandle = nullptr; // This is either null or valid }; +/** + * Simple abstraction for a temporary file + * + * Works like a regular BasicFile but implements a simple mechanism to allow creating + * a temporary file for writing in a directory which may later be moved atomically + * into the intended location after it has been fully written to. + * + */ + +class TemporaryFile : public BasicFile +{ +public: + TemporaryFile() = default; + ~TemporaryFile(); + + TemporaryFile(const TemporaryFile&) = delete; + TemporaryFile& operator=(const TemporaryFile&) = delete; + + void Close(); + void CreateTemporary(std::filesystem::path TempDirName, std::error_code& Ec); + void MoveTemporaryIntoPlace(std::filesystem::path FinalFileName, std::error_code& Ec); + const std::filesystem::path& GetPath() const { return m_TempPath; } + +private: + std::filesystem::path m_TempPath; + + using BasicFile::Open; +}; + ZENCORE_API void basicfile_forcelink(); } // namespace zen |