aboutsummaryrefslogtreecommitdiff
path: root/src/zencore/basicfile.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/zencore/basicfile.cpp')
-rw-r--r--src/zencore/basicfile.cpp117
1 files changed, 117 insertions, 0 deletions
diff --git a/src/zencore/basicfile.cpp b/src/zencore/basicfile.cpp
index 9dcf7663a..fdf742261 100644
--- a/src/zencore/basicfile.cpp
+++ b/src/zencore/basicfile.cpp
@@ -3,6 +3,7 @@
#include <zencore/basicfile.h>
#include <zencore/compactbinary.h>
+#include <zencore/compactbinarybuilder.h>
#include <zencore/except.h>
#include <zencore/filesystem.h>
#include <zencore/fmtutils.h>
@@ -608,6 +609,67 @@ LockFile::Update(CbObject Payload, std::error_code& Ec)
BasicFile::Write(Payload.GetBuffer(), 0, Ec);
}
+bool
+LockFile::IsHeldLive(const std::filesystem::path& FileName, bool AttemptCleanup)
+{
+#if ZEN_PLATFORM_WINDOWS
+ if (AttemptCleanup)
+ {
+ if (DeleteFileW(FileName.c_str()))
+ {
+ return false;
+ }
+ const DWORD Err = ::GetLastError();
+ if (Err == ERROR_FILE_NOT_FOUND || Err == ERROR_PATH_NOT_FOUND)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ HANDLE Handle = CreateFileW(FileName.c_str(),
+ DELETE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ nullptr,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ nullptr);
+ if (Handle != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(Handle);
+ return false;
+ }
+ const DWORD Err = ::GetLastError();
+ if (Err == ERROR_FILE_NOT_FOUND || Err == ERROR_PATH_NOT_FOUND)
+ {
+ return false;
+ }
+ return true;
+#else
+ int Fd = open(FileName.c_str(), O_RDONLY | O_CLOEXEC);
+ if (Fd < 0)
+ {
+ if (errno == ENOENT)
+ {
+ return false;
+ }
+ return true;
+ }
+ if (flock(Fd, LOCK_EX | LOCK_NB) == 0)
+ {
+ if (AttemptCleanup)
+ {
+ unlink(FileName.c_str());
+ }
+ flock(Fd, LOCK_UN);
+ close(Fd);
+ return false;
+ }
+ close(Fd);
+ return true;
+#endif
+}
+
//////////////////////////////////////////////////////////////////////////
BasicFileBuffer::BasicFileBuffer(BasicFile& Base, uint64_t BufferSize)
@@ -947,6 +1009,61 @@ TEST_CASE("TemporaryFile")
}
}
+TEST_CASE("LockFile.IsHeldLive")
+{
+ ScopedCurrentDirectoryChange _;
+ const std::filesystem::path Path = std::filesystem::current_path() / "heldlive.lock";
+
+ SUBCASE("Nonexistent")
+ {
+ std::error_code Ec;
+ RemoveFile(Path, Ec);
+ CHECK(LockFile::IsHeldLive(Path, /*AttemptCleanup*/ true) == false);
+ CHECK(LockFile::IsHeldLive(Path, /*AttemptCleanup*/ false) == false);
+ CHECK(IsFile(Path) == false);
+ }
+
+ SUBCASE("Live holder")
+ {
+ LockFile Lock;
+ std::error_code Ec;
+ Lock.Create(Path, CbObjectWriter().Save(), Ec);
+ CHECK(!Ec);
+ CHECK(IsFile(Path));
+
+ CHECK(LockFile::IsHeldLive(Path, /*AttemptCleanup*/ false) == true);
+ CHECK(IsFile(Path));
+ CHECK(LockFile::IsHeldLive(Path, /*AttemptCleanup*/ true) == true);
+ CHECK(IsFile(Path));
+ }
+
+ SUBCASE("Stale file, cleanup")
+ {
+ {
+ BasicFile File;
+ File.Open(Path, BasicFile::Mode::kTruncate);
+ File.Write("x", 1, 0);
+ }
+ CHECK(IsFile(Path));
+ CHECK(LockFile::IsHeldLive(Path, /*AttemptCleanup*/ true) == false);
+ CHECK(IsFile(Path) == false);
+ }
+
+ SUBCASE("Stale file, no cleanup")
+ {
+ {
+ BasicFile File;
+ File.Open(Path, BasicFile::Mode::kTruncate);
+ File.Write("x", 1, 0);
+ }
+ CHECK(IsFile(Path));
+ CHECK(LockFile::IsHeldLive(Path, /*AttemptCleanup*/ false) == false);
+ CHECK(IsFile(Path));
+ std::error_code Ec;
+ RemoveFile(Path, Ec);
+ }
+}
+
TEST_CASE("BasicFileBuffer")
{
ScopedCurrentDirectoryChange _;