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