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