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