PIVX Core  5.6.99
P2P Digital Currency
db.cpp
Go to the documentation of this file.
1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2021 The Bitcoin developers
3 // Copyright (c) 2019-2021 The PIVX Core developers
4 // Distributed under the MIT/X11 software license, see the accompanying
5 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
6 
7 #include "db.h"
8 
9 #include "addrman.h"
10 #include "guiinterfaceutil.h"
11 #include "hash.h"
12 #include "protocol.h"
13 #include "util/system.h"
14 #include "utilstrencodings.h"
15 #include "wallet/walletutil.h"
16 
17 #include <stdint.h>
18 
19 #ifndef WIN32
20 #include <sys/stat.h>
21 #endif
22 
23 #include <boost/thread.hpp>
24 
25 
26 namespace {
27 
37 void CheckUniqueFileid(const BerkeleyEnvironment& env, const std::string& filename, Db& db, WalletDatabaseFileId& fileid)
38 {
39  if (env.IsMock()) return;
40 
41  int ret = db.get_mpf()->get_fileid(fileid.value);
42  if (ret != 0) {
43  throw std::runtime_error(strprintf("BerkeleyBatch: Can't open database %s (get_fileid failed with %d)", filename, ret));
44  }
45 
46  for (const auto& item : env.m_fileids) {
47  if (fileid == item.second && &fileid != &item.second) {
48  throw std::runtime_error(strprintf("BerkeleyBatch: Can't open database %s (duplicates fileid %s from %s)", filename,
49  HexStr(item.second.value), item.first));
50  }
51  }
52 }
53 
54 RecursiveMutex cs_db;
55 std::map<std::string, BerkeleyEnvironment> g_dbenvs;
56 } // namespace
57 
59 {
60  return memcmp(value, &rhs.value, sizeof(value)) == 0;
61 }
62 
63 BerkeleyEnvironment* GetWalletEnv(const fs::path& wallet_path, std::string& database_filename)
64 {
65  fs::path env_directory;
66  if (fs::is_regular_file(wallet_path)) {
67  // Special case for backwards compatibility: if wallet path points to an
68  // existing file, treat it as the path to a BDB data file in a parent
69  // directory that also contains BDB log files.
70  env_directory = wallet_path.parent_path();
71  database_filename = wallet_path.filename().string();
72  } else {
73  // Normal case: Interpret wallet path as a directory path containing
74  // data and log files.
75  env_directory = wallet_path;
76  database_filename = "wallet.dat";
77  }
78  LOCK(cs_db);
79  // Note: An unused temporary BerkeleyEnvironment object may be created inside the
80  // emplace function if the key already exists. This is a little inefficient,
81  // but not a big concern since the map will be changed in the future to hold
82  // pointers instead of objects, anyway.
83  return &g_dbenvs.emplace(std::piecewise_construct, std::forward_as_tuple(env_directory.string()), std::forward_as_tuple(env_directory)).first->second;
84 }
85 
86 //
87 // BerkeleyBatch
88 //
89 
91 {
92  if (!fDbEnvInit)
93  return;
94 
95  fDbEnvInit = false;
96 
97  for (auto& db : mapDb) {
98  auto count = mapFileUseCount.find(db.first);
99  assert(count == mapFileUseCount.end() || count->second == 0);
100  if (db.second) {
101  db.second->close(0);
102  delete db.second;
103  db.second = nullptr;
104  }
105  }
106 
107  int ret = dbenv->close(0);
108  if (ret != 0)
109  LogPrintf("%s: Error %d closing database environment: %s\n", __func__, ret, DbEnv::strerror(ret));
110  if (!fMockDb)
111  DbEnv((u_int32_t)0).remove(strPath.c_str(), 0);
112 }
113 
115 {
116  dbenv.reset(new DbEnv(DB_CXX_NO_EXCEPTIONS));
117  fDbEnvInit = false;
118  fMockDb = false;
119 }
120 
121 BerkeleyEnvironment::BerkeleyEnvironment(const fs::path& dir_path) : strPath(dir_path.string())
122 {
123  Reset();
124 }
125 
127 {
128  Close();
129 }
130 
132 {
133  if (fDbEnvInit)
134  return true;
135 
136  boost::this_thread::interruption_point();
137 
138  fs::path pathIn = strPath;
139  TryCreateDirectories(pathIn);
140  if (!LockDirectory(pathIn, ".walletlock")) {
141  LogPrintf("Cannot obtain a lock on wallet directory %s. Another instance of PIVX may be using it.\n", strPath);
142  return false;
143  }
144 
145  fs::path pathLogDir = pathIn / "database";
146  TryCreateDirectories(pathLogDir);
147  fs::path pathErrorFile = pathIn / "db.log";
148  LogPrintf("BerkeleyEnvironment::Open: LogDir=%s ErrorFile=%s\n", pathLogDir.string(), pathErrorFile.string());
149 
150  unsigned int nEnvFlags = 0;
151  if (gArgs.GetBoolArg("-privdb", DEFAULT_WALLET_PRIVDB))
152  nEnvFlags |= DB_PRIVATE;
153 
154  dbenv->set_lg_dir(pathLogDir.string().c_str());
155  dbenv->set_cachesize(0, 0x100000, 1); // 1 MiB should be enough for just the wallet
156  dbenv->set_lg_bsize(0x10000);
157  dbenv->set_lg_max(1048576);
158  dbenv->set_lk_max_locks(40000);
159  dbenv->set_lk_max_objects(40000);
160  dbenv->set_errfile(fsbridge::fopen(pathErrorFile, "a"));
161  dbenv->set_flags(DB_AUTO_COMMIT, 1);
162  dbenv->set_flags(DB_TXN_WRITE_NOSYNC, 1);
163  dbenv->log_set_config(DB_LOG_AUTO_REMOVE, 1);
164  int ret = dbenv->open(strPath.c_str(),
165  DB_CREATE |
166  DB_INIT_LOCK |
167  DB_INIT_LOG |
168  DB_INIT_MPOOL |
169  DB_INIT_TXN |
170  DB_THREAD |
171  DB_RECOVER |
172  nEnvFlags,
173  S_IRUSR | S_IWUSR);
174  if (ret != 0) {
175  LogPrintf("%s: Error %d opening database environment: %s\n", __func__, ret, DbEnv::strerror(ret));
176  int ret2 = dbenv->close(0);
177  if (ret2 != 0) {
178  LogPrintf("%s: Error %d closing failed database environment: %s\n", __func__, ret2, DbEnv::strerror(ret2));
179  }
180  Reset();
181  if (retry) {
182  // try moving the database env out of the way
183  fs::path pathDatabaseBak = pathIn / strprintf("database.%d.bak", GetTime());
184  try {
185  fs::rename(pathLogDir, pathDatabaseBak);
186  LogPrintf("Moved old %s to %s. Retrying.\n", pathLogDir.string(), pathDatabaseBak.string());
187  } catch (const fs::filesystem_error&) {
188  // failure is ok (well, not really, but it's not worse than what we started with)
189  }
190  // try opening it again one more time
191  if (!Open(false /* retry */)) {
192  // if it still fails, it probably means we can't even create the database env
193  return false;
194  }
195  } else {
196  return false;
197  }
198  }
199 
200  fDbEnvInit = true;
201  fMockDb = false;
202  return true;
203 }
204 
206 {
207  if (fDbEnvInit)
208  throw std::runtime_error("BerkeleyEnvironment::MakeMock : Already initialized");
209 
210  boost::this_thread::interruption_point();
211 
212  LogPrint(BCLog::DB, "BerkeleyEnvironment::MakeMock\n");
213 
214  dbenv->set_cachesize(1, 0, 1);
215  dbenv->set_lg_bsize(10485760 * 4);
216  dbenv->set_lg_max(10485760);
217  dbenv->set_lk_max_locks(10000);
218  dbenv->set_lk_max_objects(10000);
219  dbenv->set_flags(DB_AUTO_COMMIT, 1);
220  dbenv->log_set_config(DB_LOG_IN_MEMORY, 1);
221  int ret = dbenv->open(nullptr,
222  DB_CREATE |
223  DB_INIT_LOCK |
224  DB_INIT_LOG |
225  DB_INIT_MPOOL |
226  DB_INIT_TXN |
227  DB_THREAD |
228  DB_PRIVATE,
229  S_IRUSR | S_IWUSR);
230  if (ret > 0)
231  throw std::runtime_error(strprintf("BerkeleyEnvironment::MakeMock : Error %d opening database environment.", ret));
232 
233  fDbEnvInit = true;
234  fMockDb = true;
235 }
236 
237 BerkeleyEnvironment::VerifyResult BerkeleyEnvironment::Verify(const std::string& strFile, recoverFunc_type recoverFunc, std::string& out_backup_filename)
238 {
239  LOCK(cs_db);
240  assert(mapFileUseCount.count(strFile) == 0);
241 
242  Db db(dbenv.get(), 0);
243  int result = db.verify(strFile.c_str(), nullptr, nullptr, 0);
244  if (result == 0)
245  return VERIFY_OK;
246  else if (recoverFunc == nullptr)
247  return RECOVER_FAIL;
248 
249  // Try to recover:
250  bool fRecovered = (*recoverFunc)(fs::path(strPath) / strFile, out_backup_filename);
251  return (fRecovered ? RECOVER_OK : RECOVER_FAIL);
252 }
253 
254 bool BerkeleyBatch::Recover(const fs::path& file_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& newFilename)
255 {
256  std::string filename;
257  BerkeleyEnvironment* env = GetWalletEnv(file_path, filename);
258 
259  // Recovery procedure:
260  // move wallet file to walletfilename.timestamp.bak
261  // Call Salvage with fAggressive=true to
262  // get as much data as possible.
263  // Rewrite salvaged data to fresh wallet file
264  // Set -rescan so any missing transactions will be
265  // found.
266  int64_t now = GetTime();
267  newFilename = strprintf("%s.%d.bak", filename, now);
268 
269  int result = env->dbenv->dbrename(nullptr, filename.c_str(), nullptr,
270  newFilename.c_str(), DB_AUTO_COMMIT);
271  if (result == 0) {
272  LogPrintf("Renamed %s to %s\n", filename, newFilename);
273  } else {
274  LogPrintf("Failed to rename %s to %s\n", filename, newFilename);
275  return false;
276  }
277 
278  std::vector<BerkeleyEnvironment::KeyValPair> salvagedData;
279  bool fSuccess = env->Salvage(newFilename, true, salvagedData);
280  if (salvagedData.empty())
281  {
282  LogPrintf("Salvage(aggressive) found no records in %s.\n", newFilename);
283  return false;
284  }
285  LogPrintf("Salvage(aggressive) found %u records\n", salvagedData.size());
286 
287  std::unique_ptr<Db> pdbCopy = std::make_unique<Db>(env->dbenv.get(), 0);
288  int ret = pdbCopy->open(nullptr, // Txn pointer
289  filename.c_str(), // Filename
290  "main", // Logical db name
291  DB_BTREE, // Database type
292  DB_CREATE, // Flags
293  0);
294  if (ret > 0) {
295  LogPrintf("Cannot create database file %s\n", filename);
296  pdbCopy->close(0);
297  return false;
298  }
299 
300  DbTxn* ptxn = env->TxnBegin();
301  for (BerkeleyEnvironment::KeyValPair& row : salvagedData)
302  {
303  if (recoverKVcallback)
304  {
305  CDataStream ssKey(row.first, SER_DISK, CLIENT_VERSION);
306  CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION);
307  if (!(*recoverKVcallback)(callbackDataIn, ssKey, ssValue))
308  continue;
309  }
310  Dbt datKey(&row.first[0], row.first.size());
311  Dbt datValue(&row.second[0], row.second.size());
312  int ret2 = pdbCopy->put(ptxn, &datKey, &datValue, DB_NOOVERWRITE);
313  if (ret2 > 0)
314  fSuccess = false;
315  }
316  ptxn->commit(0);
317  pdbCopy->close(0);
318 
319  return fSuccess;
320 }
321 
322 bool BerkeleyBatch::VerifyEnvironment(const fs::path& file_path, std::string& errorStr)
323 {
324  std::string walletFile;
325  BerkeleyEnvironment* env = GetWalletEnv(file_path, walletFile);
326  fs::path walletDir = env->Directory();
327 
328  LogPrintf("Using BerkeleyDB version %s\n", DbEnv::version(0, 0, 0));
329  LogPrintf("Using wallet %s\n", walletFile);
330 
331  // Wallet file must be a plain filename without a directory
332  fs::path wallet_file_path(walletFile);
333  if (walletFile != wallet_file_path.filename().string())
334  return UIError(strprintf(_("Wallet %s resides outside data directory %s"), walletFile, walletDir.string()));
335 
336  if (!env->Open(true /* retry */)) {
337  errorStr = strprintf(_("Error initializing wallet database environment %s!"), walletDir);
338  return false;
339  }
340 
341  return true;
342 }
343 
344 bool BerkeleyBatch::VerifyDatabaseFile(const fs::path& file_path, std::string& warningStr, std::string& errorStr, BerkeleyEnvironment::recoverFunc_type recoverFunc)
345 {
346  std::string walletFile;
347  BerkeleyEnvironment* env = GetWalletEnv(file_path, walletFile);
348  fs::path walletDir = env->Directory();
349 
350  if (fs::exists(walletDir / walletFile))
351  {
352  std::string backup_filename;
353  BerkeleyEnvironment::VerifyResult r = env->Verify(walletFile, recoverFunc, backup_filename);
355  {
356  warningStr = strprintf(_("Warning: Wallet file corrupt, data salvaged!"
357  " Original %s saved as %s in %s; if"
358  " your balance or transactions are incorrect you should"
359  " restore from a backup."),
360  walletFile, backup_filename, walletDir);
361  }
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.emplace_back(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 = std::make_unique<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  // PIVX 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", 100) * 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<RecursiveMutex> 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 = std::make_unique<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 mi = env->mapFileUseCount.begin();
746  while (mi != env->mapFileUseCount.end()) {
747  nRefCount += (*mi).second;
748  mi++;
749  }
750 
751  if (nRefCount == 0) {
752  boost::this_thread::interruption_point();
753  std::map<std::string, int>::iterator _mi = env->mapFileUseCount.find(strFile);
754  if (_mi != env->mapFileUseCount.end()) {
755  LogPrint(BCLog::DB, "Flushing %s\n", strFile);
756  int64_t nStart = GetTimeMillis();
757 
758  // Flush wallet file so it's self contained
759  env->CloseDb(strFile);
761 
762  env->mapFileUseCount.erase(_mi++);
763  LogPrint(BCLog::DB, "Flushed %s %dms\n", strFile, GetTimeMillis() - nStart);
764  ret = true;
765  }
766  }
767  }
768 
769  return ret;
770 }
771 
772 bool BerkeleyDatabase::Rewrite(const char* pszSkip)
773 {
774  return BerkeleyBatch::Rewrite(*this, pszSkip);
775 }
776 
777 bool BerkeleyDatabase::Backup(const std::string& strDest)
778 {
779  if (IsDummy()) {
780  return false;
781  }
782  while (true)
783  {
784  {
785  LOCK(cs_db);
786  if (!env->mapFileUseCount.count(strFile) || env->mapFileUseCount[strFile] == 0)
787  {
788  // Flush log data to the dat file
789  env->CloseDb(strFile);
791  env->mapFileUseCount.erase(strFile);
792 
793  // Copy wallet file
794  fs::path pathSrc = env->Directory() / strFile;
795  fs::path pathDest(strDest);
796  if (fs::is_directory(pathDest))
797  pathDest /= strFile;
798 
799  try {
800  if (fs::equivalent(pathSrc, pathDest)) {
801  LogPrintf("cannot backup to wallet source file %s\n", pathDest.string());
802  return false;
803  }
804 
805 #if BOOST_VERSION >= 107400
806  fs::copy_file(pathSrc.c_str(), pathDest, fs::copy_options::overwrite_existing);
807 #elif BOOST_VERSION >= 105800 /* BOOST_LIB_VERSION 1_58 */
808  fs::copy_file(pathSrc.c_str(), pathDest, fs::copy_option::overwrite_if_exists);
809 #endif
810  LogPrintf("copied %s to %s\n", strFile, pathDest.string());
811  return true;
812  } catch (const fs::filesystem_error& e) {
813  LogPrintf("error copying %s to %s - %s\n", strFile, pathDest.string(), fsbridge::get_filesystem_error_message(e));
814  return false;
815  }
816  }
817  }
818  MilliSleep(100);
819  }
820 }
821 
822 void BerkeleyDatabase::Flush(bool shutdown)
823 {
824  if (!IsDummy()) {
825  env->Flush(shutdown);
826  if (shutdown) {
827  LOCK(cs_db);
828  g_dbenvs.erase(env->Directory().string());
829  env = nullptr;
830  } else {
831  // TODO: To avoid g_dbenvs.erase erasing the environment prematurely after the
832  // first database shutdown when multiple databases are open in the same
833  // environment, should replace raw database `env` pointers with shared or weak
834  // pointers, or else separate the database and environment shutdowns so
835  // environments can be shut down after databases.
836  env->m_fileids.erase(strFile);
837  }
838  }
839 }
840 
842 {
843  if (!IsDummy()) {
844  env->ReloadDbEnv();
845  }
846 }
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
Definition: system.cpp:449
bool GetBoolArg(const std::string &strArg, bool fDefault) const
Return boolean argument or default value.
Definition: system.cpp:465
RAII class that provides access to a Berkeley database.
Definition: db.h:180
bool WriteVersion(int nVersion)
Definition: db.h:397
std::string strFile
Definition: db.h:183
Dbc * GetCursor()
Definition: db.h:315
static bool VerifyEnvironment(const fs::path &file_path, std::string &errorStr)
Definition: db.cpp:322
bool Exists(const K &key)
Definition: db.h:296
static bool PeriodicFlush(BerkeleyDatabase &database)
Definition: db.cpp:732
static bool VerifyDatabaseFile(const fs::path &file_path, std::string &warningStr, std::string &errorStr, BerkeleyEnvironment::recoverFunc_type recoverFunc)
Definition: db.cpp:344
int ReadAtCursor(Dbc *pcursor, CDataStream &ssKey, CDataStream &ssValue, bool setRange=false)
Definition: db.h:326
BerkeleyEnvironment * env
Definition: db.h:187
void Close()
Definition: db.cpp:543
Db * pdb
Definition: db.h:182
DbTxn * activeTxn
Definition: db.h:184
bool fFlushOnClose
Definition: db.h:186
bool fReadOnly
Definition: db.h:185
void Flush()
Definition: db.cpp:525
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:254
BerkeleyBatch(BerkeleyDatabase &database, const char *pszMode="r+", bool fFlushOnCloseIn=true)
Definition: db.cpp:446
static bool Rewrite(BerkeleyDatabase &database, const char *pszSkip=nullptr)
Definition: db.cpp:602
An instance of this class represents one database.
Definition: db.h:105
bool Rewrite(const char *pszSkip=nullptr)
Rewrite the entire database on disk, with the exception of key pszSkip if non-zero.
Definition: db.cpp:772
void Flush(bool shutdown)
Make sure all changes are flushed to disk.
Definition: db.cpp:822
BerkeleyEnvironment * env
BerkeleyDB specific.
Definition: db.h:167
bool Backup(const std::string &strDest)
Back up the entire database to a file.
Definition: db.cpp:777
void IncrementUpdateCounter()
Definition: db.cpp:538
bool IsDummy()
Return whether this database handle is a dummy for testing.
Definition: db.h:174
std::string strFile
Definition: db.h:168
void ReloadDbEnv()
Definition: db.cpp:841
std::atomic< unsigned int > nUpdateCounter
Definition: db.h:159
std::unordered_map< std::string, WalletDatabaseFileId > m_fileids
Definition: db.h:46
std::map< std::string, Db * > mapDb
Definition: db.h:45
void MakeMock()
Definition: db.cpp:205
fs::path Directory() const
Definition: db.h:56
std::map< std::string, int > mapFileUseCount
Definition: db.h:44
bool IsMock() const
Definition: db.h:54
void ReloadDbEnv()
Definition: db.cpp:576
bool Open(bool retry)
Definition: db.cpp:131
bool(* recoverFunc_type)(const fs::path &file_path, std::string &out_backup_filename)
Definition: db.h:67
BerkeleyEnvironment(const fs::path &env_directory)
Definition: db.cpp:121
bool fDbEnvInit
Definition: db.h:36
void Close()
Definition: db.cpp:90
VerifyResult Verify(const std::string &strFile, recoverFunc_type recoverFunc, std::string &out_backup_filename)
Definition: db.cpp:237
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
void Reset()
Definition: db.cpp:114
VerifyResult
Verify that database file strFile is OK.
Definition: db.h:64
std::string strPath
Definition: db.h:40
DbTxn * TxnBegin(int flags=DB_TXN_WRITE_NOSYNC)
Definition: db.h:88
std::unique_ptr< DbEnv > dbenv
Definition: db.h:43
std::condition_variable_any m_db_in_use
Definition: db.h:47
void CheckpointLSN(const std::string &strFile)
Definition: db.cpp:437
void Flush(bool fShutdown)
Definition: db.cpp:691
bool Salvage(const std::string &strFile, bool fAggressive, std::vector< KeyValPair > &vResult)
Definition: db.cpp:376
~BerkeleyEnvironment()
Definition: db.cpp:126
bool fMockDb
Definition: db.h:37
void CloseDb(const std::string &strFile)
Definition: db.cpp:562
value_type * data()
Definition: streams.h:178
size_type size() const
Definition: streams.h:165
void clear()
Definition: streams.h:171
BerkeleyEnvironment * GetWalletEnv(const fs::path &wallet_path, std::string &database_filename)
Get BerkeleyEnvironment and database filename given a wallet path.
Definition: db.cpp:63
@ LOCK
Definition: lockunlock.h:16
#define LogPrint(category,...)
Definition: logging.h:163
@ DB
Definition: logging.h:47
FILE * fopen(const fs::path &p, const char *mode)
Definition: fs.cpp:23
std::string get_filesystem_error_message(const fs::filesystem_error &e)
Definition: fs.cpp:130
int flags
Definition: pivx-tx.cpp:400
@ SER_DISK
Definition: serialize.h:175
u_int8_t value[DB_FILE_ID_LEN]
Definition: db.h:29
bool operator==(const WalletDatabaseFileId &rhs) const
Definition: db.cpp:58
#define AssertLockNotHeld(cs)
Definition: sync.h:76
#define TRY_LOCK(cs, name)
Definition: sync.h:224
bool TryCreateDirectories(const fs::path &p)
Ignores exceptions thrown by Boost's create_directories if the requested directory exists.
Definition: system.cpp:891
ArgsManager gArgs
Definition: system.cpp:89
bool LockDirectory(const fs::path &directory, const std::string &lockfile_name, bool probe_only)
Definition: system.cpp:110
std::string _(const char *psz)
Translation function: Call Translate signal on UI interface, which returns a Optional result.
Definition: system.h:65
#define strprintf
Definition: tinyformat.h:1056
std::string HexStr(const Span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
std::vector< unsigned char > ParseHex(const char *psz)
int64_t GetTimeMillis()
Returns the system time (not mockable)
Definition: utiltime.cpp:61
void MilliSleep(int64_t n)
Definition: utiltime.cpp:82
int64_t GetTime()
DEPRECATED Use either GetSystemTimeInSeconds (not mockable) or GetTime<T> (mockable)
Definition: utiltime.cpp:27