From 1661a472b8245eb4588fedbf19c9ed07a41e7602 Mon Sep 17 00:00:00 2001 From: Chun Kuan Lee Date: Wed, 25 Jul 2018 17:33:22 +0800 Subject: add unicode compatible file_lock for Windows boost::interprocess::file_lock cannot open the files that contain characters which cannot be parsed by the user's code page on Windows. This commit add a new class to handle those specific file for Windows. --- src/fs.cpp | 85 ++++++++++++++++++++++++++++++++++++++++++++++ src/fs.h | 20 +++++++++++ src/init.cpp | 2 +- src/util.cpp | 22 +++++------- test/lint/lint-includes.sh | 1 - 5 files changed, 114 insertions(+), 16 deletions(-) diff --git a/src/fs.cpp b/src/fs.cpp index 570ed3e2ee..e7d06e45ab 100644 --- a/src/fs.cpp +++ b/src/fs.cpp @@ -1,5 +1,12 @@ #include +#ifndef WIN32 +#include +#else +#include +#include +#endif + namespace fsbridge { FILE *fopen(const fs::path& p, const char *mode) @@ -12,4 +19,82 @@ FILE *freopen(const fs::path& p, const char *mode, FILE *stream) return ::freopen(p.string().c_str(), mode, stream); } +#ifndef WIN32 + +static std::string GetErrorReason() { + return std::strerror(errno); +} + +FileLock::FileLock(const fs::path& file) +{ + fd = open(file.string().c_str(), O_RDWR); + if (fd == -1) { + reason = GetErrorReason(); + } +} + +FileLock::~FileLock() +{ + if (fd != -1) { + close(fd); + } +} + +bool FileLock::TryLock() +{ + if (fd == -1) { + return false; + } + struct flock lock; + lock.l_type = F_WRLCK; + lock.l_whence = SEEK_SET; + lock.l_start = 0; + lock.l_len = 0; + if (fcntl(fd, F_SETLK, &lock) == -1) { + reason = GetErrorReason(); + return false; + } + return true; +} +#else + +static std::string GetErrorReason() { + wchar_t* err; + FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast(&err), 0, nullptr); + std::wstring err_str(err); + LocalFree(err); + return std::wstring_convert>().to_bytes(err_str); +} + +FileLock::FileLock(const fs::path& file) +{ + hFile = CreateFileW(file.wstring().c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + if (hFile == INVALID_HANDLE_VALUE) { + reason = GetErrorReason(); + } +} + +FileLock::~FileLock() +{ + if (hFile != INVALID_HANDLE_VALUE) { + CloseHandle(hFile); + } +} + +bool FileLock::TryLock() +{ + if (hFile == INVALID_HANDLE_VALUE) { + return false; + } + _OVERLAPPED overlapped = {0}; + if (!LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY, 0, 0, 0, &overlapped)) { + reason = GetErrorReason(); + return false; + } + return true; +} +#endif + } // fsbridge diff --git a/src/fs.h b/src/fs.h index abb4be254b..e3ff51604d 100644 --- a/src/fs.h +++ b/src/fs.h @@ -19,6 +19,26 @@ namespace fs = boost::filesystem; namespace fsbridge { FILE *fopen(const fs::path& p, const char *mode); FILE *freopen(const fs::path& p, const char *mode, FILE *stream); + + class FileLock + { + public: + FileLock() = delete; + FileLock(const FileLock&) = delete; + FileLock(FileLock&&) = delete; + explicit FileLock(const fs::path& file); + ~FileLock(); + bool TryLock(); + std::string GetReason() { return reason; } + + private: + std::string reason; +#ifndef WIN32 + int fd = -1; +#else + void* hFile = (void*)-1; // INVALID_HANDLE_VALUE +#endif + }; }; #endif // BITCOIN_FS_H diff --git a/src/init.cpp b/src/init.cpp index 2131a6adc0..bd330459f6 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -51,13 +51,13 @@ #ifndef WIN32 #include +#include #endif #include #include #include #include -#include #include #include diff --git a/src/util.cpp b/src/util.cpp index 1aab85264f..55b09dcff8 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -71,7 +71,6 @@ #include #endif -#include #include #include #include @@ -139,7 +138,7 @@ instance_of_cinit; * cleans them up and thus automatically unlocks them, or ReleaseDirectoryLocks * is called. */ -static std::map> dir_locks; +static std::map> dir_locks; /** Mutex to protect dir_locks. */ static std::mutex cs_dir_locks; @@ -156,18 +155,13 @@ bool LockDirectory(const fs::path& directory, const std::string lockfile_name, b // Create empty lock file if it doesn't exist. FILE* file = fsbridge::fopen(pathLockFile, "a"); if (file) fclose(file); - - try { - auto lock = MakeUnique(pathLockFile.string().c_str()); - if (!lock->try_lock()) { - return false; - } - if (!probe_only) { - // Lock successful and we're not just probing, put it into the map - dir_locks.emplace(pathLockFile.string(), std::move(lock)); - } - } catch (const boost::interprocess::interprocess_exception& e) { - return error("Error while attempting to lock directory %s: %s", directory.string(), e.what()); + auto lock = MakeUnique(pathLockFile); + if (!lock->TryLock()) { + return error("Error while attempting to lock directory %s: %s", directory.string(), lock->GetReason()); + } + if (!probe_only) { + // Lock successful and we're not just probing, put it into the map + dir_locks.emplace(pathLockFile.string(), std::move(lock)); } return true; } diff --git a/test/lint/lint-includes.sh b/test/lint/lint-includes.sh index 8f7a1fd76b..a38ce6f4cf 100755 --- a/test/lint/lint-includes.sh +++ b/test/lint/lint-includes.sh @@ -57,7 +57,6 @@ EXPECTED_BOOST_INCLUDES=( boost/filesystem.hpp boost/filesystem/detail/utf8_codecvt_facet.hpp boost/filesystem/fstream.hpp - boost/interprocess/sync/file_lock.hpp boost/multi_index/hashed_index.hpp boost/multi_index/ordered_index.hpp boost/multi_index/sequenced_index.hpp -- cgit v1.2.3