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