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