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