Dash Core Source Documentation (0.16.0.1)

Find detailed information regarding the Dash Core source code.

db.cpp
Go to the documentation of this file.
1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2015 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 <addrman.h>
9 #include <hash.h>
10 #include <protocol.h>
11 #include <util.h>
12 #include <utilstrencodings.h>
13 #include <wallet/walletutil.h>
14 
15 #include <stdint.h>
16 
17 #ifndef WIN32
18 #include <sys/stat.h>
19 #endif
20 
21 #include <boost/thread.hpp>
22 
23 namespace {
33 void CheckUniqueFileid(const BerkeleyEnvironment& env, const std::string& filename, Db& db)
34 {
35  if (env.IsMock()) return;
36 
37  u_int8_t fileid[DB_FILE_ID_LEN];
38  int ret = db.get_mpf()->get_fileid(fileid);
39  if (ret != 0) {
40  throw std::runtime_error(strprintf("BerkeleyBatch: Can't open database %s (get_fileid failed with %d)", filename, ret));
41  }
42 
43  for (const auto& item : env.mapDb) {
44  u_int8_t item_fileid[DB_FILE_ID_LEN];
45  if (item.second && item.second->get_mpf()->get_fileid(item_fileid) == 0 &&
46  memcmp(fileid, item_fileid, sizeof(fileid)) == 0) {
47  const char* item_filename = nullptr;
48  item.second->get_dbname(&item_filename, nullptr);
49  throw std::runtime_error(strprintf("BerkeleyBatch: Can't open database %s (duplicates fileid %s from %s)", filename,
50  HexStr(std::begin(item_fileid), std::end(item_fileid)),
51  item_filename ? item_filename : "(unknown database)"));
52  }
53  }
54 }
55 
56 CCriticalSection cs_db;
57 std::map<std::string, BerkeleyEnvironment> g_dbenvs;
58 } // namespace
59 
60 BerkeleyEnvironment* GetWalletEnv(const fs::path& wallet_path, std::string& database_filename)
61 {
62  fs::path env_directory;
63  if (fs::is_regular_file(wallet_path)) {
64  // Special case for backwards compatibility: if wallet path points to an
65  // existing file, treat it as the path to a BDB data file in a parent
66  // directory that also contains BDB log files.
67  env_directory = wallet_path.parent_path();
68  database_filename = wallet_path.filename().string();
69  } else {
70  // Normal case: Interpret wallet path as a directory path containing
71  // data and log files.
72  env_directory = wallet_path;
73  database_filename = "wallet.dat";
74  }
75  LOCK(cs_db);
76  // Note: An ununsed temporary BerkeleyEnvironment object may be created inside the
77  // emplace function if the key already exists. This is a little inefficient,
78  // but not a big concern since the map will be changed in the future to hold
79  // pointers instead of objects, anyway.
80  return &g_dbenvs.emplace(std::piecewise_construct, std::forward_as_tuple(env_directory.string()), std::forward_as_tuple(env_directory)).first->second;
81 }
82 
83 //
84 // BerkeleyBatch
85 //
86 
88 {
89  if (!fDbEnvInit)
90  return;
91 
92  fDbEnvInit = false;
93 
94  for (auto& db : mapDb) {
95  auto count = mapFileUseCount.find(db.first);
96  assert(count == mapFileUseCount.end() || count->second == 0);
97  if (db.second) {
98  db.second->close(0);
99  delete db.second;
100  db.second = nullptr;
101  }
102  }
103 
104  int ret = dbenv->close(0);
105  if (ret != 0)
106  LogPrintf("BerkeleyEnvironment::EnvShutdown: Error %d shutting down database environment: %s\n", ret, DbEnv::strerror(ret));
107  if (!fMockDb)
108  DbEnv((u_int32_t)0).remove(strPath.c_str(), 0);
109 }
110 
112 {
113  dbenv.reset(new DbEnv(DB_CXX_NO_EXCEPTIONS));
114  fDbEnvInit = false;
115  fMockDb = false;
116 }
117 
118 BerkeleyEnvironment::BerkeleyEnvironment(const fs::path& dir_path) : strPath(dir_path.string())
119 {
120  Reset();
121 }
122 
124 {
125  Close();
126 }
127 
129 {
130  if (fDbEnvInit)
131  return true;
132 
133  boost::this_thread::interruption_point();
134 
135  fs::path pathIn = strPath;
136  TryCreateDirectories(pathIn);
137  if (!LockDirectory(pathIn, ".walletlock")) {
138  LogPrintf("Cannot obtain a lock on wallet directory %s. Another instance of bitcoin may be using it.\n", strPath);
139  return false;
140  }
141 
142  fs::path pathLogDir = pathIn / "database";
143  TryCreateDirectories(pathLogDir);
144  fs::path pathErrorFile = pathIn / "db.log";
145  LogPrintf("BerkeleyEnvironment::Open: LogDir=%s ErrorFile=%s\n", pathLogDir.string(), pathErrorFile.string());
146 
147  unsigned int nEnvFlags = 0;
148  if (gArgs.GetBoolArg("-privdb", DEFAULT_WALLET_PRIVDB))
149  nEnvFlags |= DB_PRIVATE;
150 
151  dbenv->set_lg_dir(pathLogDir.string().c_str());
152  dbenv->set_cachesize(0, 0x100000, 1); // 1 MiB should be enough for just the wallet
153  dbenv->set_lg_bsize(0x10000);
154  dbenv->set_lg_max(1048576);
155  dbenv->set_lk_max_locks(40000);
156  dbenv->set_lk_max_objects(40000);
157  dbenv->set_errfile(fsbridge::fopen(pathErrorFile, "a"));
158  dbenv->set_flags(DB_AUTO_COMMIT, 1);
159  dbenv->set_flags(DB_TXN_WRITE_NOSYNC, 1);
160  dbenv->log_set_config(DB_LOG_AUTO_REMOVE, 1);
161  int ret = dbenv->open(strPath.c_str(),
162  DB_CREATE |
163  DB_INIT_LOCK |
164  DB_INIT_LOG |
165  DB_INIT_MPOOL |
166  DB_INIT_TXN |
167  DB_THREAD |
168  DB_RECOVER |
169  nEnvFlags,
170  S_IRUSR | S_IWUSR);
171  if (ret != 0) {
172  dbenv->close(0);
173  LogPrintf("BerkeleyEnvironment::Open: Error %d opening database environment: %s\n", ret, DbEnv::strerror(ret));
174  if (retry) {
175  // try moving the database env out of the way
176  fs::path pathDatabaseBak = pathIn / strprintf("database.%d.bak", GetTime());
177  try {
178  fs::rename(pathLogDir, pathDatabaseBak);
179  LogPrintf("Moved old %s to %s. Retrying.\n", pathLogDir.string(), pathDatabaseBak.string());
180  } catch (const fs::filesystem_error&) {
181  // failure is ok (well, not really, but it's not worse than what we started with)
182  }
183  // try opening it again one more time
184  if (!Open(false /* retry */)) {
185  // if it still fails, it probably means we can't even create the database env
186  return false;
187  }
188  } else {
189  return false;
190  }
191  }
192 
193  fDbEnvInit = true;
194  fMockDb = false;
195  return true;
196 }
197 
199 {
200  if (fDbEnvInit)
201  throw std::runtime_error("BerkeleyEnvironment::MakeMock: Already initialized");
202 
203  boost::this_thread::interruption_point();
204 
205  LogPrint(BCLog::DB, "BerkeleyEnvironment::MakeMock\n");
206 
207  dbenv->set_cachesize(1, 0, 1);
208  dbenv->set_lg_bsize(10485760 * 4);
209  dbenv->set_lg_max(10485760);
210  dbenv->set_lk_max_locks(10000);
211  dbenv->set_lk_max_objects(10000);
212  dbenv->set_flags(DB_AUTO_COMMIT, 1);
213  dbenv->log_set_config(DB_LOG_IN_MEMORY, 1);
214  int ret = dbenv->open(nullptr,
215  DB_CREATE |
216  DB_INIT_LOCK |
217  DB_INIT_LOG |
218  DB_INIT_MPOOL |
219  DB_INIT_TXN |
220  DB_THREAD |
221  DB_PRIVATE,
222  S_IRUSR | S_IWUSR);
223  if (ret > 0)
224  throw std::runtime_error(strprintf("BerkeleyEnvironment::MakeMock: Error %d opening database environment.", ret));
225 
226  fDbEnvInit = true;
227  fMockDb = true;
228 }
229 
230 BerkeleyEnvironment::VerifyResult BerkeleyEnvironment::Verify(const std::string& strFile, recoverFunc_type recoverFunc, std::string& out_backup_filename)
231 {
232  LOCK(cs_db);
233  assert(mapFileUseCount.count(strFile) == 0);
234 
235  Db db(dbenv.get(), 0);
236  int result = db.verify(strFile.c_str(), nullptr, nullptr, 0);
237  if (result == 0)
238  return VERIFY_OK;
239  else if (recoverFunc == nullptr)
240  return RECOVER_FAIL;
241 
242  // Try to recover:
243  bool fRecovered = (*recoverFunc)(fs::path(strPath) / strFile, out_backup_filename);
244  return (fRecovered ? RECOVER_OK : RECOVER_FAIL);
245 }
246 
247 bool BerkeleyBatch::Recover(const fs::path& file_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& newFilename)
248 {
249  std::string filename;
250  BerkeleyEnvironment* env = GetWalletEnv(file_path, filename);
251 
252  // Recovery procedure:
253  // move wallet file to walletfilename.timestamp.bak
254  // Call Salvage with fAggressive=true to
255  // get as much data as possible.
256  // Rewrite salvaged data to fresh wallet file
257  // Set -rescan so any missing transactions will be
258  // found.
259  int64_t now = GetTime();
260  newFilename = strprintf("%s.%d.bak", filename, now);
261 
262  int result = env->dbenv->dbrename(nullptr, filename.c_str(), nullptr,
263  newFilename.c_str(), DB_AUTO_COMMIT);
264  if (result == 0)
265  LogPrintf("Renamed %s to %s\n", filename, newFilename);
266  else
267  {
268  LogPrintf("Failed to rename %s to %s\n", filename, newFilename);
269  return false;
270  }
271 
272  std::vector<BerkeleyEnvironment::KeyValPair> salvagedData;
273  bool fSuccess = env->Salvage(newFilename, true, salvagedData);
274  if (salvagedData.empty())
275  {
276  LogPrintf("Salvage(aggressive) found no records in %s.\n", newFilename);
277  return false;
278  }
279  LogPrintf("Salvage(aggressive) found %u records\n", salvagedData.size());
280 
281  std::unique_ptr<Db> pdbCopy = MakeUnique<Db>(env->dbenv.get(), 0);
282  int ret = pdbCopy->open(nullptr, // Txn pointer
283  filename.c_str(), // Filename
284  "main", // Logical db name
285  DB_BTREE, // Database type
286  DB_CREATE, // Flags
287  0);
288  if (ret > 0) {
289  LogPrintf("Cannot create database file %s\n", filename);
290  pdbCopy->close(0);
291  return false;
292  }
293 
294  DbTxn* ptxn = env->TxnBegin();
295  for (BerkeleyEnvironment::KeyValPair& row : salvagedData)
296  {
297  if (recoverKVcallback)
298  {
299  CDataStream ssKey(row.first, SER_DISK, CLIENT_VERSION);
300  CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION);
301  if (!(*recoverKVcallback)(callbackDataIn, ssKey, ssValue))
302  continue;
303  }
304  Dbt datKey(&row.first[0], row.first.size());
305  Dbt datValue(&row.second[0], row.second.size());
306  int ret2 = pdbCopy->put(ptxn, &datKey, &datValue, DB_NOOVERWRITE);
307  if (ret2 > 0)
308  fSuccess = false;
309  }
310  ptxn->commit(0);
311  pdbCopy->close(0);
312 
313  return fSuccess;
314 }
315 
316 bool BerkeleyBatch::VerifyEnvironment(const fs::path& file_path, std::string& errorStr)
317 {
318  std::string walletFile;
319  BerkeleyEnvironment* env = GetWalletEnv(file_path, walletFile);
320  fs::path walletDir = env->Directory();
321 
322  LogPrintf("Using BerkeleyDB version %s\n", DbEnv::version(0, 0, 0));
323  LogPrintf("Using wallet %s\n", walletFile);
324 
325  // Wallet file must be a plain filename without a directory
326  if (walletFile != fs::basename(walletFile) + fs::extension(walletFile))
327  {
328  errorStr = strprintf(_("Wallet %s resides outside wallet directory %s"), walletFile, walletDir.string());
329  return false;
330  }
331 
332  if (!env->Open(true /* retry */)) {
333  errorStr = strprintf(_("Error initializing wallet database environment %s!"), walletDir);
334  return false;
335  }
336 
337  return true;
338 }
339 
340 bool BerkeleyBatch::VerifyDatabaseFile(const fs::path& file_path, std::string& warningStr, std::string& errorStr, BerkeleyEnvironment::recoverFunc_type recoverFunc)
341 {
342  std::string walletFile;
343  BerkeleyEnvironment* env = GetWalletEnv(file_path, walletFile);
344  fs::path walletDir = env->Directory();
345 
346  if (fs::exists(walletDir / walletFile))
347  {
348  std::string backup_filename;
349  BerkeleyEnvironment::VerifyResult r = env->Verify(walletFile, recoverFunc, backup_filename);
351  {
352  warningStr = strprintf(_("Warning: Wallet file corrupt, data salvaged!"
353  " Original %s saved as %s in %s; if"
354  " your balance or transactions are incorrect you should"
355  " restore from a backup."),
356  walletFile, backup_filename, walletDir);
357  }
359  {
360  errorStr = strprintf(_("%s corrupt, salvage failed"), walletFile);
361  return false;
362  }
363  }
364  // also return true if files does not exists
365  return true;
366 }
367 
368 /* End of headers, beginning of key/value data */
369 static const char *HEADER_END = "HEADER=END";
370 /* End of key/value data */
371 static const char *DATA_END = "DATA=END";
372 
373 bool BerkeleyEnvironment::Salvage(const std::string& strFile, bool fAggressive, std::vector<BerkeleyEnvironment::KeyValPair>& vResult)
374 {
375  LOCK(cs_db);
376  assert(mapFileUseCount.count(strFile) == 0);
377 
378  u_int32_t flags = DB_SALVAGE;
379  if (fAggressive)
380  flags |= DB_AGGRESSIVE;
381 
382  std::stringstream strDump;
383 
384  Db db(dbenv.get(), 0);
385  int result = db.verify(strFile.c_str(), nullptr, &strDump, flags);
386  if (result == DB_VERIFY_BAD) {
387  LogPrintf("BerkeleyEnvironment::Salvage: Database salvage found errors, all data may not be recoverable.\n");
388  if (!fAggressive) {
389  LogPrintf("BerkeleyEnvironment::Salvage: Rerun with aggressive mode to ignore errors and continue.\n");
390  return false;
391  }
392  }
393  if (result != 0 && result != DB_VERIFY_BAD) {
394  LogPrintf("BerkeleyEnvironment::Salvage: Database salvage failed with result %d.\n", result);
395  return false;
396  }
397 
398  // Format of bdb dump is ascii lines:
399  // header lines...
400  // HEADER=END
401  // hexadecimal key
402  // hexadecimal value
403  // ... repeated
404  // DATA=END
405 
406  std::string strLine;
407  while (!strDump.eof() && strLine != HEADER_END)
408  getline(strDump, strLine); // Skip past header
409 
410  std::string keyHex, valueHex;
411  while (!strDump.eof() && keyHex != DATA_END) {
412  getline(strDump, keyHex);
413  if (keyHex != DATA_END) {
414  if (strDump.eof())
415  break;
416  getline(strDump, valueHex);
417  if (valueHex == DATA_END) {
418  LogPrintf("BerkeleyEnvironment::Salvage: WARNING: Number of keys in data does not match number of values.\n");
419  break;
420  }
421  vResult.push_back(make_pair(ParseHex(keyHex), ParseHex(valueHex)));
422  }
423  }
424 
425  if (keyHex != DATA_END) {
426  LogPrintf("BerkeleyEnvironment::Salvage: WARNING: Unexpected end of file while reading salvage output.\n");
427  return false;
428  }
429 
430  return (result == 0);
431 }
432 
433 
434 void BerkeleyEnvironment::CheckpointLSN(const std::string& strFile)
435 {
436  dbenv->txn_checkpoint(0, 0, 0);
437  if (fMockDb)
438  return;
439  dbenv->lsn_reset(strFile.c_str(), 0);
440 }
441 
442 
443 BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode, bool fFlushOnCloseIn) : pdb(nullptr), activeTxn(nullptr)
444 {
445  fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w'));
446  fFlushOnClose = fFlushOnCloseIn;
447  env = database.env;
448  if (database.IsDummy()) {
449  return;
450  }
451  const std::string &strFilename = database.strFile;
452 
453  bool fCreate = strchr(pszMode, 'c') != nullptr;
454  unsigned int nFlags = DB_THREAD;
455  if (fCreate)
456  nFlags |= DB_CREATE;
457 
458  {
459  LOCK(cs_db);
460  if (!env->Open(false /* retry */))
461  throw std::runtime_error("BerkeleyBatch: Failed to open database environment.");
462 
463  pdb = env->mapDb[strFilename];
464  if (pdb == nullptr) {
465  int ret;
466  std::unique_ptr<Db> pdb_temp = MakeUnique<Db>(env->dbenv.get(), 0);
467 
468  bool fMockDb = env->IsMock();
469  if (fMockDb) {
470  DbMpoolFile* mpf = pdb_temp->get_mpf();
471  ret = mpf->set_flags(DB_MPOOL_NOFILE, 1);
472  if (ret != 0) {
473  throw std::runtime_error(strprintf("BerkeleyBatch: Failed to configure for no temp file backing for database %s", strFilename));
474  }
475  }
476 
477  ret = pdb_temp->open(nullptr, // Txn pointer
478  fMockDb ? nullptr : strFilename.c_str(), // Filename
479  fMockDb ? strFilename.c_str() : "main", // Logical db name
480  DB_BTREE, // Database type
481  nFlags, // Flags
482  0);
483 
484  if (ret != 0) {
485  throw std::runtime_error(strprintf("BerkeleyBatch: Error %d, can't open database %s", ret, strFilename));
486  }
487 
488  // Call CheckUniqueFileid on the containing BDB environment to
489  // avoid BDB data consistency bugs that happen when different data
490  // files in the same environment have the same fileid.
491  //
492  // Also call CheckUniqueFileid on all the other g_dbenvs to prevent
493  // bitcoin from opening the same data file through another
494  // environment when the file is referenced through equivalent but
495  // not obviously identical symlinked or hard linked or bind mounted
496  // paths. In the future a more relaxed check for equal inode and
497  // device ids could be done instead, which would allow opening
498  // different backup copies of a wallet at the same time. Maybe even
499  // more ideally, an exclusive lock for accessing the database could
500  // be implemented, so no equality checks are needed at all. (Newer
501  // versions of BDB have an set_lk_exclusive method for this
502  // purpose, but the older version we use does not.)
503  for (auto& env : g_dbenvs) {
504  CheckUniqueFileid(env.second, strFilename, *pdb_temp);
505  }
506 
507  pdb = pdb_temp.release();
508  env->mapDb[strFilename] = pdb;
509 
510  if (fCreate && !Exists(std::string("version"))) {
511  bool fTmp = fReadOnly;
512  fReadOnly = false;
514  fReadOnly = fTmp;
515  }
516  }
517  ++env->mapFileUseCount[strFilename];
518  strFile = strFilename;
519  }
520 }
521 
523 {
524  if (activeTxn)
525  return;
526 
527  // Flush database activity from memory pool to disk log
528  unsigned int nMinutes = 0;
529  if (fReadOnly)
530  nMinutes = 1;
531 
532  env->dbenv->txn_checkpoint(nMinutes ? gArgs.GetArg("-dblogsize", DEFAULT_WALLET_DBLOGSIZE) * 1024 : 0, nMinutes, 0);
533 }
534 
536 {
537  ++nUpdateCounter;
538 }
539 
541 {
542  if (!pdb)
543  return;
544  if (activeTxn)
545  activeTxn->abort();
546  activeTxn = nullptr;
547  pdb = nullptr;
548 
549  if (fFlushOnClose)
550  Flush();
551 
552  {
553  LOCK(cs_db);
555  }
556 }
557 
558 void BerkeleyEnvironment::CloseDb(const std::string& strFile)
559 {
560  {
561  LOCK(cs_db);
562  if (mapDb[strFile] != nullptr) {
563  // Close the database handle
564  Db* pdb = mapDb[strFile];
565  pdb->close(0);
566  delete pdb;
567  mapDb[strFile] = nullptr;
568  }
569  }
570 }
571 
572 bool BerkeleyBatch::Rewrite(BerkeleyDatabase& database, const char* pszSkip)
573 {
574  if (database.IsDummy()) {
575  return true;
576  }
577  BerkeleyEnvironment *env = database.env;
578  const std::string& strFile = database.strFile;
579  while (true) {
580  {
581  LOCK(cs_db);
582  if (!env->mapFileUseCount.count(strFile) || env->mapFileUseCount[strFile] == 0) {
583  // Flush log data to the dat file
584  env->CloseDb(strFile);
586  env->mapFileUseCount.erase(strFile);
587 
588  bool fSuccess = true;
589  LogPrintf("BerkeleyBatch::Rewrite: Rewriting %s...\n", strFile);
590  std::string strFileRes = strFile + ".rewrite";
591  { // surround usage of db with extra {}
592  BerkeleyBatch db(database, "r");
593  std::unique_ptr<Db> pdbCopy = MakeUnique<Db>(env->dbenv.get(), 0);
594 
595  int ret = pdbCopy->open(nullptr, // Txn pointer
596  strFileRes.c_str(), // Filename
597  "main", // Logical db name
598  DB_BTREE, // Database type
599  DB_CREATE, // Flags
600  0);
601  if (ret > 0) {
602  LogPrintf("BerkeleyBatch::Rewrite: Can't create database file %s\n", strFileRes);
603  fSuccess = false;
604  }
605 
606  Dbc* pcursor = db.GetCursor();
607  if (pcursor)
608  while (fSuccess) {
611  int ret1 = db.ReadAtCursor(pcursor, ssKey, ssValue);
612  if (ret1 == DB_NOTFOUND) {
613  pcursor->close();
614  break;
615  } else if (ret1 != 0) {
616  pcursor->close();
617  fSuccess = false;
618  break;
619  }
620  if (pszSkip &&
621  strncmp(ssKey.data(), pszSkip, std::min(ssKey.size(), strlen(pszSkip))) == 0)
622  continue;
623  if (strncmp(ssKey.data(), "\x07version", 8) == 0) {
624  // Update version:
625  ssValue.clear();
626  ssValue << CLIENT_VERSION;
627  }
628  Dbt datKey(ssKey.data(), ssKey.size());
629  Dbt datValue(ssValue.data(), ssValue.size());
630  int ret2 = pdbCopy->put(nullptr, &datKey, &datValue, DB_NOOVERWRITE);
631  if (ret2 > 0)
632  fSuccess = false;
633  }
634  if (fSuccess) {
635  db.Close();
636  env->CloseDb(strFile);
637  if (pdbCopy->close(0))
638  fSuccess = false;
639  } else {
640  pdbCopy->close(0);
641  }
642  }
643  if (fSuccess) {
644  Db dbA(env->dbenv.get(), 0);
645  if (dbA.remove(strFile.c_str(), nullptr, 0))
646  fSuccess = false;
647  Db dbB(env->dbenv.get(), 0);
648  if (dbB.rename(strFileRes.c_str(), nullptr, strFile.c_str(), 0))
649  fSuccess = false;
650  }
651  if (!fSuccess)
652  LogPrintf("BerkeleyBatch::Rewrite: Failed to rewrite database file %s\n", strFileRes);
653  return fSuccess;
654  }
655  }
656  MilliSleep(100);
657  }
658 }
659 
660 
661 void BerkeleyEnvironment::Flush(bool fShutdown)
662 {
663  int64_t nStart = GetTimeMillis();
664  // Flush log data to the actual data file on all files that are not in use
665  LogPrint(BCLog::DB, "BerkeleyEnvironment::Flush: Flush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started");
666  if (!fDbEnvInit)
667  return;
668  {
669  LOCK(cs_db);
670  std::map<std::string, int>::iterator mi = mapFileUseCount.begin();
671  while (mi != mapFileUseCount.end()) {
672  std::string strFile = (*mi).first;
673  int nRefCount = (*mi).second;
674  LogPrint(BCLog::DB, "BerkeleyEnvironment::Flush: Flushing %s (refcount = %d)...\n", strFile, nRefCount);
675  if (nRefCount == 0) {
676  // Move log data to the dat file
677  CloseDb(strFile);
678  LogPrint(BCLog::DB, "BerkeleyEnvironment::Flush: %s checkpoint\n", strFile);
679  dbenv->txn_checkpoint(0, 0, 0);
680  LogPrint(BCLog::DB, "BerkeleyEnvironment::Flush: %s detach\n", strFile);
681  if (!fMockDb)
682  dbenv->lsn_reset(strFile.c_str(), 0);
683  LogPrint(BCLog::DB, "BerkeleyEnvironment::Flush: %s closed\n", strFile);
684  mapFileUseCount.erase(mi++);
685  } else
686  mi++;
687  }
688  LogPrint(BCLog::DB, "BerkeleyEnvironment::Flush: Flush(%s)%s took %15dms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started", GetTimeMillis() - nStart);
689  if (fShutdown) {
690  char** listp;
691  if (mapFileUseCount.empty()) {
692  dbenv->log_archive(&listp, DB_ARCH_REMOVE);
693  Close();
694  if (!fMockDb)
695  fs::remove_all(fs::path(strPath) / "database");
696  }
697  }
698  }
699 }
700 
702 {
703  if (database.IsDummy()) {
704  return true;
705  }
706  bool ret = false;
707  BerkeleyEnvironment *env = database.env;
708  const std::string& strFile = database.strFile;
709  TRY_LOCK(cs_db, lockDb);
710  if (lockDb)
711  {
712  // Don't do this if any databases are in use
713  int nRefCount = 0;
714  std::map<std::string, int>::iterator mit = env->mapFileUseCount.begin();
715  while (mit != env->mapFileUseCount.end())
716  {
717  nRefCount += (*mit).second;
718  mit++;
719  }
720 
721  if (nRefCount == 0)
722  {
723  boost::this_thread::interruption_point();
724  std::map<std::string, int>::iterator mi = env->mapFileUseCount.find(strFile);
725  if (mi != env->mapFileUseCount.end())
726  {
727  LogPrint(BCLog::DB, "Flushing %s\n", strFile);
728  int64_t nStart = GetTimeMillis();
729 
730  // Flush wallet file so it's self contained
731  env->CloseDb(strFile);
733 
734  env->mapFileUseCount.erase(mi++);
735  LogPrint(BCLog::DB, "Flushed %s %dms\n", strFile, GetTimeMillis() - nStart);
736  ret = true;
737  }
738  }
739  }
740 
741  return ret;
742 }
743 
744 bool BerkeleyDatabase::Rewrite(const char* pszSkip)
745 {
746  return BerkeleyBatch::Rewrite(*this, pszSkip);
747 }
748 
749 bool BerkeleyDatabase::Backup(const std::string& strDest)
750 {
751  if (IsDummy()) {
752  return false;
753  }
754  while (true)
755  {
756  {
757  LOCK(cs_db);
758  if (!env->mapFileUseCount.count(strFile) || env->mapFileUseCount[strFile] == 0)
759  {
760  // Flush log data to the dat file
761  env->CloseDb(strFile);
763  env->mapFileUseCount.erase(strFile);
764 
765  // Copy wallet file
766  fs::path pathSrc = env->Directory() / strFile; // see bitcoin #13667
767  fs::path pathDest(strDest);
768  if (fs::is_directory(pathDest))
769  pathDest /= strFile;
770 
771  try {
772  if (fs::equivalent(pathSrc, pathDest)) {
773  LogPrintf("cannot backup to wallet source file %s\n", pathDest.string());
774  return false;
775  }
776 
777  fs::copy_file(pathSrc, pathDest, fs::copy_option::overwrite_if_exists);
778  LogPrintf("copied %s to %s\n", strFile, pathDest.string());
779  return true;
780  } catch (const fs::filesystem_error& e) {
781  LogPrintf("error copying %s to %s - %s\n", strFile, pathDest.string(), e.what());
782  return false;
783  }
784  }
785  }
786  MilliSleep(100);
787  }
788 }
789 
790 void BerkeleyDatabase::Flush(bool shutdown)
791 {
792  if (!IsDummy()) {
793  env->Flush(shutdown);
794  }
795 }
void Flush(bool shutdown)
Make sure all changes are flushed to disk.
Definition: db.cpp:790
bool fReadOnly
Definition: db.h:173
bool fDbEnvInit
Definition: db.h:31
FILE * fopen(const fs::path &p, const char *mode)
Definition: fs.cpp:5
void MilliSleep(int64_t n)
Definition: utiltime.cpp:75
void Close()
Definition: db.cpp:87
#define TRY_LOCK(cs, name)
Definition: sync.h:180
VerifyResult
Verify that database file strFile is OK.
Definition: db.h:57
void Close()
Definition: db.cpp:540
#define strprintf
Definition: tinyformat.h:1066
bool fFlushOnClose
Definition: db.h:174
std::string HexStr(const T itbegin, const T itend, bool fSpaces=false)
bool Exists(const K &key)
Definition: db.h:284
value_type * data()
Definition: streams.h:203
An instance of this class represents one database.
Definition: db.h:95
int flags
Definition: dash-tx.cpp:462
Dbc * GetCursor()
Definition: db.h:303
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:103
bool Backup(const std::string &strDest)
Back up the entire database to a file.
Definition: db.cpp:749
bool GetBoolArg(const std::string &strArg, bool fDefault) const
Return boolean argument or default value.
Definition: util.cpp:824
fs::path Directory() const
Definition: db.h:49
bool Open(bool retry)
Definition: db.cpp:128
static bool PeriodicFlush(BerkeleyDatabase &database)
Definition: db.cpp:701
std::map< std::string, Db * > mapDb
Definition: db.h:40
bool LockDirectory(const fs::path &directory, const std::string lockfile_name, bool probe_only)
Definition: util.cpp:477
DbTxn * TxnBegin(int flags=DB_TXN_WRITE_NOSYNC)
Definition: db.h:79
std::map< std::string, int > mapFileUseCount
Definition: db.h:39
std::unique_ptr< DbEnv > dbenv
Definition: db.h:38
void CheckpointLSN(const std::string &strFile)
Definition: db.cpp:434
static const char * DATA_END
Definition: db.cpp:371
int64_t GetTime()
Return system time (or mocked time, if set)
Definition: utiltime.cpp:22
std::pair< std::vector< unsigned char >, std::vector< unsigned char > > KeyValPair
Salvage data from a file that Verify says is bad.
Definition: db.h:69
#define LogPrintf(...)
Definition: util.h:203
size_type size() const
Definition: streams.h:194
std::string strFile
Definition: db.h:171
bool Rewrite(const char *pszSkip=nullptr)
Rewrite the entire database on disk, with the exception of key pszSkip if non-zero.
Definition: db.cpp:744
#define LOCK(cs)
Definition: sync.h:178
BerkeleyEnvironment * GetWalletEnv(const fs::path &wallet_path, std::string &database_filename)
Get BerkeleyEnvironment and database filename given a wallet path.
Definition: db.cpp:60
bool TryCreateDirectories(const fs::path &p)
Ignores exceptions thrown by Boost&#39;s create_directories if the requested directory exists...
Definition: util.cpp:1085
static const char * HEADER_END
Definition: db.cpp:369
bool WriteVersion(int nVersion)
Definition: db.h:385
DbTxn * activeTxn
Definition: db.h:172
void MakeMock()
Definition: db.cpp:198
RAII class that provides access to a Berkeley database.
Definition: db.h:167
void Flush()
Definition: db.cpp:522
std::string strFile
Definition: db.h:156
static const unsigned int DEFAULT_WALLET_DBLOGSIZE
Definition: db.h:25
static bool VerifyEnvironment(const fs::path &file_path, std::string &errorStr)
Definition: db.cpp:316
BerkeleyEnvironment(const fs::path &env_directory)
Definition: db.cpp:118
static bool Recover(const fs::path &file_path, void *callbackDataIn, bool(*recoverKVcallback)(void *callbackData, CDataStream ssKey, CDataStream ssValue), std::string &out_backup_filename)
Definition: db.cpp:247
bool Salvage(const std::string &strFile, bool fAggressive, std::vector< KeyValPair > &vResult)
Definition: db.cpp:373
#define LogPrint(category,...)
Definition: util.h:214
static bool Rewrite(BerkeleyDatabase &database, const char *pszSkip=nullptr)
Definition: db.cpp:572
ArgsManager gArgs
Definition: util.cpp:108
void IncrementUpdateCounter()
Definition: db.cpp:535
std::string strPath
Definition: db.h:35
BerkeleyBatch(BerkeleyDatabase &database, const char *pszMode="r+", bool fFlushOnCloseIn=true)
Definition: db.cpp:443
int64_t GetTimeMillis()
Returns the system time (not mockable)
Definition: utiltime.cpp:56
int ReadAtCursor(Dbc *pcursor, CDataStream &ssKey, CDataStream &ssValue, bool setRange=false)
Definition: db.h:314
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
Definition: util.cpp:808
BerkeleyEnvironment * env
Definition: db.h:175
void Flush(bool fShutdown)
Definition: db.cpp:661
Db * pdb
Definition: db.h:170
static int count
Definition: tests.c:45
std::atomic< unsigned int > nUpdateCounter
Definition: db.h:148
bool(* recoverFunc_type)(const fs::path &file_path, std::string &out_backup_filename)
Definition: db.h:60
VerifyResult Verify(const std::string &strFile, recoverFunc_type recoverFunc, std::string &out_backup_filename)
Definition: db.cpp:230
static const bool DEFAULT_WALLET_PRIVDB
Definition: db.h:26
void clear()
Definition: streams.h:200
static bool VerifyDatabaseFile(const fs::path &file_path, std::string &warningStr, std::string &errorStr, BerkeleyEnvironment::recoverFunc_type recoverFunc)
Definition: db.cpp:340
void Reset()
Definition: db.cpp:111
void CloseDb(const std::string &strFile)
Definition: db.cpp:558
bool IsMock() const
Definition: db.h:47
~BerkeleyEnvironment()
Definition: db.cpp:123
BerkeleyEnvironment * env
BerkeleyDB specific.
Definition: db.h:155
bool fMockDb
Definition: db.h:32
static const int CLIENT_VERSION
dashd-res.rc includes this file, but it cannot cope with real c++ code.
Definition: clientversion.h:38
std::string _(const char *psz)
Translation function: Call Translate signal on UI interface, which returns a boost::optional result...
Definition: util.h:92
Wrapped mutex: supports recursive locking, but no waiting TODO: We should move away from using the re...
Definition: sync.h:94
std::vector< unsigned char > ParseHex(const char *psz)
bool IsDummy()
Return whether this database handle is a dummy for testing.
Definition: db.h:162
Released under the MIT license