1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2014 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.
12 #include "utilstrencodings.h"
20 #include <boost/filesystem.hpp>
21 #include <boost/thread.hpp>
22 #include <boost/version.hpp>
27 unsigned int nWalletDBUpdated
;
36 void CDBEnv::EnvShutdown()
42 int ret
= dbenv
->close(0);
44 LogPrintf("CDBEnv::EnvShutdown: Error %d shutting down database environment: %s\n", ret
, DbEnv::strerror(ret
));
46 DbEnv(0).remove(strPath
.c_str(), 0);
52 dbenv
= new DbEnv(DB_CXX_NO_EXCEPTIONS
);
57 CDBEnv::CDBEnv() : dbenv(NULL
)
74 bool CDBEnv::Open(const boost::filesystem::path
& pathIn
)
79 boost::this_thread::interruption_point();
81 strPath
= pathIn
.string();
82 boost::filesystem::path pathLogDir
= pathIn
/ "database";
83 TryCreateDirectory(pathLogDir
);
84 boost::filesystem::path pathErrorFile
= pathIn
/ "db.log";
85 LogPrintf("CDBEnv::Open: LogDir=%s ErrorFile=%s\n", pathLogDir
.string(), pathErrorFile
.string());
87 unsigned int nEnvFlags
= 0;
88 if (GetBoolArg("-privdb", true))
89 nEnvFlags
|= DB_PRIVATE
;
91 dbenv
->set_lg_dir(pathLogDir
.string().c_str());
92 dbenv
->set_cachesize(0, 0x100000, 1); // 1 MiB should be enough for just the wallet
93 dbenv
->set_lg_bsize(0x10000);
94 dbenv
->set_lg_max(1048576);
95 dbenv
->set_lk_max_locks(40000);
96 dbenv
->set_lk_max_objects(40000);
97 dbenv
->set_errfile(fopen(pathErrorFile
.string().c_str(), "a")); /// debug
98 dbenv
->set_flags(DB_AUTO_COMMIT
, 1);
99 dbenv
->set_flags(DB_TXN_WRITE_NOSYNC
, 1);
100 dbenv
->log_set_config(DB_LOG_AUTO_REMOVE
, 1);
101 int ret
= dbenv
->open(strPath
.c_str(),
112 return error("CDBEnv::Open: Error %d opening database environment: %s\n", ret
, DbEnv::strerror(ret
));
119 void CDBEnv::MakeMock()
122 throw runtime_error("CDBEnv::MakeMock: Already initialized");
124 boost::this_thread::interruption_point();
126 LogPrint("db", "CDBEnv::MakeMock\n");
128 dbenv
->set_cachesize(1, 0, 1);
129 dbenv
->set_lg_bsize(10485760 * 4);
130 dbenv
->set_lg_max(10485760);
131 dbenv
->set_lk_max_locks(10000);
132 dbenv
->set_lk_max_objects(10000);
133 dbenv
->set_flags(DB_AUTO_COMMIT
, 1);
134 dbenv
->log_set_config(DB_LOG_IN_MEMORY
, 1);
135 int ret
= dbenv
->open(NULL
,
145 throw runtime_error(strprintf("CDBEnv::MakeMock: Error %d opening database environment.", ret
));
151 CDBEnv::VerifyResult
CDBEnv::Verify(const std::string
& strFile
, bool (*recoverFunc
)(CDBEnv
& dbenv
, const std::string
& strFile
))
154 assert(mapFileUseCount
.count(strFile
) == 0);
157 int result
= db
.verify(strFile
.c_str(), NULL
, NULL
, 0);
160 else if (recoverFunc
== NULL
)
164 bool fRecovered
= (*recoverFunc
)(*this, strFile
);
165 return (fRecovered
? RECOVER_OK
: RECOVER_FAIL
);
168 bool CDBEnv::Salvage(const std::string
& strFile
, bool fAggressive
, std::vector
<CDBEnv::KeyValPair
>& vResult
)
171 assert(mapFileUseCount
.count(strFile
) == 0);
173 u_int32_t flags
= DB_SALVAGE
;
175 flags
|= DB_AGGRESSIVE
;
177 stringstream strDump
;
180 int result
= db
.verify(strFile
.c_str(), NULL
, &strDump
, flags
);
181 if (result
== DB_VERIFY_BAD
) {
182 LogPrintf("CDBEnv::Salvage: Database salvage found errors, all data may not be recoverable.\n");
184 LogPrintf("CDBEnv::Salvage: Rerun with aggressive mode to ignore errors and continue.\n");
188 if (result
!= 0 && result
!= DB_VERIFY_BAD
) {
189 LogPrintf("CDBEnv::Salvage: Database salvage failed with result %d.\n", result
);
193 // Format of bdb dump is ascii lines:
202 while (!strDump
.eof() && strLine
!= "HEADER=END")
203 getline(strDump
, strLine
); // Skip past header
205 std::string keyHex
, valueHex
;
206 while (!strDump
.eof() && keyHex
!= "DATA=END") {
207 getline(strDump
, keyHex
);
208 if (keyHex
!= "DATA_END") {
209 getline(strDump
, valueHex
);
210 vResult
.push_back(make_pair(ParseHex(keyHex
), ParseHex(valueHex
)));
214 return (result
== 0);
218 void CDBEnv::CheckpointLSN(const std::string
& strFile
)
220 dbenv
->txn_checkpoint(0, 0, 0);
223 dbenv
->lsn_reset(strFile
.c_str(), 0);
227 CDB::CDB(const std::string
& strFilename
, const char* pszMode
, bool fFlushOnCloseIn
) : pdb(NULL
), activeTxn(NULL
)
230 fReadOnly
= (!strchr(pszMode
, '+') && !strchr(pszMode
, 'w'));
231 fFlushOnClose
= fFlushOnCloseIn
;
232 if (strFilename
.empty())
235 bool fCreate
= strchr(pszMode
, 'c') != NULL
;
236 unsigned int nFlags
= DB_THREAD
;
242 if (!bitdb
.Open(GetDataDir()))
243 throw runtime_error("CDB: Failed to open database environment.");
245 strFile
= strFilename
;
246 ++bitdb
.mapFileUseCount
[strFile
];
247 pdb
= bitdb
.mapDb
[strFile
];
249 pdb
= new Db(bitdb
.dbenv
, 0);
251 bool fMockDb
= bitdb
.IsMock();
253 DbMpoolFile
* mpf
= pdb
->get_mpf();
254 ret
= mpf
->set_flags(DB_MPOOL_NOFILE
, 1);
256 throw runtime_error(strprintf("CDB: Failed to configure for no temp file backing for database %s", strFile
));
259 ret
= pdb
->open(NULL
, // Txn pointer
260 fMockDb
? NULL
: strFile
.c_str(), // Filename
261 fMockDb
? strFile
.c_str() : "main", // Logical db name
262 DB_BTREE
, // Database type
269 --bitdb
.mapFileUseCount
[strFile
];
271 throw runtime_error(strprintf("CDB: Error %d, can't open database %s", ret
, strFile
));
274 if (fCreate
&& !Exists(string("version"))) {
275 bool fTmp
= fReadOnly
;
277 WriteVersion(CLIENT_VERSION
);
281 bitdb
.mapDb
[strFile
] = pdb
;
291 // Flush database activity from memory pool to disk log
292 unsigned int nMinutes
= 0;
296 bitdb
.dbenv
->txn_checkpoint(nMinutes
? GetArg("-dblogsize", DEFAULT_WALLET_DBLOGSIZE
) * 1024 : 0, nMinutes
, 0);
313 --bitdb
.mapFileUseCount
[strFile
];
317 void CDBEnv::CloseDb(const string
& strFile
)
321 if (mapDb
[strFile
] != NULL
) {
322 // Close the database handle
323 Db
* pdb
= mapDb
[strFile
];
326 mapDb
[strFile
] = NULL
;
331 bool CDBEnv::RemoveDb(const string
& strFile
)
333 this->CloseDb(strFile
);
336 int rc
= dbenv
->dbremove(NULL
, strFile
.c_str(), NULL
, DB_AUTO_COMMIT
);
340 bool CDB::Rewrite(const string
& strFile
, const char* pszSkip
)
345 if (!bitdb
.mapFileUseCount
.count(strFile
) || bitdb
.mapFileUseCount
[strFile
] == 0) {
346 // Flush log data to the dat file
347 bitdb
.CloseDb(strFile
);
348 bitdb
.CheckpointLSN(strFile
);
349 bitdb
.mapFileUseCount
.erase(strFile
);
351 bool fSuccess
= true;
352 LogPrintf("CDB::Rewrite: Rewriting %s...\n", strFile
);
353 string strFileRes
= strFile
+ ".rewrite";
354 { // surround usage of db with extra {}
355 CDB
db(strFile
.c_str(), "r");
356 Db
* pdbCopy
= new Db(bitdb
.dbenv
, 0);
358 int ret
= pdbCopy
->open(NULL
, // Txn pointer
359 strFileRes
.c_str(), // Filename
360 "main", // Logical db name
361 DB_BTREE
, // Database type
365 LogPrintf("CDB::Rewrite: Can't create database file %s\n", strFileRes
);
369 Dbc
* pcursor
= db
.GetCursor();
372 CDataStream
ssKey(SER_DISK
, CLIENT_VERSION
);
373 CDataStream
ssValue(SER_DISK
, CLIENT_VERSION
);
374 int ret
= db
.ReadAtCursor(pcursor
, ssKey
, ssValue
, DB_NEXT
);
375 if (ret
== DB_NOTFOUND
) {
378 } else if (ret
!= 0) {
384 strncmp(&ssKey
[0], pszSkip
, std::min(ssKey
.size(), strlen(pszSkip
))) == 0)
386 if (strncmp(&ssKey
[0], "\x07version", 8) == 0) {
389 ssValue
<< CLIENT_VERSION
;
391 Dbt
datKey(&ssKey
[0], ssKey
.size());
392 Dbt
datValue(&ssValue
[0], ssValue
.size());
393 int ret2
= pdbCopy
->put(NULL
, &datKey
, &datValue
, DB_NOOVERWRITE
);
399 bitdb
.CloseDb(strFile
);
400 if (pdbCopy
->close(0))
406 Db
dbA(bitdb
.dbenv
, 0);
407 if (dbA
.remove(strFile
.c_str(), NULL
, 0))
409 Db
dbB(bitdb
.dbenv
, 0);
410 if (dbB
.rename(strFileRes
.c_str(), NULL
, strFile
.c_str(), 0))
414 LogPrintf("CDB::Rewrite: Failed to rewrite database file %s\n", strFileRes
);
424 void CDBEnv::Flush(bool fShutdown
)
426 int64_t nStart
= GetTimeMillis();
427 // Flush log data to the actual data file on all files that are not in use
428 LogPrint("db", "CDBEnv::Flush: Flush(%s)%s\n", fShutdown
? "true" : "false", fDbEnvInit
? "" : " database not started");
433 map
<string
, int>::iterator mi
= mapFileUseCount
.begin();
434 while (mi
!= mapFileUseCount
.end()) {
435 string strFile
= (*mi
).first
;
436 int nRefCount
= (*mi
).second
;
437 LogPrint("db", "CDBEnv::Flush: Flushing %s (refcount = %d)...\n", strFile
, nRefCount
);
438 if (nRefCount
== 0) {
439 // Move log data to the dat file
441 LogPrint("db", "CDBEnv::Flush: %s checkpoint\n", strFile
);
442 dbenv
->txn_checkpoint(0, 0, 0);
443 LogPrint("db", "CDBEnv::Flush: %s detach\n", strFile
);
445 dbenv
->lsn_reset(strFile
.c_str(), 0);
446 LogPrint("db", "CDBEnv::Flush: %s closed\n", strFile
);
447 mapFileUseCount
.erase(mi
++);
451 LogPrint("db", "CDBEnv::Flush: Flush(%s)%s took %15dms\n", fShutdown
? "true" : "false", fDbEnvInit
? "" : " database not started", GetTimeMillis() - nStart
);
454 if (mapFileUseCount
.empty()) {
455 dbenv
->log_archive(&listp
, DB_ARCH_REMOVE
);
458 boost::filesystem::remove_all(boost::filesystem::path(strPath
) / "database");