Avoid opening copied wallet databases simultaneously
[bitcoinplatinum.git] / src / wallet / db.cpp
blob459d289a4278f136c9455be111879981abdeb8ed
1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2016 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.
6 #include "db.h"
8 #include "addrman.h"
9 #include "fs.h"
10 #include "hash.h"
11 #include "protocol.h"
12 #include "util.h"
13 #include "utilstrencodings.h"
15 #include <stdint.h>
17 #ifndef WIN32
18 #include <sys/stat.h>
19 #endif
21 #include <boost/thread.hpp>
23 namespace {
24 //! Make sure database has a unique fileid within the environment. If it
25 //! doesn't, throw an error. BDB caches do not work properly when more than one
26 //! open database has the same fileid (values written to one database may show
27 //! up in reads to other databases).
28 //!
29 //! BerkeleyDB generates unique fileids by default
30 //! (https://docs.oracle.com/cd/E17275_01/html/programmer_reference/program_copy.html),
31 //! so bitcoin should never create different databases with the same fileid, but
32 //! this error can be triggered if users manually copy database files.
33 void CheckUniqueFileid(const CDBEnv& env, const std::string& filename, Db& db)
35 if (env.IsMock()) return;
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));
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)"));
55 } // namespace
58 // CDB
61 CDBEnv bitdb;
63 void CDBEnv::EnvShutdown()
65 if (!fDbEnvInit)
66 return;
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);
76 void CDBEnv::Reset()
78 delete dbenv;
79 dbenv = new DbEnv(DB_CXX_NO_EXCEPTIONS);
80 fDbEnvInit = false;
81 fMockDb = false;
84 CDBEnv::CDBEnv() : dbenv(nullptr)
86 Reset();
89 CDBEnv::~CDBEnv()
91 EnvShutdown();
92 delete dbenv;
93 dbenv = nullptr;
96 void CDBEnv::Close()
98 EnvShutdown();
101 bool CDBEnv::Open(const fs::path& pathIn)
103 if (fDbEnvInit)
104 return true;
106 boost::this_thread::interruption_point();
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());
114 unsigned int nEnvFlags = 0;
115 if (gArgs.GetBoolArg("-privdb", DEFAULT_WALLET_PRIVDB))
116 nEnvFlags |= DB_PRIVATE;
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")); /// debug
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));
143 fDbEnvInit = true;
144 fMockDb = false;
145 return true;
148 void CDBEnv::MakeMock()
150 if (fDbEnvInit)
151 throw std::runtime_error("CDBEnv::MakeMock: Already initialized");
153 boost::this_thread::interruption_point();
155 LogPrint(BCLog::DB, "CDBEnv::MakeMock\n");
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));
176 fDbEnvInit = true;
177 fMockDb = true;
180 CDBEnv::VerifyResult CDBEnv::Verify(const std::string& strFile, recoverFunc_type recoverFunc, std::string& out_backup_filename)
182 LOCK(cs_db);
183 assert(mapFileUseCount.count(strFile) == 0);
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;
192 // Try to recover:
193 bool fRecovered = (*recoverFunc)(strFile, out_backup_filename);
194 return (fRecovered ? RECOVER_OK : RECOVER_FAIL);
197 bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& newFilename)
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);
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
215 LogPrintf("Failed to rename %s to %s\n", filename, newFilename);
216 return false;
219 std::vector<CDBEnv::KeyValPair> salvagedData;
220 bool fSuccess = bitdb.Salvage(newFilename, true, salvagedData);
221 if (salvagedData.empty())
223 LogPrintf("Salvage(aggressive) found no records in %s.\n", newFilename);
224 return false;
226 LogPrintf("Salvage(aggressive) found %u records\n", salvagedData.size());
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
235 if (ret > 0) {
236 LogPrintf("Cannot create database file %s\n", filename);
237 pdbCopy->close(0);
238 return false;
241 DbTxn* ptxn = bitdb.TxnBegin();
242 for (CDBEnv::KeyValPair& row : salvagedData)
244 if (recoverKVcallback)
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;
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;
257 ptxn->commit(0);
258 pdbCopy->close(0);
260 return fSuccess;
263 bool CDB::VerifyEnvironment(const std::string& walletFile, const fs::path& dataDir, std::string& errorStr)
265 LogPrintf("Using BerkeleyDB version %s\n", DbEnv::version(0, 0, 0));
266 LogPrintf("Using wallet %s\n", walletFile);
268 // Wallet file must be a plain filename without a directory
269 if (walletFile != fs::basename(walletFile) + fs::extension(walletFile))
271 errorStr = strprintf(_("Wallet %s resides outside data directory %s"), walletFile, dataDir.string());
272 return false;
275 if (!bitdb.Open(dataDir))
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)
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;
294 return true;
297 bool CDB::VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr, CDBEnv::recoverFunc_type recoverFunc)
299 if (fs::exists(dataDir / walletFile))
301 std::string backup_filename;
302 CDBEnv::VerifyResult r = bitdb.Verify(walletFile, recoverFunc, backup_filename);
303 if (r == CDBEnv::RECOVER_OK)
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);
311 if (r == CDBEnv::RECOVER_FAIL)
313 errorStr = strprintf(_("%s corrupt, salvage failed"), walletFile);
314 return false;
317 // also return true if files does not exists
318 return true;
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";
326 bool CDBEnv::Salvage(const std::string& strFile, bool fAggressive, std::vector<CDBEnv::KeyValPair>& vResult)
328 LOCK(cs_db);
329 assert(mapFileUseCount.count(strFile) == 0);
331 u_int32_t flags = DB_SALVAGE;
332 if (fAggressive)
333 flags |= DB_AGGRESSIVE;
335 std::stringstream strDump;
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;
346 if (result != 0 && result != DB_VERIFY_BAD) {
347 LogPrintf("CDBEnv::Salvage: Database salvage failed with result %d.\n", result);
348 return false;
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
359 std::string strLine;
360 while (!strDump.eof() && strLine != HEADER_END)
361 getline(strDump, strLine); // Skip past header
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;
374 vResult.push_back(make_pair(ParseHex(keyHex), ParseHex(valueHex)));
378 if (keyHex != DATA_END) {
379 LogPrintf("CDBEnv::Salvage: WARNING: Unexpected end of file while reading salvage output.\n");
380 return false;
383 return (result == 0);
387 void CDBEnv::CheckpointLSN(const std::string& strFile)
389 dbenv->txn_checkpoint(0, 0, 0);
390 if (fMockDb)
391 return;
392 dbenv->lsn_reset(strFile.c_str(), 0);
396 CDB::CDB(CWalletDBWrapper& dbw, const char* pszMode, bool fFlushOnCloseIn) : pdb(nullptr), activeTxn(nullptr)
398 fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w'));
399 fFlushOnClose = fFlushOnCloseIn;
400 env = dbw.env;
401 if (dbw.IsDummy()) {
402 return;
404 const std::string &strFilename = dbw.strFile;
406 bool fCreate = strchr(pszMode, 'c') != nullptr;
407 unsigned int nFlags = DB_THREAD;
408 if (fCreate)
409 nFlags |= DB_CREATE;
412 LOCK(env->cs_db);
413 if (!env->Open(GetDataDir()))
414 throw std::runtime_error("CDB: Failed to open database environment.");
416 pdb = env->mapDb[strFilename];
417 if (pdb == nullptr) {
418 int ret;
419 std::unique_ptr<Db> pdb_temp(new Db(env->dbenv, 0));
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));
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
437 if (ret != 0) {
438 throw std::runtime_error(strprintf("CDB: Error %d, can't open database %s", ret, strFilename));
440 CheckUniqueFileid(*env, strFilename, *pdb_temp);
442 pdb = pdb_temp.release();
443 env->mapDb[strFilename] = pdb;
445 if (fCreate && !Exists(std::string("version"))) {
446 bool fTmp = fReadOnly;
447 fReadOnly = false;
448 WriteVersion(CLIENT_VERSION);
449 fReadOnly = fTmp;
452 ++env->mapFileUseCount[strFilename];
453 strFile = strFilename;
457 void CDB::Flush()
459 if (activeTxn)
460 return;
462 // Flush database activity from memory pool to disk log
463 unsigned int nMinutes = 0;
464 if (fReadOnly)
465 nMinutes = 1;
467 env->dbenv->txn_checkpoint(nMinutes ? gArgs.GetArg("-dblogsize", DEFAULT_WALLET_DBLOGSIZE) * 1024 : 0, nMinutes, 0);
470 void CWalletDBWrapper::IncrementUpdateCounter()
472 ++nUpdateCounter;
475 void CDB::Close()
477 if (!pdb)
478 return;
479 if (activeTxn)
480 activeTxn->abort();
481 activeTxn = nullptr;
482 pdb = nullptr;
484 if (fFlushOnClose)
485 Flush();
488 LOCK(env->cs_db);
489 --env->mapFileUseCount[strFile];
493 void CDBEnv::CloseDb(const std::string& strFile)
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;
507 bool CDB::Rewrite(CWalletDBWrapper& dbw, const char* pszSkip)
509 if (dbw.IsDummy()) {
510 return true;
512 CDBEnv *env = dbw.env;
513 const std::string& strFile = dbw.strFile;
514 while (true) {
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);
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);
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
536 if (ret > 0) {
537 LogPrintf("CDB::Rewrite: Can't create database file %s\n", strFileRes);
538 fSuccess = false;
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;
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;
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;
569 if (fSuccess) {
570 db.Close();
571 env->CloseDb(strFile);
572 if (pdbCopy->close(0))
573 fSuccess = false;
574 } else {
575 pdbCopy->close(0);
577 delete pdbCopy;
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;
587 if (!fSuccess)
588 LogPrintf("CDB::Rewrite: Failed to rewrite database file %s\n", strFileRes);
589 return fSuccess;
592 MilliSleep(100);
597 void CDBEnv::Flush(bool fShutdown)
599 int64_t nStart = GetTimeMillis();
600 // Flush log data to the actual data file on all files that are not in use
601 LogPrint(BCLog::DB, "CDBEnv::Flush: Flush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started");
602 if (!fDbEnvInit)
603 return;
605 LOCK(cs_db);
606 std::map<std::string, int>::iterator mi = mapFileUseCount.begin();
607 while (mi != mapFileUseCount.end()) {
608 std::string strFile = (*mi).first;
609 int nRefCount = (*mi).second;
610 LogPrint(BCLog::DB, "CDBEnv::Flush: Flushing %s (refcount = %d)...\n", strFile, nRefCount);
611 if (nRefCount == 0) {
612 // Move log data to the dat file
613 CloseDb(strFile);
614 LogPrint(BCLog::DB, "CDBEnv::Flush: %s checkpoint\n", strFile);
615 dbenv->txn_checkpoint(0, 0, 0);
616 LogPrint(BCLog::DB, "CDBEnv::Flush: %s detach\n", strFile);
617 if (!fMockDb)
618 dbenv->lsn_reset(strFile.c_str(), 0);
619 LogPrint(BCLog::DB, "CDBEnv::Flush: %s closed\n", strFile);
620 mapFileUseCount.erase(mi++);
621 } else
622 mi++;
624 LogPrint(BCLog::DB, "CDBEnv::Flush: Flush(%s)%s took %15dms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started", GetTimeMillis() - nStart);
625 if (fShutdown) {
626 char** listp;
627 if (mapFileUseCount.empty()) {
628 dbenv->log_archive(&listp, DB_ARCH_REMOVE);
629 Close();
630 if (!fMockDb)
631 fs::remove_all(fs::path(strPath) / "database");
637 bool CDB::PeriodicFlush(CWalletDBWrapper& dbw)
639 if (dbw.IsDummy()) {
640 return true;
642 bool ret = false;
643 CDBEnv *env = dbw.env;
644 const std::string& strFile = dbw.strFile;
645 TRY_LOCK(bitdb.cs_db,lockDb);
646 if (lockDb)
648 // Don't do this if any databases are in use
649 int nRefCount = 0;
650 std::map<std::string, int>::iterator mit = env->mapFileUseCount.begin();
651 while (mit != env->mapFileUseCount.end())
653 nRefCount += (*mit).second;
654 mit++;
657 if (nRefCount == 0)
659 boost::this_thread::interruption_point();
660 std::map<std::string, int>::iterator mi = env->mapFileUseCount.find(strFile);
661 if (mi != env->mapFileUseCount.end())
663 LogPrint(BCLog::DB, "Flushing %s\n", strFile);
664 int64_t nStart = GetTimeMillis();
666 // Flush wallet file so it's self contained
667 env->CloseDb(strFile);
668 env->CheckpointLSN(strFile);
670 env->mapFileUseCount.erase(mi++);
671 LogPrint(BCLog::DB, "Flushed %s %dms\n", strFile, GetTimeMillis() - nStart);
672 ret = true;
677 return ret;
680 bool CWalletDBWrapper::Rewrite(const char* pszSkip)
682 return CDB::Rewrite(*this, pszSkip);
685 bool CWalletDBWrapper::Backup(const std::string& strDest)
687 if (IsDummy()) {
688 return false;
690 while (true)
693 LOCK(env->cs_db);
694 if (!env->mapFileUseCount.count(strFile) || env->mapFileUseCount[strFile] == 0)
696 // Flush log data to the dat file
697 env->CloseDb(strFile);
698 env->CheckpointLSN(strFile);
699 env->mapFileUseCount.erase(strFile);
701 // Copy wallet file
702 fs::path pathSrc = GetDataDir() / strFile;
703 fs::path pathDest(strDest);
704 if (fs::is_directory(pathDest))
705 pathDest /= strFile;
707 try {
708 fs::copy_file(pathSrc, pathDest, fs::copy_option::overwrite_if_exists);
709 LogPrintf("copied %s to %s\n", strFile, pathDest.string());
710 return true;
711 } catch (const fs::filesystem_error& e) {
712 LogPrintf("error copying %s to %s - %s\n", strFile, pathDest.string(), e.what());
713 return false;
717 MilliSleep(100);
721 void CWalletDBWrapper::Flush(bool shutdown)
723 if (!IsDummy()) {
724 env->Flush(shutdown);