diff options
Diffstat (limited to 'src/zencore/basicfile.cpp')
| -rw-r--r-- | src/zencore/basicfile.cpp | 117 |
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 _; |