diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index 35ff0e1eec9..c0c24ae98b1 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -18,6 +18,7 @@ #include #endif +#include #include namespace { @@ -52,6 +53,24 @@ void CheckUniqueFileid(const CDBEnv& env, const std::string& filename, Db& db) } } } + +bool LockEnvDirectory(const fs::path& env_path) +{ + // Make sure only a single Bitcoin process is using the wallet directory. + fs::path lock_file_path = env_path / ".lock"; + FILE* file = fsbridge::fopen(lock_file_path, "a"); // empty lock file; created if it doesn't exist. + if (file) fclose(file); + + try { + static boost::interprocess::file_lock lock(lock_file_path.string().c_str()); + if (!lock.try_lock()) { + return false; + } + } catch (const boost::interprocess::interprocess_exception& e) { + return error("Error obtaining lock on wallet directory %s: %s.", env_path.string(), e.what()); + } + return true; +} } // namespace // @@ -95,13 +114,17 @@ void CDBEnv::Close() EnvShutdown(); } -bool CDBEnv::Open(const fs::path& pathIn) +bool CDBEnv::Open(const fs::path& pathIn, bool retry) { if (fDbEnvInit) return true; boost::this_thread::interruption_point(); + if (!LockEnvDirectory(pathIn)) { + return false; + } + strPath = pathIn.string(); fs::path pathLogDir = pathIn / "database"; TryCreateDirectories(pathLogDir); @@ -134,7 +157,24 @@ bool CDBEnv::Open(const fs::path& pathIn) S_IRUSR | S_IWUSR); if (ret != 0) { dbenv->close(0); - return error("CDBEnv::Open: Error %d opening database environment: %s\n", ret, DbEnv::strerror(ret)); + LogPrintf("CDBEnv::Open: Error %d opening database environment: %s\n", ret, DbEnv::strerror(ret)); + if (retry) { + // try moving the database env out of the way + fs::path pathDatabaseBak = pathIn / strprintf("database.%d.bak", GetTime()); + try { + fs::rename(pathLogDir, pathDatabaseBak); + LogPrintf("Moved old %s to %s. Retrying.\n", pathLogDir.string(), pathDatabaseBak.string()); + } catch (const fs::filesystem_error&) { + // failure is ok (well, not really, but it's not worse than what we started with) + } + // try opening it again one more time + if (!Open(pathIn, false)) { + // if it still fails, it probably means we can't even create the database env + return false; + } + } else { + return false; + } } fDbEnvInit = true; @@ -269,25 +309,11 @@ bool CDB::VerifyEnvironment(const std::string& walletFile, const fs::path& walle return false; } - if (!bitdb.Open(walletDir)) - { - // try moving the database env out of the way - fs::path pathDatabase = walletDir / "database"; - fs::path pathDatabaseBak = walletDir / strprintf("database.%d.bak", GetTime()); - try { - fs::rename(pathDatabase, pathDatabaseBak); - LogPrintf("Moved old %s to %s. Retrying.\n", pathDatabase.string(), pathDatabaseBak.string()); - } catch (const fs::filesystem_error&) { - // failure is ok (well, not really, but it's not worse than what we started with) - } - - // try again - if (!bitdb.Open(walletDir)) { - // if it still fails, it probably means we can't even create the database env - errorStr = strprintf(_("Error initializing wallet database environment %s!"), walletDir); - return false; - } + if (!bitdb.Open(walletDir, true)) { + errorStr = strprintf(_("Cannot obtain a lock on wallet directory %s. Another instance of bitcoin may be using it."), walletDir); + return false; } + return true; } diff --git a/src/wallet/db.h b/src/wallet/db.h index c6f317927fa..787135e400c 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -68,7 +68,7 @@ public: typedef std::pair, std::vector > KeyValPair; bool Salvage(const std::string& strFile, bool fAggressive, std::vector& vResult); - bool Open(const fs::path& path); + bool Open(const fs::path& path, bool retry = 0); void Close(); void Flush(bool fShutdown); void CheckpointLSN(const std::string& strFile);