Bitcoin Core 28.99.0
P2P Digital Currency
bdb.cpp
Go to the documentation of this file.
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
33static_assert(BDB_DB_FILE_ID_LEN == DB_FILE_ID_LEN, "DB_FILE_ID_LEN should be 20.");
34
35namespace wallet {
36namespace {
37
47void CheckUniqueFileid(const BerkeleyEnvironment& env, const std::string& filename, Db& db, WalletDatabaseFileId& fileid)
48{
49 if (env.IsMock()) return;
50
51 int ret = db.get_mpf()->get_fileid(fileid.value);
52 if (ret != 0) {
53 throw std::runtime_error(strprintf("BerkeleyDatabase: Can't open database %s (get_fileid failed with %d)", filename, ret));
54 }
55
56 for (const auto& item : env.m_fileids) {
57 if (fileid == item.second && &fileid != &item.second) {
58 throw std::runtime_error(strprintf("BerkeleyDatabase: Can't open database %s (duplicates fileid %s from %s)", filename,
59 HexStr(item.second.value), item.first));
60 }
61 }
62}
63
64RecursiveMutex cs_db;
65std::map<std::string, std::weak_ptr<BerkeleyEnvironment>> g_dbenvs GUARDED_BY(cs_db);
66} // namespace
67
68static constexpr auto REVERSE_BYTE_ORDER{std::endian::native == std::endian::little ? 4321 : 1234};
69
71{
72 return memcmp(value, &rhs.value, sizeof(value)) == 0;
73}
74
81std::shared_ptr<BerkeleyEnvironment> GetBerkeleyEnv(const fs::path& env_directory, bool use_shared_memory)
82{
83 LOCK(cs_db);
84 auto inserted = g_dbenvs.emplace(fs::PathToString(env_directory), std::weak_ptr<BerkeleyEnvironment>());
85 if (inserted.second) {
86 auto env = std::make_shared<BerkeleyEnvironment>(env_directory, use_shared_memory);
87 inserted.first->second = env;
88 return env;
89 }
90 return inserted.first->second.lock();
91}
92
93//
94// BerkeleyBatch
95//
96
98{
99 if (!fDbEnvInit)
100 return;
101
102 fDbEnvInit = false;
103
104 for (auto& db : m_databases) {
105 BerkeleyDatabase& database = db.second.get();
106 assert(database.m_refcount <= 0);
107 if (database.m_db) {
108 database.m_db->close(0);
109 database.m_db.reset();
110 }
111 }
112
113 FILE* error_file = nullptr;
114 dbenv->get_errfile(&error_file);
115
116 int ret = dbenv->close(0);
117 if (ret != 0)
118 LogPrintf("BerkeleyEnvironment::Close: Error %d closing database environment: %s\n", ret, DbEnv::strerror(ret));
119 if (!fMockDb)
120 DbEnv(uint32_t{0}).remove(strPath.c_str(), 0);
121
122 if (error_file) fclose(error_file);
123
125}
126
128{
129 dbenv.reset(new DbEnv(DB_CXX_NO_EXCEPTIONS));
130 fDbEnvInit = false;
131 fMockDb = false;
132}
133
134BerkeleyEnvironment::BerkeleyEnvironment(const fs::path& dir_path, bool use_shared_memory) : strPath(fs::PathToString(dir_path)), m_use_shared_memory(use_shared_memory)
135{
136 Reset();
137}
138
140{
141 LOCK(cs_db);
142 g_dbenvs.erase(strPath);
143 Close();
144}
145
147{
148 if (fDbEnvInit) {
149 return true;
150 }
151
153 TryCreateDirectories(pathIn);
154 if (util::LockDirectory(pathIn, ".walletlock") != util::LockResult::Success) {
155 LogPrintf("Cannot obtain a lock on wallet directory %s. Another instance may be using it.\n", strPath);
156 err = strprintf(_("Error initializing wallet database environment %s!"), fs::quoted(fs::PathToString(Directory())));
157 return false;
158 }
159
160 fs::path pathLogDir = pathIn / "database";
161 TryCreateDirectories(pathLogDir);
162 fs::path pathErrorFile = pathIn / "db.log";
163 LogPrintf("BerkeleyEnvironment::Open: LogDir=%s ErrorFile=%s\n", fs::PathToString(pathLogDir), fs::PathToString(pathErrorFile));
164
165 unsigned int nEnvFlags = 0;
166 if (!m_use_shared_memory) {
167 nEnvFlags |= DB_PRIVATE;
168 }
169
170 dbenv->set_lg_dir(fs::PathToString(pathLogDir).c_str());
171 dbenv->set_cachesize(0, 0x100000, 1); // 1 MiB should be enough for just the wallet
172 dbenv->set_lg_bsize(0x10000);
173 dbenv->set_lg_max(1048576);
174 dbenv->set_lk_max_locks(40000);
175 dbenv->set_lk_max_objects(40000);
176 dbenv->set_errfile(fsbridge::fopen(pathErrorFile, "a"));
177 dbenv->set_flags(DB_AUTO_COMMIT, 1);
178 dbenv->set_flags(DB_TXN_WRITE_NOSYNC, 1);
179 dbenv->log_set_config(DB_LOG_AUTO_REMOVE, 1);
180 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 S_IRUSR | S_IWUSR);
190 if (ret != 0) {
191 LogPrintf("BerkeleyEnvironment::Open: Error %d opening database environment: %s\n", ret, DbEnv::strerror(ret));
192 int ret2 = dbenv->close(0);
193 if (ret2 != 0) {
194 LogPrintf("BerkeleyEnvironment::Open: Error %d closing failed database environment: %s\n", ret2, DbEnv::strerror(ret2));
195 }
196 Reset();
197 err = strprintf(_("Error initializing wallet database environment %s!"), fs::quoted(fs::PathToString(Directory())));
198 if (ret == DB_RUNRECOVERY) {
199 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 return false;
202 }
203
204 fDbEnvInit = true;
205 fMockDb = false;
206 return true;
207}
208
210BerkeleyEnvironment::BerkeleyEnvironment() : m_use_shared_memory(false)
211{
212 Reset();
213
214 LogDebug(BCLog::WALLETDB, "BerkeleyEnvironment::MakeMock\n");
215
216 dbenv->set_cachesize(1, 0, 1);
217 dbenv->set_lg_bsize(10485760 * 4);
218 dbenv->set_lg_max(10485760);
219 dbenv->set_lk_max_locks(10000);
220 dbenv->set_lk_max_objects(10000);
221 dbenv->set_flags(DB_AUTO_COMMIT, 1);
222 dbenv->log_set_config(DB_LOG_IN_MEMORY, 1);
223 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 S_IRUSR | S_IWUSR);
232 if (ret > 0) {
233 throw std::runtime_error(strprintf("BerkeleyEnvironment::MakeMock: Error %d opening database environment.", ret));
234 }
235
236 fDbEnvInit = true;
237 fMockDb = true;
238}
239
241class SafeDbt final
242{
243 Dbt m_dbt;
244
245public:
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
261{
262 m_dbt.set_flags(DB_DBT_MALLOC);
263}
264
265SafeDbt::SafeDbt(void* data, size_t size)
266 : m_dbt(data, size)
267{
268}
269
271{
272 if (m_dbt.get_data() != nullptr) {
273 // Clear memory, e.g. in case it was a private key
274 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 if (m_dbt.get_flags() & DB_DBT_MALLOC) {
279 free(m_dbt.get_data());
280 }
281 }
282}
283
284const void* SafeDbt::get_data() const
285{
286 return m_dbt.get_data();
287}
288
289uint32_t SafeDbt::get_size() const
290{
291 return m_dbt.get_size();
292}
293
294SafeDbt::operator Dbt*()
295{
296 return &m_dbt;
297}
298
300{
301 return {reinterpret_cast<const std::byte*>(dbt.get_data()), dbt.get_size()};
302}
303
304BerkeleyDatabase::BerkeleyDatabase(std::shared_ptr<BerkeleyEnvironment> env, fs::path filename, const DatabaseOptions& options) :
306 env(std::move(env)),
307 m_byteswap(options.require_format == DatabaseFormat::BERKELEY_SWAP),
308 m_filename(std::move(filename)),
309 m_max_log_mb(options.max_log_mb)
310{
311 auto inserted = this->env->m_databases.emplace(m_filename, std::ref(*this));
312 assert(inserted.second);
313}
314
316{
317 fs::path walletDir = env->Directory();
318 fs::path file_path = walletDir / m_filename;
319
320 LogPrintf("Using BerkeleyDB version %s\n", BerkeleyDatabaseVersion());
321 LogPrintf("Using wallet %s\n", fs::PathToString(file_path));
322
323 if (!env->Open(errorStr)) {
324 return false;
325 }
326
327 if (fs::exists(file_path))
328 {
329 assert(m_refcount == 0);
330
331 Db db(env->dbenv.get(), 0);
332 const std::string strFile = fs::PathToString(m_filename);
333 int result = db.verify(strFile.c_str(), nullptr, nullptr, 0);
334 if (result != 0) {
335 errorStr = strprintf(_("%s corrupt. Try using the wallet tool bitcoin-wallet to salvage or restoring a backup."), fs::quoted(fs::PathToString(file_path)));
336 return false;
337 }
338 }
339 // also return true if files does not exists
340 return true;
341}
342
343void BerkeleyEnvironment::CheckpointLSN(const std::string& strFile)
344{
345 dbenv->txn_checkpoint(0, 0, 0);
346 if (fMockDb)
347 return;
348 dbenv->lsn_reset(strFile.c_str(), 0);
349}
350
352{
353 if (env) {
354 LOCK(cs_db);
355 env->CloseDb(m_filename);
356 assert(!m_db);
357 size_t erased = env->m_databases.erase(m_filename);
358 assert(erased == 1);
359 env->m_fileids.erase(fs::PathToString(m_filename));
360 }
361}
362
363BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase& database, const bool read_only, bool fFlushOnCloseIn) : m_database(database)
364{
365 database.AddRef();
366 database.Open();
367 fReadOnly = read_only;
368 fFlushOnClose = fFlushOnCloseIn;
369 env = database.env.get();
370 pdb = database.m_db.get();
372}
373
375{
376 unsigned int nFlags = DB_THREAD | DB_CREATE;
377
378 {
379 LOCK(cs_db);
380 bilingual_str open_err;
381 if (!env->Open(open_err))
382 throw std::runtime_error("BerkeleyDatabase: Failed to open database environment.");
383
384 if (m_db == nullptr) {
385 int ret;
386 std::unique_ptr<Db> pdb_temp = std::make_unique<Db>(env->dbenv.get(), 0);
387 const std::string strFile = fs::PathToString(m_filename);
388
389 bool fMockDb = env->IsMock();
390 if (fMockDb) {
391 DbMpoolFile* mpf = pdb_temp->get_mpf();
392 ret = mpf->set_flags(DB_MPOOL_NOFILE, 1);
393 if (ret != 0) {
394 throw std::runtime_error(strprintf("BerkeleyDatabase: Failed to configure for no temp file backing for database %s", strFile));
395 }
396 }
397
398 if (m_byteswap) {
399 pdb_temp->set_lorder(REVERSE_BYTE_ORDER);
400 }
401
402 ret = pdb_temp->open(nullptr, // Txn pointer
403 fMockDb ? nullptr : strFile.c_str(), // Filename
404 fMockDb ? strFile.c_str() : "main", // Logical db name
405 DB_BTREE, // Database type
406 nFlags, // Flags
407 0);
408
409 if (ret != 0) {
410 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 CheckUniqueFileid(*env, strFile, *pdb_temp, this->env->m_fileids[strFile]);
417
418 m_db.reset(pdb_temp.release());
419
420 }
421 }
422}
423
425{
426 if (activeTxn)
427 return;
428
429 // Flush database activity from memory pool to disk log
430 unsigned int nMinutes = 0;
431 if (fReadOnly)
432 nMinutes = 1;
433
434 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 env->dbenv->txn_checkpoint(nMinutes ? m_database.m_max_log_mb * 1024 : 0, nMinutes, 0);
436 }
437}
438
440{
442}
443
445{
446 Close();
448}
449
451{
452 if (!pdb)
453 return;
454 if (activeTxn)
455 activeTxn->abort();
456 activeTxn = nullptr;
457 pdb = nullptr;
458
459 if (fFlushOnClose)
460 Flush();
461}
462
464{
465 {
466 LOCK(cs_db);
467 auto it = m_databases.find(filename);
468 assert(it != m_databases.end());
469 BerkeleyDatabase& database = it->second.get();
470 if (database.m_db) {
471 // Close the database handle
472 database.m_db->close(0);
473 database.m_db.reset();
474 }
475 }
476}
477
479{
480 // Make sure that no Db's are in use
481 AssertLockNotHeld(cs_db);
482 std::unique_lock<RecursiveMutex> lock(cs_db);
483 m_db_in_use.wait(lock, [this](){
484 for (auto& db : m_databases) {
485 if (db.second.get().m_refcount > 0) return false;
486 }
487 return true;
488 });
489
490 std::vector<fs::path> filenames;
491 filenames.reserve(m_databases.size());
492 for (const auto& it : m_databases) {
493 filenames.push_back(it.first);
494 }
495 // Close the individual Db's
496 for (const fs::path& filename : filenames) {
497 CloseDb(filename);
498 }
499 // Reset the environment
500 Flush(true); // This will flush and close the environment
501 Reset();
502 bilingual_str open_err;
503 Open(open_err);
504}
505
507{
508 DbTxn* ptxn = nullptr;
509 int ret = dbenv->txn_begin(nullptr, &ptxn, flags);
510 if (!ptxn || ret != 0)
511 return nullptr;
512 return ptxn;
513}
514
515bool BerkeleyDatabase::Rewrite(const char* pszSkip)
516{
517 while (true) {
518 {
519 LOCK(cs_db);
520 const std::string strFile = fs::PathToString(m_filename);
521 if (m_refcount <= 0) {
522 // Flush log data to the dat file
523 env->CloseDb(m_filename);
524 env->CheckpointLSN(strFile);
525 m_refcount = -1;
526
527 bool fSuccess = true;
528 LogPrintf("BerkeleyBatch::Rewrite: Rewriting %s...\n", strFile);
529 std::string strFileRes = strFile + ".rewrite";
530 { // surround usage of db with extra {}
531 BerkeleyBatch db(*this, true);
532 std::unique_ptr<Db> pdbCopy = std::make_unique<Db>(env->dbenv.get(), 0);
533
534 if (m_byteswap) {
535 pdbCopy->set_lorder(REVERSE_BYTE_ORDER);
536 }
537
538 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 if (ret > 0) {
545 LogPrintf("BerkeleyBatch::Rewrite: Can't create database file %s\n", strFileRes);
546 fSuccess = false;
547 }
548
549 std::unique_ptr<DatabaseCursor> cursor = db.GetNewCursor();
550 if (cursor) {
551 while (fSuccess) {
552 DataStream ssKey{};
553 DataStream ssValue{};
554 DatabaseCursor::Status ret1 = cursor->Next(ssKey, ssValue);
555 if (ret1 == DatabaseCursor::Status::DONE) {
556 break;
557 } else if (ret1 == DatabaseCursor::Status::FAIL) {
558 fSuccess = false;
559 break;
560 }
561 if (pszSkip &&
562 strncmp((const char*)ssKey.data(), pszSkip, std::min(ssKey.size(), strlen(pszSkip))) == 0)
563 continue;
564 if (strncmp((const char*)ssKey.data(), "\x07version", 8) == 0) {
565 // Update version:
566 ssValue.clear();
567 ssValue << CLIENT_VERSION;
568 }
569 Dbt datKey(ssKey.data(), ssKey.size());
570 Dbt datValue(ssValue.data(), ssValue.size());
571 int ret2 = pdbCopy->put(nullptr, &datKey, &datValue, DB_NOOVERWRITE);
572 if (ret2 > 0)
573 fSuccess = false;
574 }
575 cursor.reset();
576 }
577 if (fSuccess) {
578 db.Close();
579 env->CloseDb(m_filename);
580 if (pdbCopy->close(0))
581 fSuccess = false;
582 } else {
583 pdbCopy->close(0);
584 }
585 }
586 if (fSuccess) {
587 Db dbA(env->dbenv.get(), 0);
588 if (dbA.remove(strFile.c_str(), nullptr, 0))
589 fSuccess = false;
590 Db dbB(env->dbenv.get(), 0);
591 if (dbB.rename(strFileRes.c_str(), nullptr, strFile.c_str(), 0))
592 fSuccess = false;
593 }
594 if (!fSuccess)
595 LogPrintf("BerkeleyBatch::Rewrite: Failed to rewrite database file %s\n", strFileRes);
596 return fSuccess;
597 }
598 }
599 UninterruptibleSleep(std::chrono::milliseconds{100});
600 }
601}
602
603
604void BerkeleyEnvironment::Flush(bool fShutdown)
605{
606 const auto start{SteadyClock::now()};
607 // Flush log data to the actual data file on all files that are not in use
608 LogDebug(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: [%s] Flush(%s)%s\n", strPath, fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started");
609 if (!fDbEnvInit)
610 return;
611 {
612 LOCK(cs_db);
613 bool no_dbs_accessed = true;
614 for (auto& db_it : m_databases) {
615 const fs::path& filename = db_it.first;
616 int nRefCount = db_it.second.get().m_refcount;
617 if (nRefCount < 0) continue;
618 const std::string strFile = fs::PathToString(filename);
619 LogDebug(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: Flushing %s (refcount = %d)...\n", strFile, nRefCount);
620 if (nRefCount == 0) {
621 // Move log data to the dat file
622 CloseDb(filename);
623 LogDebug(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: %s checkpoint\n", strFile);
624 dbenv->txn_checkpoint(0, 0, 0);
625 LogDebug(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: %s detach\n", strFile);
626 if (!fMockDb)
627 dbenv->lsn_reset(strFile.c_str(), 0);
628 LogDebug(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: %s closed\n", strFile);
629 nRefCount = -1;
630 } else {
631 no_dbs_accessed = false;
632 }
633 }
634 LogDebug(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 if (fShutdown) {
636 char** listp;
637 if (no_dbs_accessed) {
638 dbenv->log_archive(&listp, DB_ARCH_REMOVE);
639 Close();
640 if (!fMockDb) {
641 fs::remove_all(fs::PathFromString(strPath) / "database");
642 }
643 }
644 }
645 }
646}
647
649{
650 // Don't flush if we can't acquire the lock.
651 TRY_LOCK(cs_db, lockDb);
652 if (!lockDb) return false;
653
654 // Don't flush if any databases are in use
655 for (auto& it : env->m_databases) {
656 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 if (m_refcount < 0) return false;
661
662 const std::string strFile = fs::PathToString(m_filename);
663 LogDebug(BCLog::WALLETDB, "Flushing %s\n", strFile);
664 const auto start{SteadyClock::now()};
665
666 // Flush wallet file so it's self contained
667 env->CloseDb(m_filename);
668 env->CheckpointLSN(strFile);
669 m_refcount = -1;
670
671 LogDebug(BCLog::WALLETDB, "Flushed %s %dms\n", strFile, Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
672
673 return true;
674}
675
676bool BerkeleyDatabase::Backup(const std::string& strDest) const
677{
678 const std::string strFile = fs::PathToString(m_filename);
679 while (true)
680 {
681 {
682 LOCK(cs_db);
683 if (m_refcount <= 0)
684 {
685 // Flush log data to the dat file
686 env->CloseDb(m_filename);
687 env->CheckpointLSN(strFile);
688
689 // Copy wallet file
690 fs::path pathSrc = env->Directory() / m_filename;
691 fs::path pathDest(fs::PathFromString(strDest));
692 if (fs::is_directory(pathDest))
693 pathDest /= m_filename;
694
695 try {
696 if (fs::exists(pathDest) && fs::equivalent(pathSrc, pathDest)) {
697 LogPrintf("cannot backup to wallet source file %s\n", fs::PathToString(pathDest));
698 return false;
699 }
700
701 fs::copy_file(pathSrc, pathDest, fs::copy_options::overwrite_existing);
702 LogPrintf("copied %s to %s\n", strFile, fs::PathToString(pathDest));
703 return true;
704 } catch (const fs::filesystem_error& e) {
705 LogPrintf("error copying %s to %s - %s\n", strFile, fs::PathToString(pathDest), fsbridge::get_filesystem_error_message(e));
706 return false;
707 }
708 }
709 }
710 UninterruptibleSleep(std::chrono::milliseconds{100});
711 }
712}
713
715{
716 env->Flush(false);
717}
718
720{
721 env->Flush(true);
722}
723
725{
726 env->ReloadDbEnv();
727}
728
730 : m_key_prefix(prefix.begin(), prefix.end())
731{
732 if (!database.m_db.get()) {
733 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 int ret = database.m_db->cursor(batch.txn(), &m_cursor, 0);
738 if (ret != 0) {
739 throw std::runtime_error(STR_INTERNAL_BUG(strprintf("BDB Cursor could not be created. Returned %d", ret)));
740 }
741}
742
744{
745 if (m_cursor == nullptr) return Status::FAIL;
746 // Read at cursor
747 SafeDbt datKey(m_key_prefix.data(), m_key_prefix.size());
748 SafeDbt datValue;
749 int ret = -1;
750 if (m_first && !m_key_prefix.empty()) {
751 ret = m_cursor->get(datKey, datValue, DB_SET_RANGE);
752 } else {
753 ret = m_cursor->get(datKey, datValue, DB_NEXT);
754 }
755 m_first = false;
756 if (ret == DB_NOTFOUND) {
757 return Status::DONE;
758 }
759 if (ret != 0) {
760 return Status::FAIL;
761 }
762
763 Span<const std::byte> raw_key = SpanFromDbt(datKey);
764 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 ssKey.clear();
770 ssKey.write(raw_key);
771 ssValue.clear();
772 ssValue.write(SpanFromDbt(datValue));
773 return Status::MORE;
774}
775
777{
778 if (!m_cursor) return;
779 m_cursor->close();
780 m_cursor = nullptr;
781}
782
783std::unique_ptr<DatabaseCursor> BerkeleyBatch::GetNewCursor()
784{
785 if (!pdb) return nullptr;
786 return std::make_unique<BerkeleyCursor>(m_database, *this);
787}
788
790{
791 if (!pdb) return nullptr;
792 return std::make_unique<BerkeleyCursor>(m_database, *this, prefix);
793}
794
796{
797 if (!pdb || activeTxn)
798 return false;
799 DbTxn* ptxn = env->TxnBegin(DB_TXN_WRITE_NOSYNC);
800 if (!ptxn)
801 return false;
802 activeTxn = ptxn;
803 return true;
804}
805
807{
808 if (!pdb || !activeTxn)
809 return false;
810 int ret = activeTxn->commit(0);
811 activeTxn = nullptr;
812 return (ret == 0);
813}
814
816{
817 if (!pdb || !activeTxn)
818 return false;
819 int ret = activeTxn->abort();
820 activeTxn = nullptr;
821 return (ret == 0);
822}
823
825{
826 int major, minor;
827 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 if (major != DB_VERSION_MAJOR || minor < DB_VERSION_MINOR) {
833 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 return false;
836 }
837
838 return true;
839}
840
842{
843 return DbEnv::version(nullptr, nullptr, nullptr);
844}
845
847{
848 if (!pdb)
849 return false;
850
851 SafeDbt datKey(key.data(), key.size());
852
853 SafeDbt datValue;
854 int ret = pdb->get(activeTxn, datKey, datValue, 0);
855 if (ret == 0 && datValue.get_data() != nullptr) {
856 value.clear();
857 value.write(SpanFromDbt(datValue));
858 return true;
859 }
860 return false;
861}
862
863bool BerkeleyBatch::WriteKey(DataStream&& key, DataStream&& value, bool overwrite)
864{
865 if (!pdb)
866 return false;
867 if (fReadOnly)
868 assert(!"Write called on database in read-only mode");
869
870 SafeDbt datKey(key.data(), key.size());
871
872 SafeDbt datValue(value.data(), value.size());
873
874 int ret = pdb->put(activeTxn, datKey, datValue, (overwrite ? 0 : DB_NOOVERWRITE));
875 return (ret == 0);
876}
877
879{
880 if (!pdb)
881 return false;
882 if (fReadOnly)
883 assert(!"Erase called on database in read-only mode");
884
885 SafeDbt datKey(key.data(), key.size());
886
887 int ret = pdb->del(activeTxn, datKey, 0);
888 return (ret == 0 || ret == DB_NOTFOUND);
889}
890
892{
893 if (!pdb)
894 return false;
895
896 SafeDbt datKey(key.data(), key.size());
897
898 int ret = pdb->exists(activeTxn, datKey, 0);
899 return ret == 0;
900}
901
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 if (!Assume(activeTxn)) return false;
909
910 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 Dbt prefix_key{const_cast<std::byte*>(prefix.data()), static_cast<uint32_t>(prefix.size())}, prefix_value{};
915 int ret{cursor->dbc()->get(&prefix_key, &prefix_value, DB_SET_RANGE)};
916 for (int flag{DB_CURRENT}; ret == 0; flag = DB_NEXT) {
917 SafeDbt key, value;
918 ret = cursor->dbc()->get(key, value, flag);
919 if (ret != 0 || key.get_size() < prefix.size() || memcmp(key.get_data(), prefix.data(), prefix.size()) != 0) break;
920 ret = cursor->dbc()->del(0);
921 }
922 cursor.reset();
923 return ret == 0 || ret == DB_NOTFOUND;
924}
925
927{
928 LOCK(cs_db);
929 if (m_refcount < 0) {
930 m_refcount = 1;
931 } else {
932 m_refcount++;
933 }
934}
935
937{
938 LOCK(cs_db);
939 m_refcount--;
940 if (env) env->m_db_in_use.notify_all();
941}
942
943std::unique_ptr<DatabaseBatch> BerkeleyDatabase::MakeBatch(bool flush_on_close)
944{
945 return std::make_unique<BerkeleyBatch>(*this, false, flush_on_close);
946}
947
948std::unique_ptr<BerkeleyDatabase> MakeBerkeleyDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error)
949{
950 fs::path data_file = BDBDataFile(path);
951 std::unique_ptr<BerkeleyDatabase> db;
952 {
953 LOCK(cs_db); // Lock env.m_databases until insert in BerkeleyDatabase constructor
954 fs::path data_filename = data_file.filename();
955 std::shared_ptr<BerkeleyEnvironment> env = GetBerkeleyEnv(data_file.parent_path(), options.use_shared_memory);
956 if (env->m_databases.count(data_filename)) {
957 error = Untranslated(strprintf("Refusing to load database. Data file '%s' is already loaded.", fs::PathToString(env->Directory() / data_filename)));
959 return nullptr;
960 }
961 db = std::make_unique<BerkeleyDatabase>(std::move(env), std::move(data_filename), options);
962 }
963
964 if (options.verify && !db->Verify(error)) {
966 return nullptr;
967 }
968
970 return db;
971}
972} // namespace wallet
#define BDB_DB_FILE_ID_LEN
Definition: bdb.h:33
int ret
int flags
Definition: bitcoin-tx.cpp:536
#define STR_INTERNAL_BUG(msg)
Definition: check.h:68
#define Assume(val)
Assume is the identity function.
Definition: check.h:97
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:147
void write(Span< const value_type > src)
Definition: streams.h:251
void clear()
Definition: streams.h:187
constexpr C * begin() const noexcept
Definition: span.h:175
constexpr C * end() const noexcept
Definition: span.h:176
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
Definition: fs.h:33
path filename() const
Definition: fs.h:72
RAII class that provides access to a Berkeley database.
Definition: bdb.h:179
bool fFlushOnClose
Definition: bdb.h:192
bool ReadKey(DataStream &&key, DataStream &value) override
Definition: bdb.cpp:846
void Close() override
Definition: bdb.cpp:450
bool HasKey(DataStream &&key) override
Definition: bdb.cpp:891
BerkeleyDatabase & m_database
Definition: bdb.h:194
bool EraseKey(DataStream &&key) override
Definition: bdb.cpp:878
~BerkeleyBatch() override
Definition: bdb.cpp:444
BerkeleyBatch(BerkeleyDatabase &database, const bool fReadOnly, bool fFlushOnCloseIn=true)
Definition: bdb.cpp:363
std::unique_ptr< DatabaseCursor > GetNewPrefixCursor(Span< const std::byte > prefix) override
Definition: bdb.cpp:789
void Flush() override
Definition: bdb.cpp:424
bool TxnCommit() override
Definition: bdb.cpp:806
BerkeleyEnvironment * env
Definition: bdb.h:193
bool TxnAbort() override
Definition: bdb.cpp:815
DbTxn * txn() const
Definition: bdb.h:212
std::unique_ptr< DatabaseCursor > GetNewCursor() override
Definition: bdb.cpp:783
bool ErasePrefix(Span< const std::byte > prefix) override
Definition: bdb.cpp:902
std::string strFile
Definition: bdb.h:189
bool WriteKey(DataStream &&key, DataStream &&value, bool overwrite=true) override
Definition: bdb.cpp:863
DbTxn * activeTxn
Definition: bdb.h:190
bool TxnBegin() override
Definition: bdb.cpp:795
~BerkeleyCursor() override
Definition: bdb.cpp:776
std::vector< std::byte > m_key_prefix
Definition: bdb.h:164
BerkeleyCursor(BerkeleyDatabase &database, const BerkeleyBatch &batch, Span< const std::byte > prefix={})
Definition: bdb.cpp:729
Status Next(DataStream &key, DataStream &value) override
Definition: bdb.cpp:743
An instance of this class represents one database.
Definition: bdb.h:89
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:515
void Open() override
Open the database if it is not already opened.
Definition: bdb.cpp:374
bool Backup(const std::string &strDest) const override
Back up the entire database to a file.
Definition: bdb.cpp:676
int64_t m_max_log_mb
Definition: bdb.h:154
fs::path m_filename
Definition: bdb.h:153
void RemoveRef() override
Indicate that database user has stopped using the database and that it could be flushed or closed.
Definition: bdb.cpp:936
std::shared_ptr< BerkeleyEnvironment > env
Pointer to shared database environment.
Definition: bdb.h:145
void IncrementUpdateCounter() override
Definition: bdb.cpp:439
void ReloadDbEnv() override
Definition: bdb.cpp:724
void AddRef() override
Indicate that a new database user has begun using the database.
Definition: bdb.cpp:926
std::unique_ptr< Db > m_db
Database pointer.
Definition: bdb.h:148
void Flush() override
Make sure all changes are flushed to database file.
Definition: bdb.cpp:714
bool PeriodicFlush() override
Definition: bdb.cpp:648
std::unique_ptr< DatabaseBatch > MakeBatch(bool flush_on_close=true) override
Make a BerkeleyBatch connected to this database.
Definition: bdb.cpp:943
bool Verify(bilingual_str &error)
Verifies the environment and database file.
Definition: bdb.cpp:315
~BerkeleyDatabase() override
Definition: bdb.cpp:351
void Close() override
Flush to the database file and close the database.
Definition: bdb.cpp:719
fs::path Directory() const
Definition: bdb.h:67
std::condition_variable_any m_db_in_use
Definition: bdb.h:57
void CloseDb(const fs::path &filename)
Definition: bdb.cpp:463
std::unique_ptr< DbEnv > dbenv
Definition: bdb.h:54
BerkeleyEnvironment()
Construct an in-memory mock Berkeley environment for testing.
Definition: bdb.cpp:210
void CheckpointLSN(const std::string &strFile)
Definition: bdb.cpp:343
void Flush(bool fShutdown)
Definition: bdb.cpp:604
DbTxn * TxnBegin(int flags)
Definition: bdb.cpp:506
std::string strPath
Definition: bdb.h:51
bool Open(bilingual_str &error)
Definition: bdb.cpp:146
std::map< fs::path, std::reference_wrapper< BerkeleyDatabase > > m_databases
Definition: bdb.h:55
RAII class that automatically cleanses its data on destruction.
Definition: bdb.cpp:242
const void * get_data() const
Definition: bdb.cpp:284
uint32_t get_size() const
Definition: bdb.cpp:289
An instance of this class represents one database.
Definition: db.h:131
std::atomic< unsigned int > nUpdateCounter
Definition: db.h:175
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:141
void memory_cleanse(void *ptr, size_t len)
Secure overwrite a buffer (possibly containing secret data) with zero-bytes.
Definition: cleanse.cpp:14
static const int CLIENT_VERSION
bitcoind-res.rc includes this file, but it cannot cope with real c++ code.
Definition: clientversion.h:31
bool TryCreateDirectories(const fs::path &p)
Ignores exceptions thrown by create_directories if the requested directory exists.
Definition: fs_helpers.cpp:261
void UnlockDirectory(const fs::path &directory, const fs::path &lockfile_name)
Definition: fs_helpers.cpp:81
std::string HexStr(const Span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
Definition: hex_base.cpp:29
#define LogDebug(category,...)
Definition: logging.h:280
#define LogPrintf(...)
Definition: logging.h:266
@ WALLETDB
Definition: logging.h:49
Filesystem operations and types.
static auto quoted(const std::string &s)
Definition: fs.h:95
static bool exists(const path &p)
Definition: fs.h:89
static bool copy_file(const path &from, const path &to, copy_options options)
Definition: fs.h:128
static std::string PathToString(const path &path)
Convert path object to a byte string.
Definition: fs.h:151
static path PathFromString(const std::string &string)
Convert byte string to path object.
Definition: fs.h:174
FILE * fopen(const fs::path &p, const char *mode)
Definition: fs.cpp:26
std::string get_filesystem_error_message(const fs::filesystem_error &e)
Definition: fs.cpp:118
LockResult LockDirectory(const fs::path &directory, const fs::path &lockfile_name, bool probe_only)
Definition: fs_helpers.cpp:53
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:948
static std::set< std::string > g_loading_wallet_set GUARDED_BY(g_loading_wallet_mutex)
DatabaseFormat
Definition: db.h:184
std::string BerkeleyDatabaseVersion()
Definition: bdb.cpp:841
static constexpr auto REVERSE_BYTE_ORDER
Definition: bdb.cpp:68
fs::path BDBDataFile(const fs::path &wallet_path)
Definition: db.cpp:76
std::shared_ptr< BerkeleyEnvironment > GetBerkeleyEnv(const fs::path &env_directory, bool use_shared_memory)
Get BerkeleyEnvironment given a directory path.
Definition: bdb.cpp:81
bool BerkeleyDatabaseSanityCheck()
Perform sanity check of runtime BDB version versus linked BDB version.
Definition: bdb.cpp:824
DatabaseStatus
Definition: db.h:205
static Span< const std::byte > SpanFromDbt(const SafeDbt &dbt)
Definition: bdb.cpp:299
const char * prefix
Definition: rest.cpp:1009
Bilingual messages:
Definition: translation.h:24
bool verify
Check data integrity on load.
Definition: db.h:199
bool use_shared_memory
Let other processes access the database.
Definition: db.h:201
bool operator==(const WalletDatabaseFileId &rhs) const
Definition: bdb.cpp:70
uint8_t value[BDB_DB_FILE_ID_LEN]
Definition: bdb.h:38
#define AssertLockNotHeld(cs)
Definition: sync.h:147
#define LOCK(cs)
Definition: sync.h:257
#define TRY_LOCK(cs, name)
Definition: sync.h:261
void UninterruptibleSleep(const std::chrono::microseconds &n)
Definition: time.cpp:20
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1172
consteval auto _(util::TranslatedLiteral str)
Definition: translation.h:79
bilingual_str Untranslated(std::string original)
Mark a bilingual_str as untranslated.
Definition: translation.h:82
assert(!tx.IsCoinBase())