|
18 | 18 | #include <sys/stat.h>
|
19 | 19 | #endif
|
20 | 20 |
|
| 21 | +#include <boost/interprocess/sync/file_lock.hpp> |
21 | 22 | #include <boost/thread.hpp>
|
22 | 23 |
|
23 | 24 | namespace {
|
@@ -52,6 +53,24 @@ void CheckUniqueFileid(const CDBEnv& env, const std::string& filename, Db& db)
|
52 | 53 | }
|
53 | 54 | }
|
54 | 55 | }
|
| 56 | + |
| 57 | +bool LockEnvDirectory(const fs::path& env_path) |
| 58 | +{ |
| 59 | + // Make sure only a single Bitcoin process is using the wallet directory. |
| 60 | + fs::path lock_file_path = env_path / ".lock"; |
| 61 | + FILE* file = fsbridge::fopen(lock_file_path, "a"); // empty lock file; created if it doesn't exist. |
| 62 | + if (file) fclose(file); |
| 63 | + |
| 64 | + try { |
| 65 | + static boost::interprocess::file_lock lock(lock_file_path.string().c_str()); |
| 66 | + if (!lock.try_lock()) { |
| 67 | + return false; |
| 68 | + } |
| 69 | + } catch (const boost::interprocess::interprocess_exception& e) { |
| 70 | + return error("Error obtaining lock on wallet directory %s: %s.", env_path.string(), e.what()); |
| 71 | + } |
| 72 | + return true; |
| 73 | +} |
55 | 74 | } // namespace
|
56 | 75 |
|
57 | 76 | //
|
@@ -95,13 +114,17 @@ void CDBEnv::Close()
|
95 | 114 | EnvShutdown();
|
96 | 115 | }
|
97 | 116 |
|
98 |
| -bool CDBEnv::Open(const fs::path& pathIn) |
| 117 | +bool CDBEnv::Open(const fs::path& pathIn, bool retry) |
99 | 118 | {
|
100 | 119 | if (fDbEnvInit)
|
101 | 120 | return true;
|
102 | 121 |
|
103 | 122 | boost::this_thread::interruption_point();
|
104 | 123 |
|
| 124 | + if (!LockEnvDirectory(pathIn)) { |
| 125 | + return false; |
| 126 | + } |
| 127 | + |
105 | 128 | strPath = pathIn.string();
|
106 | 129 | fs::path pathLogDir = pathIn / "database";
|
107 | 130 | TryCreateDirectories(pathLogDir);
|
@@ -134,7 +157,24 @@ bool CDBEnv::Open(const fs::path& pathIn)
|
134 | 157 | S_IRUSR | S_IWUSR);
|
135 | 158 | if (ret != 0) {
|
136 | 159 | dbenv->close(0);
|
137 |
| - return error("CDBEnv::Open: Error %d opening database environment: %s\n", ret, DbEnv::strerror(ret)); |
| 160 | + LogPrintf("CDBEnv::Open: Error %d opening database environment: %s\n", ret, DbEnv::strerror(ret)); |
| 161 | + if (retry) { |
| 162 | + // try moving the database env out of the way |
| 163 | + fs::path pathDatabaseBak = pathIn / strprintf("database.%d.bak", GetTime()); |
| 164 | + try { |
| 165 | + fs::rename(pathLogDir, pathDatabaseBak); |
| 166 | + LogPrintf("Moved old %s to %s. Retrying.\n", pathLogDir.string(), pathDatabaseBak.string()); |
| 167 | + } catch (const fs::filesystem_error&) { |
| 168 | + // failure is ok (well, not really, but it's not worse than what we started with) |
| 169 | + } |
| 170 | + // try opening it again one more time |
| 171 | + if (!Open(pathIn, false)) { |
| 172 | + // if it still fails, it probably means we can't even create the database env |
| 173 | + return false; |
| 174 | + } |
| 175 | + } else { |
| 176 | + return false; |
| 177 | + } |
138 | 178 | }
|
139 | 179 |
|
140 | 180 | fDbEnvInit = true;
|
@@ -269,25 +309,11 @@ bool CDB::VerifyEnvironment(const std::string& walletFile, const fs::path& walle
|
269 | 309 | return false;
|
270 | 310 | }
|
271 | 311 |
|
272 |
| - if (!bitdb.Open(walletDir)) |
273 |
| - { |
274 |
| - // try moving the database env out of the way |
275 |
| - fs::path pathDatabase = walletDir / "database"; |
276 |
| - fs::path pathDatabaseBak = walletDir / strprintf("database.%d.bak", GetTime()); |
277 |
| - try { |
278 |
| - fs::rename(pathDatabase, pathDatabaseBak); |
279 |
| - LogPrintf("Moved old %s to %s. Retrying.\n", pathDatabase.string(), pathDatabaseBak.string()); |
280 |
| - } catch (const fs::filesystem_error&) { |
281 |
| - // failure is ok (well, not really, but it's not worse than what we started with) |
282 |
| - } |
283 |
| - |
284 |
| - // try again |
285 |
| - if (!bitdb.Open(walletDir)) { |
286 |
| - // if it still fails, it probably means we can't even create the database env |
287 |
| - errorStr = strprintf(_("Error initializing wallet database environment %s!"), walletDir); |
288 |
| - return false; |
289 |
| - } |
| 312 | + if (!bitdb.Open(walletDir, true)) { |
| 313 | + errorStr = strprintf(_("Cannot obtain a lock on wallet directory %s. Another instance of bitcoin may be using it."), walletDir); |
| 314 | + return false; |
290 | 315 | }
|
| 316 | + |
291 | 317 | return true;
|
292 | 318 | }
|
293 | 319 |
|
|
0 commit comments