Branch data Line data Source code
1 : : // Copyright (c) 2009-2010 Satoshi Nakamoto
2 : : // Copyright (c) 2009-2022 The Bitcoin Core developers
3 : : // Distributed under the MIT software license, see the accompanying
4 : : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 : :
6 : : #include <compat/compat.h>
7 : : #include <logging.h>
8 : : #include <util/fs.h>
9 : : #include <util/time.h>
10 : : #include <wallet/bdb.h>
11 : : #include <wallet/db.h>
12 : :
13 : : #include <sync.h>
14 : : #include <util/check.h>
15 : : #include <util/fs_helpers.h>
16 : : #include <util/strencodings.h>
17 : : #include <util/translation.h>
18 : :
19 : : #include <stdint.h>
20 : :
21 : : #include <db_cxx.h>
22 : : #include <sys/stat.h>
23 : :
24 : : // Windows may not define S_IRUSR or S_IWUSR. We define both
25 : : // here, with the same values as glibc (see stat.h).
26 : : #ifdef WIN32
27 : : #ifndef S_IRUSR
28 : : #define S_IRUSR 0400
29 : : #define S_IWUSR 0200
30 : : #endif
31 : : #endif
32 : :
33 : : static_assert(BDB_DB_FILE_ID_LEN == DB_FILE_ID_LEN, "DB_FILE_ID_LEN should be 20.");
34 : :
35 : : namespace wallet {
36 : : namespace {
37 : :
38 : : //! Make sure database has a unique fileid within the environment. If it
39 : : //! doesn't, throw an error. BDB caches do not work properly when more than one
40 : : //! open database has the same fileid (values written to one database may show
41 : : //! up in reads to other databases).
42 : : //!
43 : : //! BerkeleyDB generates unique fileids by default
44 : : //! (https://docs.oracle.com/cd/E17275_01/html/programmer_reference/program_copy.html),
45 : : //! so bitcoin should never create different databases with the same fileid, but
46 : : //! this error can be triggered if users manually copy database files.
47 : 1102 : void CheckUniqueFileid(const BerkeleyEnvironment& env, const std::string& filename, Db& db, WalletDatabaseFileId& fileid)
48 : : {
49 [ + - ]: 1102 : if (env.IsMock()) return;
50 : :
51 : 1102 : int ret = db.get_mpf()->get_fileid(fileid.value);
52 [ - + ]: 1102 : if (ret != 0) {
53 [ # # # # ]: 0 : throw std::runtime_error(strprintf("BerkeleyDatabase: Can't open database %s (get_fileid failed with %d)", filename, ret));
54 : : }
55 : :
56 [ + + ]: 2219 : for (const auto& item : env.m_fileids) {
57 [ + + + + ]: 1120 : if (fileid == item.second && &fileid != &item.second) {
58 [ + - ]: 6 : throw std::runtime_error(strprintf("BerkeleyDatabase: Can't open database %s (duplicates fileid %s from %s)", filename,
59 [ + - + - ]: 9 : HexStr(item.second.value), item.first));
60 : : }
61 : : }
62 : : }
63 : :
64 : : RecursiveMutex cs_db;
65 : : std::map<std::string, std::weak_ptr<BerkeleyEnvironment>> g_dbenvs GUARDED_BY(cs_db); //!< Map from directory name to db environment.
66 : : } // namespace
67 : :
68 : : static constexpr auto REVERSE_BYTE_ORDER{std::endian::native == std::endian::little ? 4321 : 1234};
69 : :
70 : 1120 : bool WalletDatabaseFileId::operator==(const WalletDatabaseFileId& rhs) const
71 : : {
72 : 1120 : return memcmp(value, &rhs.value, sizeof(value)) == 0;
73 : : }
74 : :
75 : : /**
76 : : * @param[in] env_directory Path to environment directory
77 : : * @return A shared pointer to the BerkeleyEnvironment object for the wallet directory, never empty because ~BerkeleyEnvironment
78 : : * erases the weak pointer from the g_dbenvs map.
79 : : * @post A new BerkeleyEnvironment weak pointer is inserted into g_dbenvs if the directory path key was not already in the map.
80 : : */
81 : 706 : std::shared_ptr<BerkeleyEnvironment> GetBerkeleyEnv(const fs::path& env_directory, bool use_shared_memory)
82 : : {
83 : 706 : LOCK(cs_db);
84 [ + - + - : 2118 : auto inserted = g_dbenvs.emplace(fs::PathToString(env_directory), std::weak_ptr<BerkeleyEnvironment>());
- + ]
85 [ + + ]: 706 : if (inserted.second) {
86 [ + - ]: 692 : auto env = std::make_shared<BerkeleyEnvironment>(env_directory, use_shared_memory);
87 : 692 : inserted.first->second = env;
88 : 692 : return env;
89 : : }
90 : 14 : return inserted.first->second.lock();
91 : 706 : }
92 : :
93 : : //
94 : : // BerkeleyBatch
95 : : //
96 : :
97 : 1099 : void BerkeleyEnvironment::Close()
98 : : {
99 [ + + ]: 1099 : if (!fDbEnvInit)
100 : : return;
101 : :
102 : 698 : fDbEnvInit = false;
103 : :
104 [ + + ]: 1108 : for (auto& db : m_databases) {
105 [ - + ]: 410 : BerkeleyDatabase& database = db.second.get();
106 [ - + ]: 410 : assert(database.m_refcount <= 0);
107 [ - + ]: 410 : if (database.m_db) {
108 : 0 : database.m_db->close(0);
109 [ - - ]: 410 : database.m_db.reset();
110 : : }
111 : : }
112 : :
113 : 698 : FILE* error_file = nullptr;
114 : 698 : dbenv->get_errfile(&error_file);
115 : :
116 : 698 : int ret = dbenv->close(0);
117 [ - + ]: 698 : if (ret != 0)
118 : 0 : LogPrintf("BerkeleyEnvironment::Close: Error %d closing database environment: %s\n", ret, DbEnv::strerror(ret));
119 [ + - ]: 698 : if (!fMockDb)
120 [ + - ]: 698 : DbEnv(uint32_t{0}).remove(strPath.c_str(), 0);
121 : :
122 [ + - ]: 698 : if (error_file) fclose(error_file);
123 : :
124 [ + - + - ]: 2094 : UnlockDirectory(fs::PathFromString(strPath), ".walletlock");
125 : : }
126 : :
127 : 712 : void BerkeleyEnvironment::Reset()
128 : : {
129 [ + - + + ]: 712 : dbenv.reset(new DbEnv(DB_CXX_NO_EXCEPTIONS));
130 : 712 : fDbEnvInit = false;
131 : 712 : fMockDb = false;
132 : 712 : }
133 : :
134 [ + - ]: 692 : BerkeleyEnvironment::BerkeleyEnvironment(const fs::path& dir_path, bool use_shared_memory) : strPath(fs::PathToString(dir_path)), m_use_shared_memory(use_shared_memory)
135 : : {
136 [ + - ]: 692 : Reset();
137 : 692 : }
138 : :
139 : 692 : BerkeleyEnvironment::~BerkeleyEnvironment()
140 : : {
141 : 692 : LOCK(cs_db);
142 : 692 : g_dbenvs.erase(strPath);
143 [ + - ]: 692 : Close();
144 : 692 : }
145 : :
146 : 110082 : bool BerkeleyEnvironment::Open(bilingual_str& err)
147 : : {
148 [ + + ]: 110082 : if (fDbEnvInit) {
149 : : return true;
150 : : }
151 : :
152 : 705 : fs::path pathIn = fs::PathFromString(strPath);
153 [ + + ]: 705 : TryCreateDirectories(pathIn);
154 [ + - + - : 1408 : if (util::LockDirectory(pathIn, ".walletlock") != util::LockResult::Success) {
+ + ]
155 [ + - ]: 6 : LogPrintf("Cannot obtain a lock on wallet directory %s. Another instance may be using it.\n", strPath);
156 [ + - + - : 24 : err = strprintf(_("Error initializing wallet database environment %s!"), fs::quoted(fs::PathToString(Directory())));
+ - + - ]
157 : 6 : return false;
158 : : }
159 : :
160 [ + - + - ]: 1396 : fs::path pathLogDir = pathIn / "database";
161 [ + - ]: 698 : TryCreateDirectories(pathLogDir);
162 [ + - + - ]: 1396 : fs::path pathErrorFile = pathIn / "db.log";
163 [ + - + - : 2792 : LogPrintf("BerkeleyEnvironment::Open: LogDir=%s ErrorFile=%s\n", fs::PathToString(pathLogDir), fs::PathToString(pathErrorFile));
+ - ]
164 : :
165 : 698 : unsigned int nEnvFlags = 0;
166 [ + - ]: 698 : if (!m_use_shared_memory) {
167 : 698 : nEnvFlags |= DB_PRIVATE;
168 : : }
169 : :
170 [ + - + - ]: 1396 : dbenv->set_lg_dir(fs::PathToString(pathLogDir).c_str());
171 [ + - ]: 698 : dbenv->set_cachesize(0, 0x100000, 1); // 1 MiB should be enough for just the wallet
172 [ + - ]: 698 : dbenv->set_lg_bsize(0x10000);
173 [ + - ]: 698 : dbenv->set_lg_max(1048576);
174 [ + - ]: 698 : dbenv->set_lk_max_locks(40000);
175 [ + - ]: 698 : dbenv->set_lk_max_objects(40000);
176 [ + - + - ]: 698 : dbenv->set_errfile(fsbridge::fopen(pathErrorFile, "a")); /// debug
177 [ + - ]: 698 : dbenv->set_flags(DB_AUTO_COMMIT, 1);
178 [ + - ]: 698 : dbenv->set_flags(DB_TXN_WRITE_NOSYNC, 1);
179 [ + - ]: 698 : dbenv->log_set_config(DB_LOG_AUTO_REMOVE, 1);
180 [ + - ]: 698 : int ret = dbenv->open(strPath.c_str(),
181 : : DB_CREATE |
182 : : DB_INIT_LOCK |
183 : : DB_INIT_LOG |
184 : : DB_INIT_MPOOL |
185 : : DB_INIT_TXN |
186 : : DB_THREAD |
187 : : DB_RECOVER |
188 : : nEnvFlags,
189 : 698 : S_IRUSR | S_IWUSR);
190 [ - + ]: 698 : if (ret != 0) {
191 [ # # # # ]: 0 : LogPrintf("BerkeleyEnvironment::Open: Error %d opening database environment: %s\n", ret, DbEnv::strerror(ret));
192 [ # # ]: 0 : int ret2 = dbenv->close(0);
193 [ # # ]: 0 : if (ret2 != 0) {
194 [ # # # # ]: 0 : LogPrintf("BerkeleyEnvironment::Open: Error %d closing failed database environment: %s\n", ret2, DbEnv::strerror(ret2));
195 : : }
196 [ # # ]: 0 : Reset();
197 [ # # # # : 0 : err = strprintf(_("Error initializing wallet database environment %s!"), fs::quoted(fs::PathToString(Directory())));
# # # # ]
198 [ # # ]: 0 : if (ret == DB_RUNRECOVERY) {
199 [ # # # # : 0 : err += Untranslated(" ") + _("This error could occur if this wallet was not shutdown cleanly and was last loaded using a build with a newer version of Berkeley DB. If so, please use the software that last loaded this wallet");
# # # # ]
200 : : }
201 : 0 : return false;
202 : : }
203 : :
204 : 698 : fDbEnvInit = true;
205 : 698 : fMockDb = false;
206 : 698 : return true;
207 : 2100 : }
208 : :
209 : : //! Construct an in-memory mock Berkeley environment for testing
210 [ # # ]: 0 : BerkeleyEnvironment::BerkeleyEnvironment() : m_use_shared_memory(false)
211 : : {
212 [ # # ]: 0 : Reset();
213 : :
214 [ # # # # : 0 : LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::MakeMock\n");
# # ]
215 : :
216 [ # # ]: 0 : dbenv->set_cachesize(1, 0, 1);
217 [ # # ]: 0 : dbenv->set_lg_bsize(10485760 * 4);
218 [ # # ]: 0 : dbenv->set_lg_max(10485760);
219 [ # # ]: 0 : dbenv->set_lk_max_locks(10000);
220 [ # # ]: 0 : dbenv->set_lk_max_objects(10000);
221 [ # # ]: 0 : dbenv->set_flags(DB_AUTO_COMMIT, 1);
222 [ # # ]: 0 : dbenv->log_set_config(DB_LOG_IN_MEMORY, 1);
223 [ # # ]: 0 : int ret = dbenv->open(nullptr,
224 : : DB_CREATE |
225 : : DB_INIT_LOCK |
226 : : DB_INIT_LOG |
227 : : DB_INIT_MPOOL |
228 : : DB_INIT_TXN |
229 : : DB_THREAD |
230 : : DB_PRIVATE,
231 : 0 : S_IRUSR | S_IWUSR);
232 [ # # ]: 0 : if (ret > 0) {
233 [ # # # # ]: 0 : throw std::runtime_error(strprintf("BerkeleyEnvironment::MakeMock: Error %d opening database environment.", ret));
234 : : }
235 : :
236 : 0 : fDbEnvInit = true;
237 : 0 : fMockDb = true;
238 : 0 : }
239 : :
240 : : /** RAII class that automatically cleanses its data on destruction */
241 : : class SafeDbt final
242 : : {
243 : : Dbt m_dbt;
244 : :
245 : : public:
246 : : // construct Dbt with internally-managed data
247 : : SafeDbt();
248 : : // construct Dbt with provided data
249 : : SafeDbt(void* data, size_t size);
250 : : ~SafeDbt();
251 : :
252 : : // delegate to Dbt
253 : : const void* get_data() const;
254 : : uint32_t get_size() const;
255 : :
256 : : // conversion operator to access the underlying Dbt
257 : : operator Dbt*();
258 : : };
259 : :
260 : 92832 : SafeDbt::SafeDbt()
261 : : {
262 : 92832 : m_dbt.set_flags(DB_DBT_MALLOC);
263 : 92832 : }
264 : :
265 : 613740 : SafeDbt::SafeDbt(void* data, size_t size)
266 : 613740 : : m_dbt(data, size)
267 : : {
268 : 613740 : }
269 : :
270 : 706572 : SafeDbt::~SafeDbt()
271 : : {
272 [ + + ]: 706572 : if (m_dbt.get_data() != nullptr) {
273 : : // Clear memory, e.g. in case it was a private key
274 : 698952 : memory_cleanse(m_dbt.get_data(), m_dbt.get_size());
275 : : // under DB_DBT_MALLOC, data is malloced by the Dbt, but must be
276 : : // freed by the caller.
277 : : // https://docs.oracle.com/cd/E17275_01/html/api_reference/C/dbt.html
278 [ + + ]: 698952 : if (m_dbt.get_flags() & DB_DBT_MALLOC) {
279 : 85242 : free(m_dbt.get_data());
280 : : }
281 : : }
282 : 706572 : }
283 : :
284 : 165415 : const void* SafeDbt::get_data() const
285 : : {
286 : 165415 : return m_dbt.get_data();
287 : : }
288 : :
289 : 149036 : uint32_t SafeDbt::get_size() const
290 : : {
291 : 149036 : return m_dbt.get_size();
292 : : }
293 : :
294 : 706572 : SafeDbt::operator Dbt*()
295 : : {
296 : 706572 : return &m_dbt;
297 : : }
298 : :
299 : 149009 : static Span<const std::byte> SpanFromDbt(const SafeDbt& dbt)
300 : : {
301 : 149009 : return {reinterpret_cast<const std::byte*>(dbt.get_data()), dbt.get_size()};
302 : : }
303 : :
304 : 696 : BerkeleyDatabase::BerkeleyDatabase(std::shared_ptr<BerkeleyEnvironment> env, fs::path filename, const DatabaseOptions& options) :
305 : : WalletDatabase(),
306 [ + + ]: 696 : env(std::move(env)),
307 [ + + ]: 696 : m_byteswap(options.require_format == DatabaseFormat::BERKELEY_SWAP),
308 : 696 : m_filename(std::move(filename)),
309 [ + + ]: 696 : m_max_log_mb(options.max_log_mb)
310 : : {
311 [ + - ]: 696 : auto inserted = this->env->m_databases.emplace(m_filename, std::ref(*this));
312 [ - + ]: 696 : assert(inserted.second);
313 [ - - ]: 696 : }
314 : :
315 : 599 : bool BerkeleyDatabase::Verify(bilingual_str& errorStr)
316 : : {
317 : 599 : fs::path walletDir = env->Directory();
318 [ + - + - ]: 1198 : fs::path file_path = walletDir / m_filename;
319 : :
320 [ + - + - ]: 599 : LogPrintf("Using BerkeleyDB version %s\n", BerkeleyDatabaseVersion());
321 [ + - + - ]: 1198 : LogPrintf("Using wallet %s\n", fs::PathToString(file_path));
322 : :
323 [ + + + + ]: 599 : if (!env->Open(errorStr)) {
324 : : return false;
325 : : }
326 : :
327 [ + - + + ]: 592 : if (fs::exists(file_path))
328 : : {
329 [ - + ]: 261 : assert(m_refcount == 0);
330 : :
331 [ + - ]: 522 : Db db(env->dbenv.get(), 0);
332 [ + - ]: 261 : const std::string strFile = fs::PathToString(m_filename);
333 [ + - ]: 261 : int result = db.verify(strFile.c_str(), nullptr, nullptr, 0);
334 [ - + ]: 261 : if (result != 0) {
335 [ # # # # : 0 : errorStr = strprintf(_("%s corrupt. Try using the wallet tool bitcoin-wallet to salvage or restoring a backup."), fs::quoted(fs::PathToString(file_path)));
# # ]
336 : 0 : return false;
337 : : }
338 : 261 : }
339 : : // also return true if files does not exists
340 : : return true;
341 : 1197 : }
342 : :
343 : 240 : void BerkeleyEnvironment::CheckpointLSN(const std::string& strFile)
344 : : {
345 : 240 : dbenv->txn_checkpoint(0, 0, 0);
346 [ + - ]: 240 : if (fMockDb)
347 : : return;
348 : 240 : dbenv->lsn_reset(strFile.c_str(), 0);
349 : : }
350 : :
351 : 1392 : BerkeleyDatabase::~BerkeleyDatabase()
352 : : {
353 [ + - ]: 696 : if (env) {
354 : 696 : LOCK(cs_db);
355 : 696 : env->CloseDb(m_filename);
356 [ - + ]: 696 : assert(!m_db);
357 : 696 : size_t erased = env->m_databases.erase(m_filename);
358 [ - + ]: 696 : assert(erased == 1);
359 [ + - ]: 696 : env->m_fileids.erase(fs::PathToString(m_filename));
360 : 696 : }
361 [ + - ]: 2088 : }
362 : :
363 [ + - ]: 109460 : BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase& database, const bool read_only, bool fFlushOnCloseIn) : m_database(database)
364 : : {
365 [ + - ]: 109460 : database.AddRef();
366 [ + + ]: 109460 : database.Open();
367 : 109457 : fReadOnly = read_only;
368 : 109457 : fFlushOnClose = fFlushOnCloseIn;
369 [ + - ]: 109457 : env = database.env.get();
370 [ + - ]: 109457 : pdb = database.m_db.get();
371 [ + - ]: 218914 : strFile = fs::PathToString(database.m_filename);
372 : 109460 : }
373 : :
374 : 109460 : void BerkeleyDatabase::Open()
375 : : {
376 : 109460 : unsigned int nFlags = DB_THREAD | DB_CREATE;
377 : :
378 : 109460 : {
379 : 109460 : LOCK(cs_db);
380 [ + - ]: 109460 : bilingual_str open_err;
381 [ + - - + ]: 109460 : if (!env->Open(open_err))
382 [ # # ]: 0 : throw std::runtime_error("BerkeleyDatabase: Failed to open database environment.");
383 : :
384 [ + + ]: 109460 : if (m_db == nullptr) {
385 : 1102 : int ret;
386 [ + - ]: 1102 : std::unique_ptr<Db> pdb_temp = std::make_unique<Db>(env->dbenv.get(), 0);
387 [ + - ]: 1102 : const std::string strFile = fs::PathToString(m_filename);
388 : :
389 [ - + ]: 1102 : bool fMockDb = env->IsMock();
390 [ - + ]: 1102 : if (fMockDb) {
391 [ # # ]: 0 : DbMpoolFile* mpf = pdb_temp->get_mpf();
392 [ # # ]: 0 : ret = mpf->set_flags(DB_MPOOL_NOFILE, 1);
393 [ # # ]: 0 : if (ret != 0) {
394 [ # # # # ]: 0 : throw std::runtime_error(strprintf("BerkeleyDatabase: Failed to configure for no temp file backing for database %s", strFile));
395 : : }
396 : : }
397 : :
398 [ + + ]: 1102 : if (m_byteswap) {
399 [ + - ]: 18 : pdb_temp->set_lorder(REVERSE_BYTE_ORDER);
400 : : }
401 : :
402 [ + - + - ]: 2204 : ret = pdb_temp->open(nullptr, // Txn pointer
403 : 1102 : fMockDb ? nullptr : strFile.c_str(), // Filename
404 : 0 : fMockDb ? strFile.c_str() : "main", // Logical db name
405 : : DB_BTREE, // Database type
406 : : nFlags, // Flags
407 : : 0);
408 : :
409 [ - + ]: 1102 : if (ret != 0) {
410 [ # # # # ]: 0 : throw std::runtime_error(strprintf("BerkeleyDatabase: Error %d, can't open database %s", ret, strFile));
411 : : }
412 : :
413 : : // Call CheckUniqueFileid on the containing BDB environment to
414 : : // avoid BDB data consistency bugs that happen when different data
415 : : // files in the same environment have the same fileid.
416 [ + - + + ]: 1102 : CheckUniqueFileid(*env, strFile, *pdb_temp, this->env->m_fileids[strFile]);
417 : :
418 [ - + ]: 1099 : m_db.reset(pdb_temp.release());
419 : :
420 : 1102 : }
421 [ + - ]: 109460 : }
422 : 109457 : }
423 : :
424 : 76599 : void BerkeleyBatch::Flush()
425 : : {
426 [ + + ]: 76599 : if (activeTxn)
427 : : return;
428 : :
429 : : // Flush database activity from memory pool to disk log
430 : 76453 : unsigned int nMinutes = 0;
431 [ + + ]: 76453 : if (fReadOnly)
432 : 20 : nMinutes = 1;
433 : :
434 [ + - ]: 76453 : if (env) { // env is nullptr for dummy databases (i.e. in tests). Don't actually flush if env is nullptr so we don't segfault
435 [ + + ]: 76453 : env->dbenv->txn_checkpoint(nMinutes ? m_database.m_max_log_mb * 1024 : 0, nMinutes, 0);
436 : : }
437 : : }
438 : :
439 : 260831 : void BerkeleyDatabase::IncrementUpdateCounter()
440 : : {
441 : 260831 : ++nUpdateCounter;
442 : 260831 : }
443 : :
444 : 218894 : BerkeleyBatch::~BerkeleyBatch()
445 : : {
446 : 109457 : Close();
447 : 109457 : m_database.RemoveRef();
448 : 218894 : }
449 : :
450 : 109477 : void BerkeleyBatch::Close()
451 : : {
452 [ + + ]: 109477 : if (!pdb)
453 : : return;
454 [ + + ]: 109457 : if (activeTxn)
455 : 45 : activeTxn->abort();
456 : 109457 : activeTxn = nullptr;
457 : 109457 : pdb = nullptr;
458 : :
459 [ + + ]: 109457 : if (fFlushOnClose)
460 : 76396 : Flush();
461 : : }
462 : :
463 : 1860 : void BerkeleyEnvironment::CloseDb(const fs::path& filename)
464 : : {
465 : 1860 : {
466 : 1860 : LOCK(cs_db);
467 : 1860 : auto it = m_databases.find(filename);
468 [ - + ]: 1860 : assert(it != m_databases.end());
469 [ + + ]: 1860 : BerkeleyDatabase& database = it->second.get();
470 [ + + ]: 1860 : if (database.m_db) {
471 : : // Close the database handle
472 [ + - ]: 1099 : database.m_db->close(0);
473 [ + - + - ]: 2959 : database.m_db.reset();
474 : : }
475 : 1860 : }
476 : 1860 : }
477 : :
478 : 20 : void BerkeleyEnvironment::ReloadDbEnv()
479 : : {
480 : : // Make sure that no Db's are in use
481 : 20 : AssertLockNotHeld(cs_db);
482 : 20 : std::unique_lock<RecursiveMutex> lock(cs_db);
483 [ + - ]: 20 : m_db_in_use.wait(lock, [this](){
484 [ + + ]: 40 : for (auto& db : m_databases) {
485 [ + - ]: 20 : if (db.second.get().m_refcount > 0) return false;
486 : : }
487 : : return true;
488 : : });
489 : :
490 : 20 : std::vector<fs::path> filenames;
491 [ + - ]: 20 : filenames.reserve(m_databases.size());
492 [ + + ]: 40 : for (const auto& it : m_databases) {
493 [ + - ]: 20 : filenames.push_back(it.first);
494 : : }
495 : : // Close the individual Db's
496 [ + + ]: 40 : for (const fs::path& filename : filenames) {
497 [ + - ]: 20 : CloseDb(filename);
498 : : }
499 : : // Reset the environment
500 [ + - ]: 20 : Flush(true); // This will flush and close the environment
501 [ + - ]: 20 : Reset();
502 [ + - ]: 20 : bilingual_str open_err;
503 [ + - ]: 20 : Open(open_err);
504 [ + - ]: 40 : }
505 : :
506 : 12848 : DbTxn* BerkeleyEnvironment::TxnBegin(int flags)
507 : : {
508 : 12848 : DbTxn* ptxn = nullptr;
509 : 12848 : int ret = dbenv->txn_begin(nullptr, &ptxn, flags);
510 [ + - - + ]: 12848 : if (!ptxn || ret != 0)
511 : 0 : return nullptr;
512 : : return ptxn;
513 : : }
514 : :
515 : 20 : bool BerkeleyDatabase::Rewrite(const char* pszSkip)
516 : : {
517 : 20 : while (true) {
518 : 20 : {
519 : 20 : LOCK(cs_db);
520 [ + - ]: 20 : const std::string strFile = fs::PathToString(m_filename);
521 [ + - ]: 20 : if (m_refcount <= 0) {
522 : : // Flush log data to the dat file
523 [ + - ]: 20 : env->CloseDb(m_filename);
524 [ + - ]: 20 : env->CheckpointLSN(strFile);
525 [ + - ]: 20 : m_refcount = -1;
526 : :
527 : 20 : bool fSuccess = true;
528 [ + - ]: 20 : LogPrintf("BerkeleyBatch::Rewrite: Rewriting %s...\n", strFile);
529 [ + - ]: 20 : std::string strFileRes = strFile + ".rewrite";
530 : 20 : { // surround usage of db with extra {}
531 [ + - ]: 20 : BerkeleyBatch db(*this, true);
532 [ + - ]: 20 : std::unique_ptr<Db> pdbCopy = std::make_unique<Db>(env->dbenv.get(), 0);
533 : :
534 [ - + ]: 20 : if (m_byteswap) {
535 [ # # ]: 0 : pdbCopy->set_lorder(REVERSE_BYTE_ORDER);
536 : : }
537 : :
538 [ + - ]: 20 : int ret = pdbCopy->open(nullptr, // Txn pointer
539 : : strFileRes.c_str(), // Filename
540 : : "main", // Logical db name
541 : : DB_BTREE, // Database type
542 : : DB_CREATE, // Flags
543 : : 0);
544 [ - + ]: 20 : if (ret > 0) {
545 [ # # ]: 0 : LogPrintf("BerkeleyBatch::Rewrite: Can't create database file %s\n", strFileRes);
546 : : fSuccess = false;
547 : : }
548 : :
549 [ + - ]: 20 : std::unique_ptr<DatabaseCursor> cursor = db.GetNewCursor();
550 [ + - ]: 20 : if (cursor) {
551 [ + - ]: 1739 : while (fSuccess) {
552 : 1739 : DataStream ssKey{};
553 : 1739 : DataStream ssValue{};
554 [ + - ]: 1739 : DatabaseCursor::Status ret1 = cursor->Next(ssKey, ssValue);
555 [ + + ]: 1739 : if (ret1 == DatabaseCursor::Status::DONE) {
556 : : break;
557 [ + - ]: 1719 : } else if (ret1 == DatabaseCursor::Status::FAIL) {
558 : : fSuccess = false;
559 : : break;
560 : : }
561 [ - + - - ]: 1719 : if (pszSkip &&
562 [ # # # # ]: 0 : strncmp((const char*)ssKey.data(), pszSkip, std::min(ssKey.size(), strlen(pszSkip))) == 0)
563 : 0 : continue;
564 [ + + ]: 1719 : if (strncmp((const char*)ssKey.data(), "\x07version", 8) == 0) {
565 : : // Update version:
566 [ + - ]: 20 : ssValue.clear();
567 [ + - ]: 20 : ssValue << CLIENT_VERSION;
568 : : }
569 [ + - ]: 1719 : Dbt datKey(ssKey.data(), ssKey.size());
570 [ + - ]: 1719 : Dbt datValue(ssValue.data(), ssValue.size());
571 [ + - ]: 1719 : int ret2 = pdbCopy->put(nullptr, &datKey, &datValue, DB_NOOVERWRITE);
572 [ - + ]: 1719 : if (ret2 > 0)
573 : 0 : fSuccess = false;
574 : 1739 : }
575 [ + - ]: 20 : cursor.reset();
576 : : }
577 [ + - ]: 20 : if (fSuccess) {
578 [ + - ]: 20 : db.Close();
579 [ + - ]: 20 : env->CloseDb(m_filename);
580 [ + - - + ]: 20 : if (pdbCopy->close(0))
581 : 0 : fSuccess = false;
582 : : } else {
583 [ # # ]: 0 : pdbCopy->close(0);
584 : : }
585 : 20 : }
586 [ + - ]: 20 : if (fSuccess) {
587 [ + - ]: 20 : Db dbA(env->dbenv.get(), 0);
588 [ + - - + ]: 20 : if (dbA.remove(strFile.c_str(), nullptr, 0))
589 : 0 : fSuccess = false;
590 [ + - ]: 20 : Db dbB(env->dbenv.get(), 0);
591 [ + - - + ]: 20 : if (dbB.rename(strFileRes.c_str(), nullptr, strFile.c_str(), 0))
592 : 0 : fSuccess = false;
593 : 20 : }
594 [ - + ]: 20 : if (!fSuccess)
595 [ # # ]: 0 : LogPrintf("BerkeleyBatch::Rewrite: Failed to rewrite database file %s\n", strFileRes);
596 : 20 : return fSuccess;
597 : 20 : }
598 [ + - - - ]: 40 : }
599 : 0 : UninterruptibleSleep(std::chrono::milliseconds{100});
600 : 0 : }
601 : : }
602 : :
603 : :
604 : 1310 : void BerkeleyEnvironment::Flush(bool fShutdown)
605 : : {
606 : 1310 : const auto start{SteadyClock::now()};
607 : : // Flush log data to the actual data file on all files that are not in use
608 [ + + + + : 2552 : LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: [%s] Flush(%s)%s\n", strPath, fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started");
+ + ]
609 [ + + ]: 1310 : if (!fDbEnvInit)
610 : : return;
611 : 944 : {
612 : 944 : LOCK(cs_db);
613 : 944 : bool no_dbs_accessed = true;
614 [ + + ]: 1907 : for (auto& db_it : m_databases) {
615 : 963 : const fs::path& filename = db_it.first;
616 [ + + ]: 963 : int nRefCount = db_it.second.get().m_refcount;
617 [ + + ]: 963 : if (nRefCount < 0) continue;
618 [ + - ]: 888 : const std::string strFile = fs::PathToString(filename);
619 [ + - + + : 888 : LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: Flushing %s (refcount = %d)...\n", strFile, nRefCount);
+ - ]
620 [ + + ]: 888 : if (nRefCount == 0) {
621 : : // Move log data to the dat file
622 [ + - ]: 884 : CloseDb(filename);
623 [ + - + + : 884 : LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: %s checkpoint\n", strFile);
+ - ]
624 [ + - ]: 884 : dbenv->txn_checkpoint(0, 0, 0);
625 [ + - + + : 884 : LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: %s detach\n", strFile);
+ - ]
626 [ + - ]: 884 : if (!fMockDb)
627 [ + - ]: 884 : dbenv->lsn_reset(strFile.c_str(), 0);
628 [ + - + + : 884 : LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: %s closed\n", strFile);
+ - ]
629 : 884 : nRefCount = -1;
630 : : } else {
631 : : no_dbs_accessed = false;
632 : : }
633 : 888 : }
634 [ + - + + : 1481 : LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: Flush(%s)%s took %15dms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started", Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
- + + + +
- ]
635 [ + + ]: 944 : if (fShutdown) {
636 : 407 : char** listp;
637 [ + - ]: 407 : if (no_dbs_accessed) {
638 [ + - ]: 407 : dbenv->log_archive(&listp, DB_ARCH_REMOVE);
639 [ + - ]: 407 : Close();
640 [ + - ]: 407 : if (!fMockDb) {
641 [ + - + - ]: 1628 : fs::remove_all(fs::PathFromString(strPath) / "database");
642 : : }
643 : : }
644 : : }
645 : 944 : }
646 : : }
647 : :
648 : 197 : bool BerkeleyDatabase::PeriodicFlush()
649 : : {
650 : : // Don't flush if we can't acquire the lock.
651 : 197 : TRY_LOCK(cs_db, lockDb);
652 [ + + ]: 197 : if (!lockDb) return false;
653 : :
654 : : // Don't flush if any databases are in use
655 [ + + ]: 386 : for (auto& it : env->m_databases) {
656 [ + - ]: 193 : if (it.second.get().m_refcount > 0) return false;
657 : : }
658 : :
659 : : // Don't flush if there haven't been any batch writes for this database.
660 [ + - ]: 193 : if (m_refcount < 0) return false;
661 : :
662 [ + - ]: 193 : const std::string strFile = fs::PathToString(m_filename);
663 [ + - + - : 193 : LogPrint(BCLog::WALLETDB, "Flushing %s\n", strFile);
+ - ]
664 : 193 : const auto start{SteadyClock::now()};
665 : :
666 : : // Flush wallet file so it's self contained
667 [ + - ]: 193 : env->CloseDb(m_filename);
668 [ + - ]: 193 : env->CheckpointLSN(strFile);
669 [ + - ]: 193 : m_refcount = -1;
670 : :
671 [ + - + - : 193 : LogPrint(BCLog::WALLETDB, "Flushed %s %dms\n", strFile, Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
+ - ]
672 : :
673 : 193 : return true;
674 : 390 : }
675 : :
676 : 27 : bool BerkeleyDatabase::Backup(const std::string& strDest) const
677 : : {
678 : 27 : const std::string strFile = fs::PathToString(m_filename);
679 : 27 : while (true)
680 : : {
681 : 27 : {
682 [ + - ]: 27 : LOCK(cs_db);
683 [ + - ]: 27 : if (m_refcount <= 0)
684 : : {
685 : : // Flush log data to the dat file
686 [ + - ]: 27 : env->CloseDb(m_filename);
687 [ + - ]: 27 : env->CheckpointLSN(strFile);
688 : :
689 : : // Copy wallet file
690 [ + - + - ]: 54 : fs::path pathSrc = env->Directory() / m_filename;
691 [ + - ]: 27 : fs::path pathDest(fs::PathFromString(strDest));
692 [ + - + + ]: 27 : if (fs::is_directory(pathDest))
693 [ + - ]: 2 : pathDest /= m_filename;
694 : :
695 : 27 : try {
696 [ + - + + : 27 : if (fs::exists(pathDest) && fs::equivalent(pathSrc, pathDest)) {
+ - + - ]
697 [ + - + - ]: 8 : LogPrintf("cannot backup to wallet source file %s\n", fs::PathToString(pathDest));
698 : 4 : return false;
699 : : }
700 : :
701 [ + - ]: 23 : fs::copy_file(pathSrc, pathDest, fs::copy_options::overwrite_existing);
702 [ + - + - ]: 46 : LogPrintf("copied %s to %s\n", strFile, fs::PathToString(pathDest));
703 : 23 : return true;
704 [ - - ]: 0 : } catch (const fs::filesystem_error& e) {
705 [ - - - - ]: 0 : LogPrintf("error copying %s to %s - %s\n", strFile, fs::PathToString(pathDest), fsbridge::get_filesystem_error_message(e));
706 : 0 : return false;
707 [ - - ]: 0 : }
708 [ + - ]: 54 : }
709 : 27 : }
710 [ # # ]: 0 : UninterruptibleSleep(std::chrono::milliseconds{100});
711 : : }
712 : 27 : }
713 : :
714 : 888 : void BerkeleyDatabase::Flush()
715 : : {
716 : 888 : env->Flush(false);
717 : 888 : }
718 : :
719 : 402 : void BerkeleyDatabase::Close()
720 : : {
721 : 402 : env->Flush(true);
722 : 402 : }
723 : :
724 : 20 : void BerkeleyDatabase::ReloadDbEnv()
725 : : {
726 : 20 : env->ReloadDbEnv();
727 : 20 : }
728 : :
729 : 11582 : BerkeleyCursor::BerkeleyCursor(BerkeleyDatabase& database, const BerkeleyBatch& batch, Span<const std::byte> prefix)
730 [ + - ]: 11582 : : m_key_prefix(prefix.begin(), prefix.end())
731 : : {
732 [ - + ]: 11582 : if (!database.m_db.get()) {
733 [ # # # # ]: 0 : throw std::runtime_error(STR_INTERNAL_BUG("BerkeleyDatabase does not exist"));
734 : : }
735 : : // Transaction argument to cursor is only needed when using the cursor to
736 : : // write to the database. Read-only cursors do not need a txn pointer.
737 [ + - ]: 11582 : int ret = database.m_db->cursor(batch.txn(), &m_cursor, 0);
738 [ - + ]: 11582 : if (ret != 0) {
739 [ # # # # : 0 : throw std::runtime_error(STR_INTERNAL_BUG(strprintf("BDB Cursor could not be created. Returned %d", ret)));
# # ]
740 : : }
741 : 11582 : }
742 : :
743 : 75391 : DatabaseCursor::Status BerkeleyCursor::Next(DataStream& ssKey, DataStream& ssValue)
744 : : {
745 [ + - ]: 75391 : if (m_cursor == nullptr) return Status::FAIL;
746 : : // Read at cursor
747 : 75391 : SafeDbt datKey(m_key_prefix.data(), m_key_prefix.size());
748 [ + - ]: 75391 : SafeDbt datValue;
749 : 75391 : int ret = -1;
750 [ + + + + ]: 75391 : if (m_first && !m_key_prefix.empty()) {
751 [ + - ]: 11541 : ret = m_cursor->get(datKey, datValue, DB_SET_RANGE);
752 : : } else {
753 [ + - ]: 63850 : ret = m_cursor->get(datKey, datValue, DB_NEXT);
754 : : }
755 : 75391 : m_first = false;
756 [ + + ]: 75391 : if (ret == DB_NOTFOUND) {
757 : : return Status::DONE;
758 : : }
759 [ + - ]: 68809 : if (ret != 0) {
760 : : return Status::FAIL;
761 : : }
762 : :
763 : 68809 : Span<const std::byte> raw_key = SpanFromDbt(datKey);
764 [ + + + + ]: 68809 : if (!m_key_prefix.empty() && std::mismatch(raw_key.begin(), raw_key.end(), m_key_prefix.begin(), m_key_prefix.end()).second != m_key_prefix.end()) {
765 : : return Status::DONE;
766 : : }
767 : :
768 : : // Convert to streams
769 [ - + ]: 63821 : ssKey.clear();
770 [ + - ]: 63821 : ssKey.write(raw_key);
771 [ - + ]: 63821 : ssValue.clear();
772 [ + - ]: 63821 : ssValue.write(SpanFromDbt(datValue));
773 : : return Status::MORE;
774 : 75391 : }
775 : :
776 : 23164 : BerkeleyCursor::~BerkeleyCursor()
777 : : {
778 [ - + ]: 11582 : if (!m_cursor) return;
779 : 11582 : m_cursor->close();
780 : 11582 : m_cursor = nullptr;
781 : 23164 : }
782 : :
783 : 28 : std::unique_ptr<DatabaseCursor> BerkeleyBatch::GetNewCursor()
784 : : {
785 [ - + ]: 28 : if (!pdb) return nullptr;
786 : 28 : return std::make_unique<BerkeleyCursor>(m_database, *this);
787 : : }
788 : :
789 : 11542 : std::unique_ptr<DatabaseCursor> BerkeleyBatch::GetNewPrefixCursor(Span<const std::byte> prefix)
790 : : {
791 [ - + ]: 11542 : if (!pdb) return nullptr;
792 : 11542 : return std::make_unique<BerkeleyCursor>(m_database, *this, prefix);
793 : : }
794 : :
795 : 12845 : bool BerkeleyBatch::TxnBegin()
796 : : {
797 [ + - + - ]: 12845 : if (!pdb || activeTxn)
798 : : return false;
799 : 12845 : DbTxn* ptxn = env->TxnBegin(DB_TXN_WRITE_NOSYNC);
800 [ + - ]: 12845 : if (!ptxn)
801 : : return false;
802 : 12845 : activeTxn = ptxn;
803 : 12845 : return true;
804 : : }
805 : :
806 : 12787 : bool BerkeleyBatch::TxnCommit()
807 : : {
808 [ + - + - ]: 12787 : if (!pdb || !activeTxn)
809 : : return false;
810 : 12787 : int ret = activeTxn->commit(0);
811 : 12787 : activeTxn = nullptr;
812 : 12787 : return (ret == 0);
813 : : }
814 : :
815 : 13 : bool BerkeleyBatch::TxnAbort()
816 : : {
817 [ + - + - ]: 13 : if (!pdb || !activeTxn)
818 : : return false;
819 : 13 : int ret = activeTxn->abort();
820 : 13 : activeTxn = nullptr;
821 : 13 : return (ret == 0);
822 : : }
823 : :
824 : 1767 : bool BerkeleyDatabaseSanityCheck()
825 : : {
826 : 1767 : int major, minor;
827 : 1767 : DbEnv::version(&major, &minor, nullptr);
828 : :
829 : : /* If the major version differs, or the minor version of library is *older*
830 : : * than the header that was compiled against, flag an error.
831 : : */
832 [ + - - + ]: 1767 : if (major != DB_VERSION_MAJOR || minor < DB_VERSION_MINOR) {
833 : 0 : LogPrintf("BerkeleyDB database version conflict: header version is %d.%d, library version is %d.%d\n",
834 : : DB_VERSION_MAJOR, DB_VERSION_MINOR, major, minor);
835 : 0 : return false;
836 : : }
837 : :
838 : : return true;
839 : : }
840 : :
841 : 599 : std::string BerkeleyDatabaseVersion()
842 : : {
843 : 599 : return DbEnv::version(nullptr, nullptr, nullptr);
844 : : }
845 : :
846 : 17383 : bool BerkeleyBatch::ReadKey(DataStream&& key, DataStream& value)
847 : : {
848 [ + - ]: 17383 : if (!pdb)
849 : : return false;
850 : :
851 : 17383 : SafeDbt datKey(key.data(), key.size());
852 : :
853 [ + - ]: 17383 : SafeDbt datValue;
854 [ + - ]: 17383 : int ret = pdb->get(activeTxn, datKey, datValue, 0);
855 [ + + + - ]: 17383 : if (ret == 0 && datValue.get_data() != nullptr) {
856 [ - + ]: 16379 : value.clear();
857 [ + - ]: 16379 : value.write(SpanFromDbt(datValue));
858 : : return true;
859 : : }
860 : : return false;
861 : 17383 : }
862 : :
863 : 252415 : bool BerkeleyBatch::WriteKey(DataStream&& key, DataStream&& value, bool overwrite)
864 : : {
865 [ + - ]: 252415 : if (!pdb)
866 : : return false;
867 [ - + ]: 252415 : if (fReadOnly)
868 : 0 : assert(!"Write called on database in read-only mode");
869 : :
870 : 252415 : SafeDbt datKey(key.data(), key.size());
871 : :
872 [ + - ]: 252415 : SafeDbt datValue(value.data(), value.size());
873 : :
874 [ + + + - ]: 333130 : int ret = pdb->put(activeTxn, datKey, datValue, (overwrite ? 0 : DB_NOOVERWRITE));
875 : 252415 : return (ret == 0);
876 : 252415 : }
877 : :
878 : 16132 : bool BerkeleyBatch::EraseKey(DataStream&& key)
879 : : {
880 [ + - ]: 16132 : if (!pdb)
881 : : return false;
882 [ - + ]: 16132 : if (fReadOnly)
883 : 0 : assert(!"Erase called on database in read-only mode");
884 : :
885 : 16132 : SafeDbt datKey(key.data(), key.size());
886 : :
887 [ + - ]: 16132 : int ret = pdb->del(activeTxn, datKey, 0);
888 : 16132 : return (ret == 0 || ret == DB_NOTFOUND);
889 : 16132 : }
890 : :
891 : 4 : bool BerkeleyBatch::HasKey(DataStream&& key)
892 : : {
893 [ + - ]: 4 : if (!pdb)
894 : : return false;
895 : :
896 : 4 : SafeDbt datKey(key.data(), key.size());
897 : :
898 [ + - ]: 4 : int ret = pdb->exists(activeTxn, datKey, 0);
899 : 4 : return ret == 0;
900 : 4 : }
901 : :
902 : 12 : bool BerkeleyBatch::ErasePrefix(Span<const std::byte> prefix)
903 : : {
904 : : // Because this function erases records one by one, ensure that it is executed within a txn context.
905 : : // Otherwise, consistency is at risk; it's possible that certain records are removed while others
906 : : // remain due to an internal failure during the procedure.
907 : : // Additionally, the Dbc::del() cursor delete call below would fail without an active transaction.
908 [ + - ]: 12 : if (!Assume(activeTxn)) return false;
909 : :
910 : 12 : auto cursor{std::make_unique<BerkeleyCursor>(m_database, *this)};
911 : : // const_cast is safe below even though prefix_key is an in/out parameter,
912 : : // because we are not using the DB_DBT_USERMEM flag, so BDB will allocate
913 : : // and return a different output data pointer
914 [ + - + - ]: 12 : Dbt prefix_key{const_cast<std::byte*>(prefix.data()), static_cast<uint32_t>(prefix.size())}, prefix_value{};
915 [ + - ]: 12 : int ret{cursor->dbc()->get(&prefix_key, &prefix_value, DB_SET_RANGE)};
916 [ + + ]: 31 : for (int flag{DB_CURRENT}; ret == 0; flag = DB_NEXT) {
917 [ + - + - ]: 29 : SafeDbt key, value;
918 [ + - ]: 29 : ret = cursor->dbc()->get(key, value, flag);
919 [ + + + - : 29 : if (ret != 0 || key.get_size() < prefix.size() || memcmp(key.get_data(), prefix.data(), prefix.size()) != 0) break;
+ + ]
920 [ + - ]: 19 : ret = cursor->dbc()->del(0);
921 : 29 : }
922 [ + - ]: 12 : cursor.reset();
923 : 12 : return ret == 0 || ret == DB_NOTFOUND;
924 : 12 : }
925 : :
926 : 109460 : void BerkeleyDatabase::AddRef()
927 : : {
928 : 109460 : LOCK(cs_db);
929 [ + + ]: 109460 : if (m_refcount < 0) {
930 : 207 : m_refcount = 1;
931 : : } else {
932 : 109253 : m_refcount++;
933 : : }
934 : 109460 : }
935 : :
936 : 109457 : void BerkeleyDatabase::RemoveRef()
937 : : {
938 : 109457 : LOCK(cs_db);
939 [ + - ]: 109457 : m_refcount--;
940 [ + - ]: 109457 : if (env) env->m_db_in_use.notify_all();
941 : 109457 : }
942 : :
943 : 109440 : std::unique_ptr<DatabaseBatch> BerkeleyDatabase::MakeBatch(bool flush_on_close)
944 : : {
945 : 109440 : return std::make_unique<BerkeleyBatch>(*this, false, flush_on_close);
946 : : }
947 : :
948 : 697 : std::unique_ptr<BerkeleyDatabase> MakeBerkeleyDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error)
949 : : {
950 : 697 : fs::path data_file = BDBDataFile(path);
951 : 697 : std::unique_ptr<BerkeleyDatabase> db;
952 : 697 : {
953 [ + - ]: 697 : LOCK(cs_db); // Lock env.m_databases until insert in BerkeleyDatabase constructor
954 [ + - ]: 697 : fs::path data_filename = data_file.filename();
955 [ + - + - ]: 2091 : std::shared_ptr<BerkeleyEnvironment> env = GetBerkeleyEnv(data_file.parent_path(), options.use_shared_memory);
956 [ + + ]: 697 : if (env->m_databases.count(data_filename)) {
957 [ + - + - : 5 : error = Untranslated(strprintf("Refusing to load database. Data file '%s' is already loaded.", fs::PathToString(env->Directory() / data_filename)));
+ - + - +
- ]
958 : 1 : status = DatabaseStatus::FAILED_ALREADY_LOADED;
959 [ + - ]: 1 : return nullptr;
960 : : }
961 [ + - - + ]: 1392 : db = std::make_unique<BerkeleyDatabase>(std::move(env), std::move(data_filename), options);
962 [ + - + - ]: 1395 : }
963 : :
964 [ + + + + : 696 : if (options.verify && !db->Verify(error)) {
+ + ]
965 : 6 : status = DatabaseStatus::FAILED_VERIFY;
966 : 6 : return nullptr;
967 : : }
968 : :
969 : 689 : status = DatabaseStatus::SUCCESS;
970 : 689 : return db;
971 : 1393 : }
972 : : } // namespace wallet
|