1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2014 The Bitcoin developers
3 // Distributed under the MIT/X11 software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
12 #include "utilstrencodings.h"
20 #include <boost/filesystem.hpp>
21 #include <boost/thread.hpp>
22 #include <boost/version.hpp>
23 #include <openssl/rand.h>
26 using namespace boost;
29 unsigned int nWalletDBUpdated;
39 void CDBEnv::EnvShutdown()
45 int ret = dbenv.close(0);
47 LogPrintf("CDBEnv::EnvShutdown : Error %d shutting down database environment: %s\n", ret, DbEnv::strerror(ret));
49 DbEnv(0).remove(path.string().c_str(), 0);
52 CDBEnv::CDBEnv() : dbenv(DB_CXX_NO_EXCEPTIONS)
68 bool CDBEnv::Open(const boost::filesystem::path& pathIn)
73 boost::this_thread::interruption_point();
76 filesystem::path pathLogDir = path / "database";
77 TryCreateDirectory(pathLogDir);
78 filesystem::path pathErrorFile = path / "db.log";
79 LogPrintf("CDBEnv::Open : LogDir=%s ErrorFile=%s\n", pathLogDir.string(), pathErrorFile.string());
81 unsigned int nEnvFlags = 0;
82 if (GetBoolArg("-privdb", true))
83 nEnvFlags |= DB_PRIVATE;
85 dbenv.set_lg_dir(pathLogDir.string().c_str());
86 dbenv.set_cachesize(0, 0x100000, 1); // 1 MiB should be enough for just the wallet
87 dbenv.set_lg_bsize(0x10000);
88 dbenv.set_lg_max(1048576);
89 dbenv.set_lk_max_locks(40000);
90 dbenv.set_lk_max_objects(40000);
91 dbenv.set_errfile(fopen(pathErrorFile.string().c_str(), "a")); /// debug
92 dbenv.set_flags(DB_AUTO_COMMIT, 1);
93 dbenv.set_flags(DB_TXN_WRITE_NOSYNC, 1);
94 dbenv.log_set_config(DB_LOG_AUTO_REMOVE, 1);
95 int ret = dbenv.open(path.string().c_str(),
106 return error("CDBEnv::Open : Error %d opening database environment: %s\n", ret, DbEnv::strerror(ret));
113 void CDBEnv::MakeMock()
116 throw runtime_error("CDBEnv::MakeMock : Already initialized");
118 boost::this_thread::interruption_point();
120 LogPrint("db", "CDBEnv::MakeMock\n");
122 dbenv.set_cachesize(1, 0, 1);
123 dbenv.set_lg_bsize(10485760*4);
124 dbenv.set_lg_max(10485760);
125 dbenv.set_lk_max_locks(10000);
126 dbenv.set_lk_max_objects(10000);
127 dbenv.set_flags(DB_AUTO_COMMIT, 1);
128 dbenv.log_set_config(DB_LOG_IN_MEMORY, 1);
129 int ret = dbenv.open(NULL,
139 throw runtime_error(strprintf("CDBEnv::MakeMock : Error %d opening database environment.", ret));
145 CDBEnv::VerifyResult CDBEnv::Verify(std::string strFile, bool (*recoverFunc)(CDBEnv& dbenv, std::string strFile))
148 assert(mapFileUseCount.count(strFile) == 0);
151 int result = db.verify(strFile.c_str(), NULL, NULL, 0);
154 else if (recoverFunc == NULL)
158 bool fRecovered = (*recoverFunc)(*this, strFile);
159 return (fRecovered ? RECOVER_OK : RECOVER_FAIL);
162 bool CDBEnv::Salvage(std::string strFile, bool fAggressive,
163 std::vector<CDBEnv::KeyValPair >& vResult)
166 assert(mapFileUseCount.count(strFile) == 0);
168 u_int32_t flags = DB_SALVAGE;
169 if (fAggressive) flags |= DB_AGGRESSIVE;
171 stringstream strDump;
174 int result = db.verify(strFile.c_str(), NULL, &strDump, flags);
175 if (result == DB_VERIFY_BAD)
177 LogPrintf("CDBEnv::Salvage : Database salvage found errors, all data may not be recoverable.\n");
180 LogPrintf("CDBEnv::Salvage : Rerun with aggressive mode to ignore errors and continue.\n");
184 if (result != 0 && result != DB_VERIFY_BAD)
186 LogPrintf("CDBEnv::Salvage : Database salvage failed with result %d.\n", result);
190 // Format of bdb dump is ascii lines:
199 while (!strDump.eof() && strLine != "HEADER=END")
200 getline(strDump, strLine); // Skip past header
202 std::string keyHex, valueHex;
203 while (!strDump.eof() && keyHex != "DATA=END")
205 getline(strDump, keyHex);
206 if (keyHex != "DATA_END")
208 getline(strDump, valueHex);
209 vResult.push_back(make_pair(ParseHex(keyHex),ParseHex(valueHex)));
213 return (result == 0);
217 void CDBEnv::CheckpointLSN(std::string strFile)
219 dbenv.txn_checkpoint(0, 0, 0);
222 dbenv.lsn_reset(strFile.c_str(), 0);
226 CDB::CDB(const char *pszFile, const char* pszMode) :
227 pdb(NULL), activeTxn(NULL)
230 fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w'));
234 bool fCreate = strchr(pszMode, 'c');
235 unsigned int nFlags = DB_THREAD;
241 if (!bitdb.Open(GetDataDir()))
242 throw runtime_error("CDB : Failed to open database environment.");
245 ++bitdb.mapFileUseCount[strFile];
246 pdb = bitdb.mapDb[strFile];
249 pdb = new Db(&bitdb.dbenv, 0);
251 bool fMockDb = bitdb.IsMock();
254 DbMpoolFile*mpf = pdb->get_mpf();
255 ret = mpf->set_flags(DB_MPOOL_NOFILE, 1);
257 throw runtime_error(strprintf("CDB : Failed to configure for no temp file backing for database %s", pszFile));
260 ret = pdb->open(NULL, // Txn pointer
261 fMockDb ? NULL : pszFile, // Filename
262 fMockDb ? pszFile : "main", // Logical db name
263 DB_BTREE, // Database type
271 --bitdb.mapFileUseCount[strFile];
273 throw runtime_error(strprintf("CDB : Error %d, can't open database %s", ret, pszFile));
276 if (fCreate && !Exists(string("version")))
278 bool fTmp = fReadOnly;
280 WriteVersion(CLIENT_VERSION);
284 bitdb.mapDb[strFile] = pdb;
294 // Flush database activity from memory pool to disk log
295 unsigned int nMinutes = 0;
299 bitdb.dbenv.txn_checkpoint(nMinutes ? GetArg("-dblogsize", 100)*1024 : 0, nMinutes, 0);
315 --bitdb.mapFileUseCount[strFile];
319 void CDBEnv::CloseDb(const string& strFile)
323 if (mapDb[strFile] != NULL)
325 // Close the database handle
326 Db* pdb = mapDb[strFile];
329 mapDb[strFile] = NULL;
334 bool CDBEnv::RemoveDb(const string& strFile)
336 this->CloseDb(strFile);
339 int rc = dbenv.dbremove(NULL, strFile.c_str(), NULL, DB_AUTO_COMMIT);
343 bool CDB::Rewrite(const string& strFile, const char* pszSkip)
349 if (!bitdb.mapFileUseCount.count(strFile) || bitdb.mapFileUseCount[strFile] == 0)
351 // Flush log data to the dat file
352 bitdb.CloseDb(strFile);
353 bitdb.CheckpointLSN(strFile);
354 bitdb.mapFileUseCount.erase(strFile);
356 bool fSuccess = true;
357 LogPrintf("CDB::Rewrite : Rewriting %s...\n", strFile);
358 string strFileRes = strFile + ".rewrite";
359 { // surround usage of db with extra {}
360 CDB db(strFile.c_str(), "r");
361 Db* pdbCopy = new Db(&bitdb.dbenv, 0);
363 int ret = pdbCopy->open(NULL, // Txn pointer
364 strFileRes.c_str(), // Filename
365 "main", // Logical db name
366 DB_BTREE, // Database type
371 LogPrintf("CDB::Rewrite : Can't create database file %s\n", strFileRes);
375 Dbc* pcursor = db.GetCursor();
379 CDataStream ssKey(SER_DISK, CLIENT_VERSION);
380 CDataStream ssValue(SER_DISK, CLIENT_VERSION);
381 int ret = db.ReadAtCursor(pcursor, ssKey, ssValue, DB_NEXT);
382 if (ret == DB_NOTFOUND)
394 strncmp(&ssKey[0], pszSkip, std::min(ssKey.size(), strlen(pszSkip))) == 0)
396 if (strncmp(&ssKey[0], "\x07version", 8) == 0)
400 ssValue << CLIENT_VERSION;
402 Dbt datKey(&ssKey[0], ssKey.size());
403 Dbt datValue(&ssValue[0], ssValue.size());
404 int ret2 = pdbCopy->put(NULL, &datKey, &datValue, DB_NOOVERWRITE);
411 bitdb.CloseDb(strFile);
412 if (pdbCopy->close(0))
419 Db dbA(&bitdb.dbenv, 0);
420 if (dbA.remove(strFile.c_str(), NULL, 0))
422 Db dbB(&bitdb.dbenv, 0);
423 if (dbB.rename(strFileRes.c_str(), NULL, strFile.c_str(), 0))
427 LogPrintf("CDB::Rewrite : Failed to rewrite database file %s\n", strFileRes);
437 void CDBEnv::Flush(bool fShutdown)
439 int64_t nStart = GetTimeMillis();
440 // Flush log data to the actual data file on all files that are not in use
441 LogPrint("db", "CDBEnv::Flush : Flush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started");
446 map<string, int>::iterator mi = mapFileUseCount.begin();
447 while (mi != mapFileUseCount.end())
449 string strFile = (*mi).first;
450 int nRefCount = (*mi).second;
451 LogPrint("db", "CDBEnv::Flush : Flushing %s (refcount = %d)...\n", strFile, nRefCount);
454 // Move log data to the dat file
456 LogPrint("db", "CDBEnv::Flush : %s checkpoint\n", strFile);
457 dbenv.txn_checkpoint(0, 0, 0);
458 LogPrint("db", "CDBEnv::Flush : %s detach\n", strFile);
460 dbenv.lsn_reset(strFile.c_str(), 0);
461 LogPrint("db", "CDBEnv::Flush : %s closed\n", strFile);
462 mapFileUseCount.erase(mi++);
467 LogPrint("db", "CDBEnv::Flush : Flush(%s)%s took %15dms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started", GetTimeMillis() - nStart);
471 if (mapFileUseCount.empty())
473 dbenv.log_archive(&listp, DB_ARCH_REMOVE);
476 boost::filesystem::remove_all(path / "database");