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