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