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