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