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