Line data Source code
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.
5 :
6 : #include "db.h"
7 :
8 : #include "addrman.h"
9 : #include "hash.h"
10 : #include "protocol.h"
11 : #include "util.h"
12 : #include "utilstrencodings.h"
13 :
14 : #include <stdint.h>
15 :
16 : #ifndef WIN32
17 : #include <sys/stat.h>
18 : #endif
19 :
20 : #include <boost/filesystem.hpp>
21 : #include <boost/thread.hpp>
22 : #include <boost/version.hpp>
23 :
24 : using namespace std;
25 :
26 :
27 : unsigned int nWalletDBUpdated;
28 :
29 :
30 : //
31 : // CDB
32 : //
33 :
34 96 : CDBEnv bitdb;
35 :
36 215 : void CDBEnv::EnvShutdown()
37 : {
38 215 : if (!fDbEnvInit)
39 96 : return;
40 :
41 119 : fDbEnvInit = false;
42 119 : int ret = dbenv->close(0);
43 119 : if (ret != 0)
44 0 : LogPrintf("CDBEnv::EnvShutdown: Error %d shutting down database environment: %s\n", ret, DbEnv::strerror(ret));
45 119 : if (!fMockDb)
46 188 : DbEnv(0).remove(strPath.c_str(), 0);
47 : }
48 :
49 121 : void CDBEnv::Reset()
50 : {
51 121 : delete dbenv;
52 121 : dbenv = new DbEnv(DB_CXX_NO_EXCEPTIONS);
53 121 : fDbEnvInit = false;
54 121 : fMockDb = false;
55 121 : }
56 :
57 384 : CDBEnv::CDBEnv() : dbenv(NULL)
58 : {
59 96 : Reset();
60 96 : }
61 :
62 480 : CDBEnv::~CDBEnv()
63 : {
64 96 : EnvShutdown();
65 96 : delete dbenv;
66 96 : dbenv = NULL;
67 96 : }
68 :
69 0 : void CDBEnv::Close()
70 : {
71 119 : EnvShutdown();
72 0 : }
73 :
74 9980 : bool CDBEnv::Open(const boost::filesystem::path& pathIn)
75 : {
76 9980 : if (fDbEnvInit)
77 : return true;
78 :
79 94 : boost::this_thread::interruption_point();
80 :
81 94 : strPath = pathIn.string();
82 188 : boost::filesystem::path pathLogDir = pathIn / "database";
83 94 : TryCreateDirectory(pathLogDir);
84 188 : boost::filesystem::path pathErrorFile = pathIn / "db.log";
85 94 : LogPrintf("CDBEnv::Open: LogDir=%s ErrorFile=%s\n", pathLogDir.string(), pathErrorFile.string());
86 :
87 94 : unsigned int nEnvFlags = 0;
88 282 : if (GetBoolArg("-privdb", true))
89 94 : nEnvFlags |= DB_PRIVATE;
90 :
91 188 : dbenv->set_lg_dir(pathLogDir.string().c_str());
92 94 : dbenv->set_cachesize(0, 0x100000, 1); // 1 MiB should be enough for just the wallet
93 94 : dbenv->set_lg_bsize(0x10000);
94 94 : dbenv->set_lg_max(1048576);
95 94 : dbenv->set_lk_max_locks(40000);
96 94 : dbenv->set_lk_max_objects(40000);
97 188 : dbenv->set_errfile(fopen(pathErrorFile.string().c_str(), "a")); /// debug
98 94 : dbenv->set_flags(DB_AUTO_COMMIT, 1);
99 94 : dbenv->set_flags(DB_TXN_WRITE_NOSYNC, 1);
100 94 : dbenv->log_set_config(DB_LOG_AUTO_REMOVE, 1);
101 : int ret = dbenv->open(strPath.c_str(),
102 : DB_CREATE |
103 : DB_INIT_LOCK |
104 : DB_INIT_LOG |
105 : DB_INIT_MPOOL |
106 : DB_INIT_TXN |
107 : DB_THREAD |
108 : DB_RECOVER |
109 : nEnvFlags,
110 188 : S_IRUSR | S_IWUSR);
111 94 : if (ret != 0)
112 0 : return error("CDBEnv::Open: Error %d opening database environment: %s\n", ret, DbEnv::strerror(ret));
113 :
114 94 : fDbEnvInit = true;
115 94 : fMockDb = false;
116 94 : return true;
117 : }
118 :
119 25 : void CDBEnv::MakeMock()
120 : {
121 25 : if (fDbEnvInit)
122 0 : throw runtime_error("CDBEnv::MakeMock: Already initialized");
123 :
124 25 : boost::this_thread::interruption_point();
125 :
126 25 : LogPrint("db", "CDBEnv::MakeMock\n");
127 :
128 25 : dbenv->set_cachesize(1, 0, 1);
129 25 : dbenv->set_lg_bsize(10485760 * 4);
130 25 : dbenv->set_lg_max(10485760);
131 25 : dbenv->set_lk_max_locks(10000);
132 25 : dbenv->set_lk_max_objects(10000);
133 25 : dbenv->set_flags(DB_AUTO_COMMIT, 1);
134 25 : dbenv->log_set_config(DB_LOG_IN_MEMORY, 1);
135 : int ret = dbenv->open(NULL,
136 : DB_CREATE |
137 : DB_INIT_LOCK |
138 : DB_INIT_LOG |
139 : DB_INIT_MPOOL |
140 : DB_INIT_TXN |
141 : DB_THREAD |
142 : DB_PRIVATE,
143 25 : S_IRUSR | S_IWUSR);
144 25 : if (ret > 0)
145 0 : throw runtime_error(strprintf("CDBEnv::MakeMock: Error %d opening database environment.", ret));
146 :
147 25 : fDbEnvInit = true;
148 25 : fMockDb = true;
149 25 : }
150 :
151 57 : CDBEnv::VerifyResult CDBEnv::Verify(const std::string& strFile, bool (*recoverFunc)(CDBEnv& dbenv, const std::string& strFile))
152 : {
153 57 : LOCK(cs_db);
154 114 : assert(mapFileUseCount.count(strFile) == 0);
155 :
156 114 : Db db(dbenv, 0);
157 57 : int result = db.verify(strFile.c_str(), NULL, NULL, 0);
158 57 : if (result == 0)
159 : return VERIFY_OK;
160 0 : else if (recoverFunc == NULL)
161 : return RECOVER_FAIL;
162 :
163 : // Try to recover:
164 0 : bool fRecovered = (*recoverFunc)(*this, strFile);
165 0 : return (fRecovered ? RECOVER_OK : RECOVER_FAIL);
166 : }
167 :
168 0 : bool CDBEnv::Salvage(const std::string& strFile, bool fAggressive, std::vector<CDBEnv::KeyValPair>& vResult)
169 : {
170 0 : LOCK(cs_db);
171 0 : assert(mapFileUseCount.count(strFile) == 0);
172 :
173 0 : u_int32_t flags = DB_SALVAGE;
174 0 : if (fAggressive)
175 0 : flags |= DB_AGGRESSIVE;
176 :
177 0 : stringstream strDump;
178 :
179 0 : Db db(dbenv, 0);
180 0 : int result = db.verify(strFile.c_str(), NULL, &strDump, flags);
181 0 : if (result == DB_VERIFY_BAD) {
182 0 : LogPrintf("CDBEnv::Salvage: Database salvage found errors, all data may not be recoverable.\n");
183 0 : if (!fAggressive) {
184 0 : LogPrintf("CDBEnv::Salvage: Rerun with aggressive mode to ignore errors and continue.\n");
185 : return false;
186 : }
187 : }
188 0 : if (result != 0 && result != DB_VERIFY_BAD) {
189 0 : LogPrintf("CDBEnv::Salvage: Database salvage failed with result %d.\n", result);
190 : return false;
191 : }
192 :
193 : // Format of bdb dump is ascii lines:
194 : // header lines...
195 : // HEADER=END
196 : // hexadecimal key
197 : // hexadecimal value
198 : // ... repeated
199 : // DATA=END
200 :
201 : string strLine;
202 0 : while (!strDump.eof() && strLine != "HEADER=END")
203 0 : getline(strDump, strLine); // Skip past header
204 :
205 : std::string keyHex, valueHex;
206 0 : while (!strDump.eof() && keyHex != "DATA=END") {
207 0 : getline(strDump, keyHex);
208 0 : if (keyHex != "DATA_END") {
209 0 : getline(strDump, valueHex);
210 0 : vResult.push_back(make_pair(ParseHex(keyHex), ParseHex(valueHex)));
211 : }
212 : }
213 :
214 0 : return (result == 0);
215 : }
216 :
217 :
218 40 : void CDBEnv::CheckpointLSN(const std::string& strFile)
219 : {
220 40 : dbenv->txn_checkpoint(0, 0, 0);
221 40 : if (fMockDb)
222 40 : return;
223 80 : dbenv->lsn_reset(strFile.c_str(), 0);
224 : }
225 :
226 :
227 19772 : CDB::CDB(const std::string& strFilename, const char* pszMode, bool fFlushOnCloseIn) : pdb(NULL), activeTxn(NULL)
228 : {
229 : int ret;
230 9887 : fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w'));
231 9886 : fFlushOnClose = fFlushOnCloseIn;
232 9886 : if (strFilename.empty())
233 0 : return;
234 :
235 9886 : bool fCreate = strchr(pszMode, 'c') != NULL;
236 9886 : unsigned int nFlags = DB_THREAD;
237 9886 : if (fCreate)
238 120 : nFlags |= DB_CREATE;
239 :
240 : {
241 9886 : LOCK(bitdb.cs_db);
242 9886 : if (!bitdb.Open(GetDataDir()))
243 0 : throw runtime_error("CDB: Failed to open database environment.");
244 :
245 9886 : strFile = strFilename;
246 9886 : ++bitdb.mapFileUseCount[strFile];
247 9886 : pdb = bitdb.mapDb[strFile];
248 9886 : if (pdb == NULL) {
249 144 : pdb = new Db(bitdb.dbenv, 0);
250 :
251 144 : bool fMockDb = bitdb.IsMock();
252 144 : if (fMockDb) {
253 25 : DbMpoolFile* mpf = pdb->get_mpf();
254 25 : ret = mpf->set_flags(DB_MPOOL_NOFILE, 1);
255 25 : if (ret != 0)
256 0 : throw runtime_error(strprintf("CDB: Failed to configure for no temp file backing for database %s", strFile));
257 : }
258 :
259 : ret = pdb->open(NULL, // Txn pointer
260 119 : fMockDb ? NULL : strFile.c_str(), // Filename
261 25 : fMockDb ? strFile.c_str() : "main", // Logical db name
262 : DB_BTREE, // Database type
263 : nFlags, // Flags
264 432 : 0);
265 :
266 144 : if (ret != 0) {
267 0 : delete pdb;
268 0 : pdb = NULL;
269 0 : --bitdb.mapFileUseCount[strFile];
270 0 : strFile = "";
271 0 : throw runtime_error(strprintf("CDB: Error %d, can't open database %s", ret, strFile));
272 : }
273 :
274 382 : if (fCreate && !Exists(string("version"))) {
275 62 : bool fTmp = fReadOnly;
276 62 : fReadOnly = false;
277 62 : WriteVersion(CLIENT_VERSION);
278 62 : fReadOnly = fTmp;
279 : }
280 :
281 144 : bitdb.mapDb[strFile] = pdb;
282 : }
283 : }
284 : }
285 :
286 5895 : void CDB::Flush()
287 : {
288 5895 : if (activeTxn)
289 5895 : return;
290 :
291 : // Flush database activity from memory pool to disk log
292 5895 : unsigned int nMinutes = 0;
293 5895 : if (fReadOnly)
294 1 : nMinutes = 1;
295 :
296 5897 : bitdb.dbenv->txn_checkpoint(nMinutes ? GetArg("-dblogsize", 100) * 1024 : 0, nMinutes, 0);
297 : }
298 :
299 9887 : void CDB::Close()
300 : {
301 9887 : if (!pdb)
302 9887 : return;
303 9886 : if (activeTxn)
304 0 : activeTxn->abort();
305 9886 : activeTxn = NULL;
306 9886 : pdb = NULL;
307 :
308 9886 : if (fFlushOnClose)
309 5895 : Flush();
310 :
311 : {
312 9886 : LOCK(bitdb.cs_db);
313 9886 : --bitdb.mapFileUseCount[strFile];
314 : }
315 : }
316 :
317 145 : void CDBEnv::CloseDb(const string& strFile)
318 : {
319 : {
320 145 : LOCK(cs_db);
321 145 : if (mapDb[strFile] != NULL) {
322 : // Close the database handle
323 144 : Db* pdb = mapDb[strFile];
324 144 : pdb->close(0);
325 144 : delete pdb;
326 144 : mapDb[strFile] = NULL;
327 : }
328 : }
329 145 : }
330 :
331 0 : bool CDBEnv::RemoveDb(const string& strFile)
332 : {
333 0 : this->CloseDb(strFile);
334 :
335 0 : LOCK(cs_db);
336 0 : int rc = dbenv->dbremove(NULL, strFile.c_str(), NULL, DB_AUTO_COMMIT);
337 0 : return (rc == 0);
338 : }
339 :
340 1 : bool CDB::Rewrite(const string& strFile, const char* pszSkip)
341 : {
342 : while (true) {
343 : {
344 1 : LOCK(bitdb.cs_db);
345 1 : if (!bitdb.mapFileUseCount.count(strFile) || bitdb.mapFileUseCount[strFile] == 0) {
346 : // Flush log data to the dat file
347 1 : bitdb.CloseDb(strFile);
348 1 : bitdb.CheckpointLSN(strFile);
349 : bitdb.mapFileUseCount.erase(strFile);
350 :
351 1 : bool fSuccess = true;
352 1 : LogPrintf("CDB::Rewrite: Rewriting %s...\n", strFile);
353 1 : string strFileRes = strFile + ".rewrite";
354 : { // surround usage of db with extra {}
355 4 : CDB db(strFile.c_str(), "r");
356 1 : Db* pdbCopy = new Db(bitdb.dbenv, 0);
357 :
358 : int ret = pdbCopy->open(NULL, // Txn pointer
359 : strFileRes.c_str(), // Filename
360 : "main", // Logical db name
361 : DB_BTREE, // Database type
362 : DB_CREATE, // Flags
363 2 : 0);
364 1 : if (ret > 0) {
365 0 : LogPrintf("CDB::Rewrite: Can't create database file %s\n", strFileRes);
366 : fSuccess = false;
367 : }
368 :
369 1 : Dbc* pcursor = db.GetCursor();
370 1 : if (pcursor)
371 102 : while (fSuccess) {
372 : CDataStream ssKey(SER_DISK, CLIENT_VERSION);
373 : CDataStream ssValue(SER_DISK, CLIENT_VERSION);
374 102 : int ret = db.ReadAtCursor(pcursor, ssKey, ssValue, DB_NEXT);
375 102 : if (ret == DB_NOTFOUND) {
376 1 : pcursor->close();
377 : break;
378 101 : } else if (ret != 0) {
379 0 : pcursor->close();
380 : fSuccess = false;
381 : break;
382 : }
383 303 : if (pszSkip &&
384 101 : strncmp(&ssKey[0], pszSkip, std::min(ssKey.size(), strlen(pszSkip))) == 0)
385 : continue;
386 101 : if (strncmp(&ssKey[0], "\x07version", 8) == 0) {
387 : // Update version:
388 : ssValue.clear();
389 1 : ssValue << CLIENT_VERSION;
390 : }
391 303 : Dbt datKey(&ssKey[0], ssKey.size());
392 303 : Dbt datValue(&ssValue[0], ssValue.size());
393 101 : int ret2 = pdbCopy->put(NULL, &datKey, &datValue, DB_NOOVERWRITE);
394 101 : if (ret2 > 0)
395 0 : fSuccess = false;
396 : }
397 1 : if (fSuccess) {
398 1 : db.Close();
399 1 : bitdb.CloseDb(strFile);
400 1 : if (pdbCopy->close(0))
401 0 : fSuccess = false;
402 1 : delete pdbCopy;
403 1 : }
404 : }
405 1 : if (fSuccess) {
406 1 : Db dbA(bitdb.dbenv, 0);
407 1 : if (dbA.remove(strFile.c_str(), NULL, 0))
408 0 : fSuccess = false;
409 2 : Db dbB(bitdb.dbenv, 0);
410 2 : if (dbB.rename(strFileRes.c_str(), NULL, strFile.c_str(), 0))
411 1 : fSuccess = false;
412 : }
413 1 : if (!fSuccess)
414 0 : LogPrintf("CDB::Rewrite: Failed to rewrite database file %s\n", strFileRes);
415 1 : return fSuccess;
416 : }
417 : }
418 0 : MilliSleep(100);
419 : }
420 0 : return false;
421 : }
422 :
423 :
424 213 : void CDBEnv::Flush(bool fShutdown)
425 : {
426 213 : int64_t nStart = GetTimeMillis();
427 : // Flush log data to the actual data file on all files that are not in use
428 213 : LogPrint("db", "CDBEnv::Flush: Flush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started");
429 213 : if (!fDbEnvInit)
430 213 : return;
431 : {
432 213 : LOCK(cs_db);
433 426 : map<string, int>::iterator mi = mapFileUseCount.begin();
434 634 : while (mi != mapFileUseCount.end()) {
435 104 : string strFile = (*mi).first;
436 104 : int nRefCount = (*mi).second;
437 104 : LogPrint("db", "CDBEnv::Flush: Flushing %s (refcount = %d)...\n", strFile, nRefCount);
438 104 : if (nRefCount == 0) {
439 : // Move log data to the dat file
440 104 : CloseDb(strFile);
441 104 : LogPrint("db", "CDBEnv::Flush: %s checkpoint\n", strFile);
442 104 : dbenv->txn_checkpoint(0, 0, 0);
443 104 : LogPrint("db", "CDBEnv::Flush: %s detach\n", strFile);
444 104 : if (!fMockDb)
445 158 : dbenv->lsn_reset(strFile.c_str(), 0);
446 104 : LogPrint("db", "CDBEnv::Flush: %s closed\n", strFile);
447 208 : mapFileUseCount.erase(mi++);
448 : } else
449 0 : mi++;
450 : }
451 213 : LogPrint("db", "CDBEnv::Flush: Flush(%s)%s took %15dms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started", GetTimeMillis() - nStart);
452 213 : if (fShutdown) {
453 : char** listp;
454 238 : if (mapFileUseCount.empty()) {
455 119 : dbenv->log_archive(&listp, DB_ARCH_REMOVE);
456 : Close();
457 119 : if (!fMockDb)
458 470 : boost::filesystem::remove_all(boost::filesystem::path(strPath) / "database");
459 : }
460 : }
461 : }
462 288 : }
|