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