22 #include <boost/thread.hpp> 34 void CheckUniqueFileid(
const CDBEnv& env,
const std::string& filename, Db& db)
38 u_int8_t fileid[DB_FILE_ID_LEN];
39 int ret = db.get_mpf()->get_fileid(fileid);
41 throw std::runtime_error(
strprintf(
"CDB: Can't open database %s (get_fileid failed with %d)", filename, ret));
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)"));
70 int ret =
dbenv->close(0);
72 LogPrintf(
"CDBEnv::EnvShutdown: Error %d shutting down database environment: %s\n", ret, DbEnv::strerror(ret));
74 DbEnv((u_int32_t)0).remove(
strPath.c_str(), 0);
80 dbenv =
new DbEnv(DB_CXX_NO_EXCEPTIONS);
107 boost::this_thread::interruption_point();
110 fs::path pathLogDir = pathIn /
"database";
112 fs::path pathErrorFile = pathIn /
"db.log";
113 LogPrintf(
"CDBEnv::Open: LogDir=%s ErrorFile=%s\n", pathLogDir.string(), pathErrorFile.string());
115 unsigned int nEnvFlags = 0;
117 nEnvFlags |= DB_PRIVATE;
119 dbenv->set_lg_dir(pathLogDir.string().c_str());
120 dbenv->set_cachesize(0, 0x100000, 1);
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);
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);
141 return error(
"CDBEnv::Open: Error %d opening database environment: %s\n", ret, DbEnv::strerror(ret));
152 throw std::runtime_error(
"CDBEnv::MakeMock: Already initialized");
154 boost::this_thread::interruption_point();
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,
175 throw std::runtime_error(
strprintf(
"CDBEnv::MakeMock: Error %d opening database environment.", ret));
187 int result = db.verify(strFile.c_str(),
nullptr,
nullptr, 0);
190 else if (recoverFunc ==
nullptr)
194 bool fRecovered = (*recoverFunc)(strFile, out_backup_filename);
198 bool CDB::Recover(
const std::string& filename,
void *callbackDataIn,
bool (*recoverKVcallback)(
void* callbackData,
CDataStream ssKey,
CDataStream ssValue), std::string& newFilename)
208 newFilename =
strprintf(
"%s.%d.bak", filename, now);
210 int result = bitdb.
dbenv->dbrename(
nullptr, filename.c_str(),
nullptr,
211 newFilename.c_str(), DB_AUTO_COMMIT);
213 LogPrintf(
"Renamed %s to %s\n", filename, newFilename);
216 LogPrintf(
"Failed to rename %s to %s\n", filename, newFilename);
220 std::vector<CDBEnv::KeyValPair> salvagedData;
221 bool fSuccess = bitdb.
Salvage(newFilename,
true, salvagedData);
222 if (salvagedData.empty())
224 LogPrintf(
"Salvage(aggressive) found no records in %s.\n", newFilename);
227 LogPrintf(
"Salvage(aggressive) found %u records\n", salvagedData.size());
229 std::unique_ptr<Db> pdbCopy(
new Db(bitdb.
dbenv, 0));
230 int ret = pdbCopy->open(
nullptr,
237 LogPrintf(
"Cannot create database file %s\n", filename);
245 if (recoverKVcallback)
249 if (!(*recoverKVcallback)(callbackDataIn, ssKey, ssValue))
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);
266 LogPrintf(
"Using BerkeleyDB version %s\n", DbEnv::version(0, 0, 0));
267 LogPrintf(
"Using wallet %s\n", walletFile);
270 if (walletFile != fs::basename(walletFile) + fs::extension(walletFile))
272 errorStr =
strprintf(
_(
"Wallet %s resides outside data directory %s"), walletFile, dataDir.string());
276 if (!bitdb.
Open(dataDir))
279 fs::path pathDatabase = dataDir /
"database";
280 fs::path pathDatabaseBak = dataDir /
strprintf(
"database.%d.bak",
GetTime());
282 fs::rename(pathDatabase, pathDatabaseBak);
283 LogPrintf(
"Moved old %s to %s. Retrying.\n", pathDatabase.string(), pathDatabaseBak.string());
284 }
catch (
const fs::filesystem_error&) {
289 if (!bitdb.
Open(dataDir)) {
300 if (fs::exists(dataDir / walletFile))
302 std::string backup_filename;
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);
314 errorStr =
strprintf(
_(
"%s corrupt, salvage failed"), walletFile);
323 static const char *HEADER_END =
"HEADER=END";
325 static const char *DATA_END =
"DATA=END";
327 bool CDBEnv::Salvage(
const std::string& strFile,
bool fAggressive, std::vector<CDBEnv::KeyValPair>& vResult)
332 u_int32_t
flags = DB_SALVAGE;
334 flags |= DB_AGGRESSIVE;
336 std::stringstream strDump;
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");
343 LogPrintf(
"CDBEnv::Salvage: Rerun with aggressive mode to ignore errors and continue.\n");
347 if (result != 0 && result != DB_VERIFY_BAD) {
348 LogPrintf(
"CDBEnv::Salvage: Database salvage failed with result %d.\n", result);
361 while (!strDump.eof() && strLine != HEADER_END)
362 getline(strDump, strLine);
364 std::string keyHex, valueHex;
365 while (!strDump.eof() && keyHex != DATA_END) {
366 getline(strDump, keyHex);
367 if (keyHex != DATA_END) {
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");
379 if (keyHex != DATA_END) {
380 LogPrintf(
"CDBEnv::Salvage: WARNING: Unexpected end of file while reading salvage output.\n");
384 return (result == 0);
390 dbenv->txn_checkpoint(0, 0, 0);
393 dbenv->lsn_reset(strFile.c_str(), 0);
399 fReadOnly = (!strchr(pszMode,
'+') && !strchr(pszMode,
'w'));
405 const std::string &strFilename = dbw.
strFile;
407 bool fCreate = strchr(pszMode,
'c') !=
nullptr;
408 unsigned int nFlags = DB_THREAD;
415 throw std::runtime_error(
"CDB: Failed to open database environment.");
418 if (
pdb ==
nullptr) {
420 std::unique_ptr<Db> pdb_temp(
new Db(env->
dbenv, 0));
422 bool fMockDb = env->
IsMock();
424 DbMpoolFile* mpf = pdb_temp->get_mpf();
425 ret = mpf->set_flags(DB_MPOOL_NOFILE, 1);
427 throw std::runtime_error(
strprintf(
"CDB: Failed to configure for no temp file backing for database %s", strFilename));
431 ret = pdb_temp->open(
nullptr,
432 fMockDb ?
nullptr : strFilename.c_str(),
433 fMockDb ? strFilename.c_str() :
"main",
439 throw std::runtime_error(
strprintf(
"CDB: Error %d, can't open database %s", ret, strFilename));
441 CheckUniqueFileid(*env, strFilename, *pdb_temp);
443 pdb = pdb_temp.release();
446 if (fCreate && !
Exists(std::string(
"version"))) {
464 unsigned int nMinutes = 0;
468 env->
dbenv->txn_checkpoint(nMinutes ?
gArgs.
GetArg(
"-dblogsize", DEFAULT_WALLET_DBLOGSIZE) * 1024 : 0, nMinutes, 0);
498 if (mapDb[strFile] !=
nullptr) {
524 bool fSuccess =
true;
525 LogPrintf(
"CDB::Rewrite: Rewriting %s...\n", strFile);
526 std::string strFileRes = strFile +
".rewrite";
529 Db* pdbCopy =
new Db(env->
dbenv, 0);
531 int ret = pdbCopy->open(
nullptr,
538 LogPrintf(
"CDB::Rewrite: Can't create database file %s\n", strFileRes);
548 if (ret1 == DB_NOTFOUND) {
551 }
else if (ret1 != 0) {
557 strncmp(ssKey.
data(), pszSkip, std::min(ssKey.
size(), strlen(pszSkip))) == 0)
559 if (strncmp(ssKey.
data(),
"\x07version", 8) == 0) {
562 ssValue << CLIENT_VERSION;
564 Dbt datKey(ssKey.
data(), ssKey.
size());
565 Dbt datValue(ssValue.
data(), ssValue.
size());
566 int ret2 = pdbCopy->put(
nullptr, &datKey, &datValue, DB_NOOVERWRITE);
573 if (pdbCopy->close(0))
581 Db dbA(env->
dbenv, 0);
582 if (dbA.remove(strFile.c_str(),
nullptr, 0))
584 Db dbB(env->
dbenv, 0);
585 if (dbB.rename(strFileRes.c_str(),
nullptr, strFile.c_str(), 0))
589 LogPrintf(
"CDB::Rewrite: Failed to rewrite database file %s\n", strFileRes);
602 LogPrint(
BCLog::DB,
"CDBEnv::Flush: Flush(%s)%s\n", fShutdown ?
"true" :
"false", fDbEnvInit ?
"" :
" database not started");
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) {
616 dbenv->txn_checkpoint(0, 0, 0);
619 dbenv->lsn_reset(strFile.c_str(), 0);
621 mapFileUseCount.erase(mi++);
625 LogPrint(
BCLog::DB,
"CDBEnv::Flush: Flush(%s)%s took %15dms\n", fShutdown ?
"true" :
"false", fDbEnvInit ?
"" :
" database not started",
GetTimeMillis() - nStart);
628 if (mapFileUseCount.empty()) {
629 dbenv->log_archive(&listp, DB_ARCH_REMOVE);
632 fs::remove_all(fs::path(strPath) /
"database");
651 std::map<std::string, int>::iterator mit = env->
mapFileUseCount.begin();
654 nRefCount += (*mit).second;
660 boost::this_thread::interruption_point();
661 std::map<std::string, int>::iterator mi = env->
mapFileUseCount.find(strFile);
704 fs::path pathDest(strDest);
705 if (fs::is_directory(pathDest))
709 fs::copy_file(pathSrc, pathDest, fs::copy_option::overwrite_if_exists);
712 }
catch (
const fs::filesystem_error& e) {
713 LogPrintf(
"error copying %s to %s - %s\n",
strFile, pathDest.string(), e.what());
725 env->
Flush(shutdown);
std::map< std::string, int > mapFileUseCount
void IncrementUpdateCounter()
DbTxn * TxnBegin(int flags=DB_TXN_WRITE_NOSYNC)
FILE * fopen(const fs::path &p, const char *mode)
void MilliSleep(int64_t n)
#define TRY_LOCK(cs, name)
std::string HexStr(const T itbegin, const T itend, bool fSpaces=false)
void Flush(bool fShutdown)
static bool VerifyDatabaseFile(const std::string &walletFile, const fs::path &dataDir, std::string &warningStr, std::string &errorStr, CDBEnv::recoverFunc_type recoverFunc)
bool Exists(const K &key)
Double ended buffer combining vector and stream-like interfaces.
bool GetBoolArg(const std::string &strArg, bool fDefault) const
Return boolean argument or default value.
bool Salvage(const std::string &strFile, bool fAggressive, std::vector< KeyValPair > &vResult)
bool IsDummy()
Return whether this database handle is a dummy for testing.
void Flush(bool shutdown)
Make sure all changes are flushed to disk.
VerifyResult
Verify that database file strFile is OK.
An instance of this class represents one database.
static bool VerifyEnvironment(const std::string &walletFile, const fs::path &dataDir, std::string &errorStr)
std::map< std::string, Db * > mapDb
bool TryCreateDirectories(const fs::path &p)
Ignores exceptions thrown by Boost's create_directories if the requested directory exists...
RAII class that provides access to a Berkeley database.
CDB(CWalletDBWrapper &dbw, const char *pszMode="r+", bool fFlushOnCloseIn=true)
static bool Recover(const std::string &filename, void *callbackDataIn, bool(*recoverKVcallback)(void *callbackData, CDataStream ssKey, CDataStream ssValue), std::string &out_backup_filename)
bool(* recoverFunc_type)(const std::string &strFile, std::string &out_backup_filename)
#define LogPrint(category,...)
bool Backup(const std::string &strDest)
Back up the entire database to a file.
static bool Rewrite(CWalletDBWrapper &dbw, const char *pszSkip=nullptr)
void CloseDb(const std::string &strFile)
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
bool WriteVersion(int nVersion)
int ReadAtCursor(Dbc *pcursor, CDataStream &ssKey, CDataStream &ssValue, bool setRange=false)
bool Open(const fs::path &path)
bool error(const char *fmt, const Args &... args)
bool Rewrite(const char *pszSkip=nullptr)
Rewrite the entire database on disk, with the exception of key pszSkip if non-zero.
void CheckpointLSN(const std::string &strFile)
const fs::path & GetDataDir(bool fNetSpecific)
int64_t GetTime()
GetTimeMicros() and GetTimeMillis() both return the system time, but in different units...
std::pair< std::vector< unsigned char >, std::vector< unsigned char > > KeyValPair
Salvage data from a file that Verify says is bad.
static bool PeriodicFlush(CWalletDBWrapper &dbw)
CDBEnv * env
BerkeleyDB specific.
VerifyResult Verify(const std::string &strFile, recoverFunc_type recoverFunc, std::string &out_backup_filename)
std::string _(const char *psz)
Translation function: Call Translate signal on UI interface, which returns a boost::optional result...
std::vector< unsigned char > ParseHex(const char *psz)