diff --git a/src/fs.h b/src/fs.h --- a/src/fs.h +++ b/src/fs.h @@ -17,6 +17,27 @@ /** Bridge operations to C stdio */ 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 + // INVALID_HANDLE_VALUE + void *hFile = (void *)-1; +#endif +}; }; // namespace fsbridge #endif // BITCOIN_FS_H diff --git a/src/fs.cpp b/src/fs.cpp --- a/src/fs.cpp +++ b/src/fs.cpp @@ -5,10 +5,95 @@ #include +#ifndef WIN32 +#include +#else +#include +#include +#endif + namespace fsbridge { FILE *fopen(const fs::path &p, const char *mode) { return ::fopen(p.string().c_str(), mode); } +#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 + } // namespace fsbridge diff --git a/src/init.cpp b/src/init.cpp --- a/src/init.cpp +++ b/src/init.cpp @@ -49,10 +49,14 @@ #include #include +#ifndef WIN32 +#include +#include +#endif + #include #include #include -#include #include #if ENABLE_ZMQ diff --git a/src/util/system.cpp b/src/util/system.cpp --- a/src/util/system.cpp +++ b/src/util/system.cpp @@ -17,7 +17,6 @@ #include #include -#include #include #include @@ -97,8 +96,7 @@ * locking, these will be held here until the global destructor 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; @@ -118,20 +116,14 @@ if (file) { fclose(file); } - - try { - auto lock = std::make_unique( - 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) { + auto lock = std::make_unique(pathLockFile); + if (!lock->TryLock()) { return error("Error while attempting to lock directory %s: %s", - directory.string(), e.what()); + 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-boost-dependencies.sh b/test/lint/lint-boost-dependencies.sh --- a/test/lint/lint-boost-dependencies.sh +++ b/test/lint/lint-boost-dependencies.sh @@ -18,7 +18,6 @@ boost/date_time/posix_time/posix_time.hpp boost/filesystem.hpp boost/filesystem/fstream.hpp - boost/interprocess/sync/file_lock.hpp boost/lexical_cast.hpp boost/multi_index/composite_key.hpp boost/multi_index/hashed_index.hpp