Bitcoin ABC 0.33.6
P2P Digital Currency
bdb.cpp
Go to the documentation of this file.
1// Copyright (c) 2009-2010 Satoshi Nakamoto
2// Copyright (c) 2009-2020 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 <wallet/bdb.h>
7#include <wallet/db.h>
8
9#include <common/args.h>
10#include <compat/compat.h>
11#include <logging.h>
12#include <sync.h>
13#include <util/fs.h>
14#include <util/fs_helpers.h>
15#include <util/strencodings.h>
16#include <util/time.h>
17#include <util/translation.h>
18
19#include <cstdint>
20#ifndef WIN32
21#include <sys/stat.h>
22#endif
23
24namespace {
34void CheckUniqueFileid(const BerkeleyEnvironment &env,
35 const std::string &filename, Db &db,
36 WalletDatabaseFileId &fileid) {
37 if (env.IsMock()) {
38 return;
39 }
40
41 int ret = db.get_mpf()->get_fileid(fileid.value);
42 if (ret != 0) {
43 throw std::runtime_error(
44 strprintf("BerkeleyDatabase: Can't open database %s (get_fileid "
45 "failed with %d)",
46 filename, ret));
47 }
48
49 for (const auto &item : env.m_fileids) {
50 if (fileid == item.second && &fileid != &item.second) {
51 throw std::runtime_error(
52 strprintf("BerkeleyDatabase: Can't open database %s "
53 "(duplicates fileid %s "
54 "from %s)",
55 filename, HexStr(item.second.value), item.first));
56 }
57 }
58}
59
60RecursiveMutex cs_db;
61
63std::map<std::string, std::weak_ptr<BerkeleyEnvironment>>
64 g_dbenvs GUARDED_BY(cs_db);
65} // namespace
66
68 return memcmp(value, &rhs.value, sizeof(value)) == 0;
69}
70
85std::shared_ptr<BerkeleyEnvironment>
86GetWalletEnv(const fs::path &wallet_path, std::string &database_filename) {
87 fs::path env_directory;
88 SplitWalletPath(wallet_path, env_directory, database_filename);
89 LOCK(cs_db);
90 auto inserted = g_dbenvs.emplace(fs::PathToString(env_directory),
91 std::weak_ptr<BerkeleyEnvironment>());
92 if (inserted.second) {
93 auto env = std::make_shared<BerkeleyEnvironment>(env_directory);
94 inserted.first->second = env;
95 return env;
96 }
97 return inserted.first->second.lock();
98}
99
100//
101// BerkeleyBatch
102//
103
105 if (!fDbEnvInit) {
106 return;
107 }
108
109 fDbEnvInit = false;
110
111 for (auto &db : m_databases) {
112 BerkeleyDatabase &database = db.second.get();
113 assert(database.m_refcount <= 0);
114 if (database.m_db) {
115 database.m_db->close(0);
116 database.m_db.reset();
117 }
118 }
119
120 FILE *error_file = nullptr;
121 dbenv->get_errfile(&error_file);
122
123 int ret = dbenv->close(0);
124 if (ret != 0) {
125 LogPrintf("BerkeleyEnvironment::Close: Error %d closing database "
126 "environment: %s\n",
127 ret, DbEnv::strerror(ret));
128 }
129 if (!fMockDb) {
130 DbEnv(uint32_t(0)).remove(strPath.c_str(), 0);
131 }
132
133 if (error_file) {
134 fclose(error_file);
135 }
136
138}
139
141 dbenv.reset(new DbEnv(DB_CXX_NO_EXCEPTIONS));
142 fDbEnvInit = false;
143 fMockDb = false;
144}
145
147 : strPath(fs::PathToString(dir_path)) {
148 Reset();
149}
150
152 LOCK(cs_db);
153 g_dbenvs.erase(strPath);
154 Close();
155}
156
158 if (fDbEnvInit) {
159 return true;
160 }
161
163 TryCreateDirectories(pathIn);
164 if (util::LockDirectory(pathIn, ".walletlock") !=
166 LogPrintf("Cannot obtain a lock on wallet directory %s. Another "
167 "instance of bitcoin may be using it.\n",
168 strPath);
169 err = strprintf(_("Error initializing wallet database environment %s!"),
171 return false;
172 }
173
174 fs::path pathLogDir = pathIn / "database";
175 TryCreateDirectories(pathLogDir);
176 fs::path pathErrorFile = pathIn / "db.log";
177 LogPrintf("BerkeleyEnvironment::Open: LogDir=%s ErrorFile=%s\n",
178 fs::PathToString(pathLogDir), fs::PathToString(pathErrorFile));
179
180 unsigned int nEnvFlags = 0;
181 if (gArgs.GetBoolArg("-privdb", DEFAULT_WALLET_PRIVDB)) {
182 nEnvFlags |= DB_PRIVATE;
183 }
184
185 dbenv->set_lg_dir(fs::PathToString(pathLogDir).c_str());
186 // 1 MiB should be enough for just the wallet
187 dbenv->set_cachesize(0, 0x100000, 1);
188 dbenv->set_lg_bsize(0x10000);
189 dbenv->set_lg_max(1048576);
190 dbenv->set_lk_max_locks(40000);
191 dbenv->set_lk_max_objects(40000);
193 dbenv->set_errfile(fsbridge::fopen(pathErrorFile, "a"));
194 dbenv->set_flags(DB_AUTO_COMMIT, 1);
195 dbenv->set_flags(DB_TXN_WRITE_NOSYNC, 1);
196 dbenv->log_set_config(DB_LOG_AUTO_REMOVE, 1);
197 int ret =
198 dbenv->open(strPath.c_str(),
199 DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL |
200 DB_INIT_TXN | DB_THREAD | DB_RECOVER | nEnvFlags,
201 S_IRUSR | S_IWUSR);
202 if (ret != 0) {
203 LogPrintf("BerkeleyEnvironment::Open: Error %d opening database "
204 "environment: %s\n",
205 ret, DbEnv::strerror(ret));
206 int ret2 = dbenv->close(0);
207 if (ret2 != 0) {
208 LogPrintf("BerkeleyEnvironment::Open: Error %d closing failed "
209 "database environment: %s\n",
210 ret2, DbEnv::strerror(ret2));
211 }
212 Reset();
213 err = strprintf(_("Error initializing wallet database environment %s!"),
215 if (ret == DB_RUNRECOVERY) {
216 err += Untranslated(" ") +
217 _("This error could occur if this wallet was not shutdown "
218 "cleanly and was last loaded using a build with a newer "
219 "version of Berkeley DB. If so, please use the software "
220 "that last loaded this wallet");
221 }
222 return false;
223 }
224
225 fDbEnvInit = true;
226 fMockDb = false;
227 return true;
228}
229
232 Reset();
233
234 LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::MakeMock\n");
235
236 dbenv->set_cachesize(1, 0, 1);
237 dbenv->set_lg_bsize(10485760 * 4);
238 dbenv->set_lg_max(10485760);
239 dbenv->set_lk_max_locks(10000);
240 dbenv->set_lk_max_objects(10000);
241 dbenv->set_flags(DB_AUTO_COMMIT, 1);
242 dbenv->log_set_config(DB_LOG_IN_MEMORY, 1);
243 int ret =
244 dbenv->open(nullptr,
245 DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL |
246 DB_INIT_TXN | DB_THREAD | DB_PRIVATE,
247 S_IRUSR | S_IWUSR);
248 if (ret > 0) {
249 throw std::runtime_error(
250 strprintf("BerkeleyEnvironment::MakeMock: Error %d opening "
251 "database environment.",
252 ret));
253 }
254
255 fDbEnvInit = true;
256 fMockDb = true;
257}
258
260 m_dbt.set_flags(DB_DBT_MALLOC);
261}
262
263BerkeleyBatch::SafeDbt::SafeDbt(void *data, size_t size) : m_dbt(data, size) {}
264
266 if (m_dbt.get_data() != nullptr) {
267 // Clear memory, e.g. in case it was a private key
268 memory_cleanse(m_dbt.get_data(), m_dbt.get_size());
269 // under DB_DBT_MALLOC, data is malloced by the Dbt, but must be
270 // freed by the caller.
271 // https://docs.oracle.com/cd/E17275_01/html/api_reference/C/dbt.html
272 if (m_dbt.get_flags() & DB_DBT_MALLOC) {
273 free(m_dbt.get_data());
274 }
275 }
276}
277
279 return m_dbt.get_data();
280}
281
283 return m_dbt.get_size();
284}
285
286BerkeleyBatch::SafeDbt::operator Dbt *() {
287 return &m_dbt;
288}
289
291 fs::path walletDir = env->Directory();
292 fs::path file_path = walletDir / strFile;
293
294 LogPrintf("Using BerkeleyDB version %s\n", BerkeleyDatabaseVersion());
295 LogPrintf("Using wallet %s\n", fs::PathToString(file_path));
296
297 if (!env->Open(errorStr)) {
298 return false;
299 }
300
301 if (fs::exists(file_path)) {
302 assert(m_refcount == 0);
303
304 Db db(env->dbenv.get(), 0);
305 int result = db.verify(strFile.c_str(), nullptr, nullptr, 0);
306 if (result != 0) {
307 errorStr =
308 strprintf(_("%s corrupt. Try using the wallet tool "
309 "bitcoin-wallet to salvage or restoring a backup."),
310 fs::quoted(fs::PathToString(file_path)));
311 return false;
312 }
313 }
314 // also return true if files does not exists
315 return true;
316}
317
319 dbenv->txn_checkpoint(0, 0, 0);
320 if (fMockDb) {
321 return;
322 }
323 dbenv->lsn_reset(strFile.c_str(), 0);
324}
325
327 if (env) {
328 LOCK(cs_db);
330 assert(!m_db);
331 size_t erased = env->m_databases.erase(strFile);
332 assert(erased == 1);
333 env->m_fileids.erase(strFile);
334 }
335}
336
337BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase &database, const bool read_only,
338 bool fFlushOnCloseIn)
339 : pdb(nullptr), activeTxn(nullptr), m_cursor(nullptr),
340 m_database(database) {
341 database.AddRef();
342 database.Open();
343 fReadOnly = read_only;
344 fFlushOnClose = fFlushOnCloseIn;
345 env = database.env.get();
346 pdb = database.m_db.get();
347 strFile = database.strFile;
348}
349
351 unsigned int nFlags = DB_THREAD | DB_CREATE;
352
353 {
354 LOCK(cs_db);
355 bilingual_str open_err;
356 if (!env->Open(open_err)) {
357 throw std::runtime_error(
358 "BerkeleyDatabase: Failed to open database environment.");
359 }
360
361 if (m_db == nullptr) {
362 int ret;
363 std::unique_ptr<Db> pdb_temp =
364 std::make_unique<Db>(env->dbenv.get(), 0);
365
366 bool fMockDb = env->IsMock();
367 if (fMockDb) {
368 DbMpoolFile *mpf = pdb_temp->get_mpf();
369 ret = mpf->set_flags(DB_MPOOL_NOFILE, 1);
370 if (ret != 0) {
371 throw std::runtime_error(strprintf(
372 "BerkeleyDatabase: Failed to configure for no "
373 "temp file backing for database %s",
374 strFile));
375 }
376 }
377
378 ret = pdb_temp->open(
379 nullptr, // Txn pointer
380 fMockDb ? nullptr : strFile.c_str(), // Filename
381 fMockDb ? strFile.c_str() : "main", // Logical db name
382 DB_BTREE, // Database type
383 nFlags, // Flags
384 0);
385
386 if (ret != 0) {
387 throw std::runtime_error(strprintf(
388 "BerkeleyDatabase: Error %d, can't open database %s", ret,
389 strFile));
390 }
391
392 // Call CheckUniqueFileid on the containing BDB environment to
393 // avoid BDB data consistency bugs that happen when different data
394 // files in the same environment have the same fileid.
395 CheckUniqueFileid(*env, strFile, *pdb_temp,
396 this->env->m_fileids[strFile]);
397
398 m_db.reset(pdb_temp.release());
399 }
400 }
401}
402
404 if (activeTxn) {
405 return;
406 }
407
408 // Flush database activity from memory pool to disk log
409 unsigned int nMinutes = 0;
410 if (fReadOnly) {
411 nMinutes = 1;
412 }
413
414 // env is nullptr for dummy databases (i.e. in tests). Don't actually flush
415 // if env is nullptr so we don't segfault
416 if (env) {
417 env->dbenv->txn_checkpoint(
418 nMinutes
419 ? gArgs.GetIntArg("-dblogsize", DEFAULT_WALLET_DBLOGSIZE) * 1024
420 : 0,
421 nMinutes, 0);
422 }
423}
424
427}
428
430 Close();
432}
433
435 if (!pdb) {
436 return;
437 }
438 if (activeTxn) {
439 activeTxn->abort();
440 }
441 activeTxn = nullptr;
442 pdb = nullptr;
443 CloseCursor();
444
445 if (fFlushOnClose) {
446 Flush();
447 }
448}
449
450void BerkeleyEnvironment::CloseDb(const std::string &strFile) {
451 LOCK(cs_db);
452 auto it = m_databases.find(strFile);
453 assert(it != m_databases.end());
454 BerkeleyDatabase &database = it->second.get();
455 if (database.m_db) {
456 // Close the database handle
457 database.m_db->close(0);
458 database.m_db.reset();
459 }
460}
461
463 // Make sure that no Db's are in use
464 AssertLockNotHeld(cs_db);
465 std::unique_lock<RecursiveMutex> lock(cs_db);
466 m_db_in_use.wait(lock, [this]() {
467 for (auto &db : m_databases) {
468 if (db.second.get().m_refcount > 0) {
469 return false;
470 }
471 }
472 return true;
473 });
474
475 std::vector<std::string> filenames;
476 filenames.reserve(m_databases.size());
477 for (const auto &it : m_databases) {
478 filenames.push_back(it.first);
479 }
480 // Close the individual Db's
481 for (const std::string &filename : filenames) {
482 CloseDb(filename);
483 }
484 // Reset the environment
485 // This will flush and close the environment
486 Flush(true);
487 Reset();
488 bilingual_str open_err;
489 Open(open_err);
490}
491
492bool BerkeleyDatabase::Rewrite(const char *pszSkip) {
493 while (true) {
494 {
495 LOCK(cs_db);
496 if (m_refcount <= 0) {
497 // Flush log data to the dat file
498 env->CloseDb(strFile);
499 env->CheckpointLSN(strFile);
500 m_refcount = -1;
501
502 bool fSuccess = true;
503 LogPrintf("BerkeleyBatch::Rewrite: Rewriting %s...\n", strFile);
504 std::string strFileRes = strFile + ".rewrite";
505 { // surround usage of db with extra {}
506 BerkeleyBatch db(*this, true);
507 std::unique_ptr<Db> pdbCopy =
508 std::make_unique<Db>(env->dbenv.get(), 0);
509
510 int ret = pdbCopy->open(nullptr, // Txn pointer
511 strFileRes.c_str(), // Filename
512 "main", // Logical db name
513 DB_BTREE, // Database type
514 DB_CREATE, // Flags
515 0);
516 if (ret > 0) {
517 LogPrintf("BerkeleyBatch::Rewrite: Can't create "
518 "database file %s\n",
519 strFileRes);
520 fSuccess = false;
521 }
522
523 if (db.StartCursor()) {
524 while (fSuccess) {
525 DataStream ssKey{};
526 DataStream ssValue{};
527 bool complete;
528 bool ret1 =
529 db.ReadAtCursor(ssKey, ssValue, complete);
530 if (complete) {
531 break;
532 }
533 if (!ret1) {
534 fSuccess = false;
535 break;
536 }
537 if (pszSkip &&
538 strncmp((const char *)ssKey.data(), pszSkip,
539 std::min(ssKey.size(),
540 strlen(pszSkip))) == 0) {
541 continue;
542 }
543 if (strncmp((const char *)ssKey.data(),
544 "\x07version", 8) == 0) {
545 // Update version:
546 ssValue.clear();
547 ssValue << CLIENT_VERSION;
548 }
549 Dbt datKey(ssKey.data(), ssKey.size());
550 Dbt datValue(ssValue.data(), ssValue.size());
551 int ret2 = pdbCopy->put(nullptr, &datKey, &datValue,
552 DB_NOOVERWRITE);
553 if (ret2 > 0) {
554 fSuccess = false;
555 }
556 }
557 db.CloseCursor();
558 }
559 if (fSuccess) {
560 db.Close();
561 env->CloseDb(strFile);
562 if (pdbCopy->close(0)) {
563 fSuccess = false;
564 }
565 } else {
566 pdbCopy->close(0);
567 }
568 }
569 if (fSuccess) {
570 Db dbA(env->dbenv.get(), 0);
571 if (dbA.remove(strFile.c_str(), nullptr, 0)) {
572 fSuccess = false;
573 }
574 Db dbB(env->dbenv.get(), 0);
575 if (dbB.rename(strFileRes.c_str(), nullptr, strFile.c_str(),
576 0)) {
577 fSuccess = false;
578 }
579 }
580 if (!fSuccess) {
581 LogPrintf("BerkeleyBatch::Rewrite: Failed to rewrite "
582 "database file %s\n",
583 strFileRes);
584 }
585 return fSuccess;
586 }
587 }
588 UninterruptibleSleep(std::chrono::milliseconds{100});
589 }
590}
591
592void BerkeleyEnvironment::Flush(bool fShutdown) {
593 int64_t nStart = GetTimeMillis();
594 // Flush log data to the actual data file on all files that are not in use
595 LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: [%s] Flush(%s)%s\n",
596 strPath, fShutdown ? "true" : "false",
597 fDbEnvInit ? "" : " database not started");
598 if (!fDbEnvInit) {
599 return;
600 }
601 {
602 LOCK(cs_db);
603 bool no_dbs_accessed = true;
604 for (auto &db_it : m_databases) {
605 std::string strFile = db_it.first;
606 int nRefCount = db_it.second.get().m_refcount;
607 if (nRefCount < 0) {
608 continue;
609 }
610 LogPrint(
612 "BerkeleyEnvironment::Flush: Flushing %s (refcount = %d)...\n",
613 strFile, nRefCount);
614 if (nRefCount == 0) {
615 // Move log data to the dat file
616 CloseDb(strFile);
618 "BerkeleyEnvironment::Flush: %s checkpoint\n",
619 strFile);
620 dbenv->txn_checkpoint(0, 0, 0);
622 "BerkeleyEnvironment::Flush: %s detach\n", strFile);
623 if (!fMockDb) {
624 dbenv->lsn_reset(strFile.c_str(), 0);
625 }
627 "BerkeleyEnvironment::Flush: %s closed\n", strFile);
628 nRefCount = -1;
629 } else {
630 no_dbs_accessed = false;
631 }
632 }
634 "BerkeleyEnvironment::Flush: Flush(%s)%s took %15dms\n",
635 fShutdown ? "true" : "false",
636 fDbEnvInit ? "" : " database not started",
637 GetTimeMillis() - nStart);
638 if (fShutdown) {
639 char **listp;
640 if (no_dbs_accessed) {
641 dbenv->log_archive(&listp, DB_ARCH_REMOVE);
642 Close();
643 if (!fMockDb) {
644 fs::remove_all(fs::PathFromString(strPath) / "database");
645 }
646 }
647 }
648 }
649}
650
652 // Don't flush if we can't acquire the lock.
653 TRY_LOCK(cs_db, lockDb);
654 if (!lockDb) {
655 return false;
656 }
657
658 // Don't flush if any databases are in use
659 for (auto &it : env->m_databases) {
660 if (it.second.get().m_refcount > 0) {
661 return false;
662 }
663 }
664
665 // Don't flush if there haven't been any batch writes for this database.
666 if (m_refcount < 0) {
667 return false;
668 }
669
670 LogPrint(BCLog::WALLETDB, "Flushing %s\n", strFile);
671 int64_t nStart = GetTimeMillis();
672
673 // Flush wallet file so it's self contained
674 env->CloseDb(strFile);
675 env->CheckpointLSN(strFile);
676 m_refcount = -1;
677
678 LogPrint(BCLog::WALLETDB, "Flushed %s %dms\n", strFile,
679 GetTimeMillis() - nStart);
680
681 return true;
682}
683
684bool BerkeleyDatabase::Backup(const std::string &strDest) const {
685 while (true) {
686 {
687 LOCK(cs_db);
688 if (m_refcount <= 0) {
689 // Flush log data to the dat file
690 env->CloseDb(strFile);
691 env->CheckpointLSN(strFile);
692
693 // Copy wallet file.
694 fs::path pathSrc = env->Directory() / strFile;
695 fs::path pathDest(fs::PathFromString(strDest));
696 if (fs::is_directory(pathDest)) {
697 pathDest /= fs::PathFromString(strFile);
698 }
699
700 try {
701 if (fs::exists(pathDest) &&
702 fs::equivalent(pathSrc, pathDest)) {
703 LogPrintf("cannot backup to wallet source file %s\n",
704 fs::PathToString(pathDest));
705 return false;
706 }
707
708 fs::copy_file(pathSrc, pathDest,
709 fs::copy_options::overwrite_existing);
710 LogPrintf("copied %s to %s\n", strFile,
711 fs::PathToString(pathDest));
712 return true;
713 } catch (const fs::filesystem_error &e) {
714 LogPrintf("error copying %s to %s - %s\n", strFile,
715 fs::PathToString(pathDest),
717 return false;
718 }
719 }
720 }
721 UninterruptibleSleep(std::chrono::milliseconds{100});
722 }
723}
724
726 env->Flush(false);
727}
728
730 env->Flush(true);
731}
732
734 env->ReloadDbEnv();
735}
736
739 if (!pdb) {
740 return false;
741 }
742 int ret = pdb->cursor(nullptr, &m_cursor, 0);
743 return ret == 0;
744}
745
747 bool &complete) {
748 complete = false;
749 if (m_cursor == nullptr) {
750 return false;
751 }
752 // Read at cursor
753 SafeDbt datKey;
754 SafeDbt datValue;
755 int ret = m_cursor->get(datKey, datValue, DB_NEXT);
756 if (ret == DB_NOTFOUND) {
757 complete = true;
758 }
759 if (ret != 0) {
760 return false;
761 } else if (datKey.get_data() == nullptr || datValue.get_data() == nullptr) {
762 return false;
763 }
764
765 // Convert to streams
766 ssKey.clear();
767 ssKey.write({BytePtr(datKey.get_data()), datKey.get_size()});
768 ssValue.clear();
769 ssValue.write({BytePtr(datValue.get_data()), datValue.get_size()});
770 return true;
771}
772
774 if (!m_cursor) {
775 return;
776 }
777 m_cursor->close();
778 m_cursor = nullptr;
779}
780
782 if (!pdb || activeTxn) {
783 return false;
784 }
785 DbTxn *ptxn = env->TxnBegin();
786 if (!ptxn) {
787 return false;
788 }
789 activeTxn = ptxn;
790 return true;
791}
792
794 if (!pdb || !activeTxn) {
795 return false;
796 }
797 int ret = activeTxn->commit(0);
798 activeTxn = nullptr;
799 return (ret == 0);
800}
801
803 if (!pdb || !activeTxn) {
804 return false;
805 }
806 int ret = activeTxn->abort();
807 activeTxn = nullptr;
808 return (ret == 0);
809}
810
812 return DbEnv::version(nullptr, nullptr, nullptr);
813}
814
816 if (!pdb) {
817 return false;
818 }
819
820 SafeDbt datKey(key.data(), key.size());
821
822 SafeDbt datValue;
823 int ret = pdb->get(activeTxn, datKey, datValue, 0);
824 if (ret == 0 && datValue.get_data() != nullptr) {
825 value.write({BytePtr(datValue.get_data()), datValue.get_size()});
826 return true;
827 }
828 return false;
829}
830
832 bool overwrite) {
833 if (!pdb) {
834 return false;
835 }
836
837 if (fReadOnly) {
838 assert(!"Write called on database in read-only mode");
839 }
840
841 SafeDbt datKey(key.data(), key.size());
842
843 SafeDbt datValue(value.data(), value.size());
844
845 int ret =
846 pdb->put(activeTxn, datKey, datValue, (overwrite ? 0 : DB_NOOVERWRITE));
847 return (ret == 0);
848}
849
851 if (!pdb) {
852 return false;
853 }
854 if (fReadOnly) {
855 assert(!"Erase called on database in read-only mode");
856 }
857
858 SafeDbt datKey(key.data(), key.size());
859
860 int ret = pdb->del(activeTxn, datKey, 0);
861 return (ret == 0 || ret == DB_NOTFOUND);
862}
863
865 if (!pdb) {
866 return false;
867 }
868
869 SafeDbt datKey(key.data(), key.size());
870
871 int ret = pdb->exists(activeTxn, datKey, 0);
872 return ret == 0;
873}
874
876 LOCK(cs_db);
877 if (m_refcount < 0) {
878 m_refcount = 1;
879 } else {
880 m_refcount++;
881 }
882}
883
885 LOCK(cs_db);
886 m_refcount--;
887 if (env) {
888 env->m_db_in_use.notify_all();
889 }
890}
891
892std::unique_ptr<DatabaseBatch>
893BerkeleyDatabase::MakeBatch(bool flush_on_close) {
894 return std::make_unique<BerkeleyBatch>(*this, false, flush_on_close);
895}
896
898 fs::path env_directory;
899 std::string data_filename;
900 SplitWalletPath(path, env_directory, data_filename);
901 return IsBerkeleyBtree(env_directory / data_filename);
902}
903
904std::unique_ptr<BerkeleyDatabase>
906 DatabaseStatus &status, bilingual_str &error) {
907 std::unique_ptr<BerkeleyDatabase> db;
908 {
909 // Lock env.m_databases until insert in BerkeleyDatabase constructor
910 LOCK(cs_db);
911 std::string data_filename;
912 std::shared_ptr<BerkeleyEnvironment> env =
913 GetWalletEnv(path, data_filename);
914 if (!env) {
915 error = Untranslated(
916 strprintf("Failed to load database. Data file '%s' is in the "
917 "process of being unloaded. Try again later.",
918 fs::PathToString(env->Directory() / data_filename)));
920 return nullptr;
921 }
922 if (env->m_databases.count(data_filename)) {
923 error = Untranslated(strprintf(
924 "Refusing to load database. Data file '%s' is already loaded.",
925 fs::PathToString(env->Directory() / data_filename)));
927 return nullptr;
928 }
929 db = std::make_unique<BerkeleyDatabase>(std::move(env),
930 std::move(data_filename));
931 }
932
933 if (options.verify && !db->Verify(error)) {
935 return nullptr;
936 }
937
939 return db;
940}
ArgsManager gArgs
Definition: args.cpp:39
bool ExistsBerkeleyDatabase(const fs::path &path)
Check if Berkeley database exists at specified path.
Definition: bdb.cpp:897
std::unique_ptr< BerkeleyDatabase > MakeBerkeleyDatabase(const fs::path &path, const DatabaseOptions &options, DatabaseStatus &status, bilingual_str &error)
Return object giving access to Berkeley database at specified path.
Definition: bdb.cpp:905
std::string BerkeleyDatabaseVersion()
Definition: bdb.cpp:811
std::shared_ptr< BerkeleyEnvironment > GetWalletEnv(const fs::path &wallet_path, std::string &database_filename)
Get BerkeleyEnvironment and database filename given a wallet path.
Definition: bdb.cpp:86
static const unsigned int DEFAULT_WALLET_DBLOGSIZE
Definition: bdb.h:28
static const bool DEFAULT_WALLET_PRIVDB
Definition: bdb.h:29
bool IsBerkeleyBtree(const fs::path &path)
Check format of database file.
Definition: walletutil.cpp:35
int64_t GetIntArg(const std::string &strArg, int64_t nDefault) const
Return integer argument or default value.
Definition: args.cpp:494
bool GetBoolArg(const std::string &strArg, bool fDefault) const
Return boolean argument or default value.
Definition: args.cpp:524
RAII class that automatically cleanses its data on destruction.
Definition: bdb.h:189
uint32_t get_size() const
Definition: bdb.cpp:282
const void * get_data() const
Definition: bdb.cpp:278
RAII class that provides access to a Berkeley database.
Definition: bdb.h:187
bool HasKey(DataStream &&key) override
Definition: bdb.cpp:864
void Close() override
Definition: bdb.cpp:434
bool ReadKey(DataStream &&key, DataStream &value) override
Definition: bdb.cpp:815
std::string strFile
Definition: bdb.h:216
bool TxnCommit() override
Definition: bdb.cpp:793
void Flush() override
Definition: bdb.cpp:403
bool ReadAtCursor(DataStream &ssKey, DataStream &ssValue, bool &complete) override
Definition: bdb.cpp:746
bool StartCursor() override
Definition: bdb.cpp:737
void CloseCursor() override
Definition: bdb.cpp:773
bool WriteKey(DataStream &&key, DataStream &&value, bool overwrite=true) override
Definition: bdb.cpp:831
BerkeleyBatch(BerkeleyDatabase &database, const bool fReadOnly, bool fFlushOnCloseIn=true)
Definition: bdb.cpp:337
bool EraseKey(DataStream &&key) override
Definition: bdb.cpp:850
BerkeleyEnvironment * env
Definition: bdb.h:221
bool TxnAbort() override
Definition: bdb.cpp:802
Db * pdb
Definition: bdb.h:215
~BerkeleyBatch() override
Definition: bdb.cpp:429
DbTxn * activeTxn
Definition: bdb.h:217
bool fFlushOnClose
Definition: bdb.h:220
BerkeleyDatabase & m_database
Definition: bdb.h:222
bool fReadOnly
Definition: bdb.h:219
Dbc * m_cursor
Definition: bdb.h:218
bool TxnBegin() override
Definition: bdb.cpp:781
An instance of this class represents one database.
Definition: bdb.h:94
std::shared_ptr< BerkeleyEnvironment > env
Pointer to shared database environment.
Definition: bdb.h:171
void IncrementUpdateCounter() override
Definition: bdb.cpp:425
void ReloadDbEnv() override
Definition: bdb.cpp:733
std::unique_ptr< DatabaseBatch > MakeBatch(bool flush_on_close=true) override
Make a BerkeleyBatch connected to this database.
Definition: bdb.cpp:893
~BerkeleyDatabase() override
Definition: bdb.cpp:326
bool Rewrite(const char *pszSkip=nullptr) override
Rewrite the entire database on disk, with the exception of key pszSkip if non-zero.
Definition: bdb.cpp:492
std::string strFile
Definition: bdb.h:179
void AddRef() override
Indicate the a new database user has began using the database.
Definition: bdb.cpp:875
void Flush() override
Make sure all changes are flushed to database file.
Definition: bdb.cpp:725
void Open() override
Open the database if it is not already opened.
Definition: bdb.cpp:350
bool PeriodicFlush() override
flush the wallet passively (TRY_LOCK) ideal to be called periodically
Definition: bdb.cpp:651
void RemoveRef() override
Indicate that database user has stopped using the database and that it could be flushed or closed.
Definition: bdb.cpp:884
void Close() override
Flush to the database file and close the database.
Definition: bdb.cpp:729
std::unique_ptr< Db > m_db
Database pointer.
Definition: bdb.h:177
bool Verify(bilingual_str &error)
Verifies the environment and database file.
Definition: bdb.cpp:290
bool Backup(const std::string &strDest) const override
Back up the entire database to a file.
Definition: bdb.cpp:684
std::unordered_map< std::string, WalletDatabaseFileId > m_fileids
Definition: bdb.h:50
DbTxn * TxnBegin(int flags=DB_TXN_WRITE_NOSYNC)
Definition: bdb.h:71
fs::path Directory() const
Definition: bdb.h:61
bool IsMock() const
Definition: bdb.h:59
void ReloadDbEnv()
Definition: bdb.cpp:462
std::map< std::string, std::reference_wrapper< BerkeleyDatabase > > m_databases
Definition: bdb.h:49
bool fDbEnvInit
Definition: bdb.h:40
bool Open(bilingual_str &error)
Definition: bdb.cpp:157
std::string strPath
Definition: bdb.h:45
std::unique_ptr< DbEnv > dbenv
Definition: bdb.h:48
std::condition_variable_any m_db_in_use
Definition: bdb.h:51
void CheckpointLSN(const std::string &strFile)
Definition: bdb.cpp:318
void Flush(bool fShutdown)
Definition: bdb.cpp:592
bool fMockDb
Definition: bdb.h:41
BerkeleyEnvironment()
Construct an in-memory mock Berkeley environment for testing.
Definition: bdb.cpp:231
void CloseDb(const std::string &strFile)
Definition: bdb.cpp:450
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:118
void write(Span< const value_type > src)
Definition: streams.h:290
void clear()
Definition: streams.h:161
std::atomic< unsigned int > nUpdateCounter
Definition: db.h:156
std::atomic< int > m_refcount
Counts the number of active database users to be sure that the database is not closed while someone i...
Definition: db.h:113
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
Definition: fs.h:30
void memory_cleanse(void *ptr, size_t len)
Secure overwrite a buffer (possibly containing secret data) with zero-bytes.
Definition: cleanse.cpp:14
static constexpr int CLIENT_VERSION
bitcoind-res.rc includes this file, but it cannot cope with real c++ code.
Definition: clientversion.h:38
void UnlockDirectory(const fs::path &directory, const std::string &lockfile_name)
Definition: fs_helpers.cpp:86
bool TryCreateDirectories(const fs::path &p)
Ignores exceptions thrown by create_directories if the requested directory exists.
Definition: fs_helpers.cpp:269
std::string HexStr(const Span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
Definition: hex_base.cpp:30
#define LogPrint(category,...)
Definition: logging.h:452
#define LogPrintf(...)
Definition: logging.h:424
@ WALLETDB
Definition: logging.h:75
Filesystem operations and types.
Definition: fs.h:20
static auto quoted(const std::string &s)
Definition: fs.h:112
static bool exists(const path &p)
Definition: fs.h:107
static bool copy_file(const path &from, const path &to, copy_options options)
Definition: fs.h:124
static std::string PathToString(const path &path)
Convert path object to byte string.
Definition: fs.h:147
static path PathFromString(const std::string &string)
Convert byte string to path object.
Definition: fs.h:170
FILE * fopen(const fs::path &p, const char *mode)
Definition: fs.cpp:30
std::string get_filesystem_error_message(const fs::filesystem_error &e)
Definition: fs.cpp:133
LockResult LockDirectory(const fs::path &directory, const std::string lockfile_name, bool probe_only)
Definition: fs_helpers.cpp:56
CAddrDb db
Definition: main.cpp:35
const std::byte * BytePtr(const void *data)
Convert a data pointer to a std::byte data pointer.
Definition: span.h:287
bool verify
Definition: db.h:224
bool operator==(const WalletDatabaseFileId &rhs) const
Definition: bdb.cpp:67
uint8_t value[DB_FILE_ID_LEN]
Definition: bdb.h:32
Bilingual messages:
Definition: translation.h:17
#define AssertLockNotHeld(cs)
Definition: sync.h:163
#define LOCK(cs)
Definition: sync.h:306
#define TRY_LOCK(cs, name)
Definition: sync.h:314
#define GUARDED_BY(x)
Definition: threadsafety.h:45
int64_t GetTimeMillis()
Returns the system time (not mockable)
Definition: time.cpp:76
void UninterruptibleSleep(const std::chrono::microseconds &n)
Definition: time.cpp:21
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1202
bilingual_str _(const char *psz)
Translation function.
Definition: translation.h:68
bilingual_str Untranslated(std::string original)
Mark a bilingual_str as untranslated.
Definition: translation.h:36
static const char * filenames[]
Definition: unitester.cpp:68
assert(!tx.IsCoinBase())
void SplitWalletPath(const fs::path &wallet_path, fs::path &env_directory, std::string &database_filename)
Definition: db.cpp:11
DatabaseStatus
Definition: db.h:227