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