LCOV - code coverage report
Current view: top level - src/wallet - db.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 200 255 78.4 %
Date: 2015-10-12 22:39:14 Functions: 16 19 84.2 %
Legend: Lines: hit not hit

          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 : }

Generated by: LCOV version 1.11