aboutsummaryrefslogtreecommitdiff
path: root/zenstore/basicfile.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'zenstore/basicfile.cpp')
-rw-r--r--zenstore/basicfile.cpp194
1 files changed, 171 insertions, 23 deletions
diff --git a/zenstore/basicfile.cpp b/zenstore/basicfile.cpp
index fe54184cf..f41f04101 100644
--- a/zenstore/basicfile.cpp
+++ b/zenstore/basicfile.cpp
@@ -35,12 +35,19 @@ BasicFile::Open(std::filesystem::path FileName, bool IsCreate)
void
BasicFile::Open(std::filesystem::path FileName, bool IsCreate, std::error_code& Ec)
{
+ Ec.clear();
+
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,
@@ -52,6 +59,8 @@ BasicFile::Open(std::filesystem::path FileName, bool IsCreate, std::error_code&
if (FileHandle == INVALID_HANDLE_VALUE)
{
Ec = zen::MakeErrorCodeFromLastError();
+
+ return;
}
m_FileHandle = FileHandle;
@@ -63,25 +72,37 @@ BasicFile::Close()
if (m_FileHandle)
{
::CloseHandle(m_FileHandle);
+ m_FileHandle = nullptr;
}
}
void
-BasicFile::Read(void* Data, uint64_t Size, uint64_t Offset)
+BasicFile::Read(void* Data, uint64_t BytesToRead, uint64_t FileOffset)
{
- OVERLAPPED Ovl{};
+ const uint64_t MaxChunkSize = 2u * 1024 * 1024 * 1024;
- Ovl.Offset = DWORD(Offset & 0xffff'ffffu);
- Ovl.OffsetHigh = DWORD(Offset >> 32);
+ while (BytesToRead)
+ {
+ const uint64_t NumberOfBytesToRead = Min(BytesToRead, MaxChunkSize);
- DWORD dwNumberOfBytesToRead = gsl::narrow<DWORD>(Size);
- DWORD dwNumberOfBytesRead = 0;
+ OVERLAPPED Ovl{};
- BOOL Success = ::ReadFile(m_FileHandle, Data, dwNumberOfBytesToRead, &dwNumberOfBytesRead, &Ovl);
+ Ovl.Offset = DWORD(FileOffset & 0xffff'ffffu);
+ Ovl.OffsetHigh = DWORD(FileOffset >> 32);
- if (!Success)
- {
- ThrowLastError("Failed to read from file '{}'"_format(zen::PathFromHandle(m_FileHandle)));
+ DWORD dwNumberOfBytesRead = 0;
+ BOOL Success = ::ReadFile(m_FileHandle, Data, DWORD(NumberOfBytesToRead), &dwNumberOfBytesRead, &Ovl);
+
+ ZEN_ASSERT(dwNumberOfBytesRead == NumberOfBytesToRead);
+
+ if (!Success)
+ {
+ ThrowLastError("Failed to read from file '{}'"_format(zen::PathFromHandle(m_FileHandle)));
+ }
+
+ BytesToRead -= NumberOfBytesToRead;
+ FileOffset += NumberOfBytesToRead;
+ Data = reinterpret_cast<uint8_t*>(Data) + NumberOfBytesToRead;
}
}
@@ -89,9 +110,7 @@ IoBuffer
BasicFile::ReadAll()
{
IoBuffer Buffer(FileSize());
-
- Read((void*)Buffer.Data(), Buffer.Size(), 0);
-
+ Read(Buffer.MutableData(), Buffer.Size(), 0);
return Buffer;
}
@@ -125,25 +144,57 @@ BasicFile::StreamByteRange(uint64_t FileOffset, uint64_t Size, std::function<voi
}
void
-BasicFile::Write(const void* Data, uint64_t Size, uint64_t Offset)
+BasicFile::Write(const void* Data, uint64_t Size, uint64_t FileOffset, std::error_code& Ec)
{
- OVERLAPPED Ovl{};
+ Ec.clear();
+
+ const uint64_t MaxChunkSize = 2u * 1024 * 1024 * 1024;
+
+ while (Size)
+ {
+ const uint64_t NumberOfBytesToWrite = Min(Size, MaxChunkSize);
+
+ OVERLAPPED Ovl{};
+
+ Ovl.Offset = DWORD(FileOffset & 0xffff'ffffu);
+ Ovl.OffsetHigh = DWORD(FileOffset >> 32);
+
+ DWORD dwNumberOfBytesWritten = 0;
- Ovl.Offset = DWORD(Offset & 0xffff'ffffu);
- Ovl.OffsetHigh = DWORD(Offset >> 32);
+ BOOL Success = ::WriteFile(m_FileHandle, Data, DWORD(NumberOfBytesToWrite), &dwNumberOfBytesWritten, &Ovl);
- DWORD dwNumberOfBytesToWrite = gsl::narrow<DWORD>(Size);
- DWORD dwNumberOfBytesWritten = 0;
+ if (!Success)
+ {
+ Ec = MakeErrorCodeFromLastError();
- BOOL Success = ::WriteFile(m_FileHandle, Data, dwNumberOfBytesToWrite, &dwNumberOfBytesWritten, &Ovl);
+ return;
+ }
- if (!Success)
+ Size -= NumberOfBytesToWrite;
+ FileOffset += NumberOfBytesToWrite;
+ Data = reinterpret_cast<const uint8_t*>(Data) + NumberOfBytesToWrite;
+ }
+}
+
+void
+BasicFile::Write(const void* Data, uint64_t Size, uint64_t Offset)
+{
+ std::error_code Ec;
+ Write(Data, Size, Offset, Ec);
+
+ if (Ec)
{
- ThrowLastError("Failed to write to file '{}'"_format(zen::PathFromHandle(m_FileHandle)));
+ throw std::system_error(Ec, "Failed to write to file '{}'"_format(zen::PathFromHandle(m_FileHandle)));
}
}
void
+BasicFile::WriteAll(IoBuffer Data, std::error_code& Ec)
+{
+ Write(Data.Data(), Data.Size(), 0, Ec);
+}
+
+void
BasicFile::Flush()
{
FlushFileBuffers(m_FileHandle);
@@ -158,6 +209,60 @@ 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")
@@ -169,6 +274,49 @@ TEST_CASE("BasicFile")
CHECK_NOTHROW(File1.Open("zonk", true));
CHECK_NOTHROW(File1.Write("abcd", 4, 0));
CHECK(File1.FileSize() == 4);
+ {
+ IoBuffer Data = File1.ReadAll();
+ CHECK(Data.Size() == 4);
+ CHECK_EQ(memcmp(Data.Data(), "abcd", 4), 0);
+ }
+ CHECK_NOTHROW(File1.Write("efgh", 4, 2));
+ CHECK(File1.FileSize() == 6);
+ {
+ IoBuffer Data = File1.ReadAll();
+ CHECK(Data.Size() == 6);
+ CHECK_EQ(memcmp(Data.Data(), "abefgh", 6), 0);
+ }
+}
+
+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