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