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