Feathercoin  0.5.0
P2P Digital Currency
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
db.cpp
Go to the documentation of this file.
1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2012 The Bitcoin developers
3 // Copyright (c) 2013 Feathercoin 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 #include "util.h"
9 #include "main.h"
10 #include <boost/filesystem.hpp>
11 #include <boost/filesystem/fstream.hpp>
12 
13 #ifndef WIN32
14 #include "sys/stat.h"
15 #endif
16 
17 using namespace std;
18 using namespace boost;
19 
20 
21 unsigned int nWalletDBUpdated;
22 
23 
24 
25 //
26 // CDB
27 //
28 
30 
32 {
33  if (!fDbEnvInit)
34  return;
35 
36  fDbEnvInit = false;
37  int ret = dbenv.close(0);
38  if (ret != 0)
39  printf("EnvShutdown exception: %s (%d)\n", DbEnv::strerror(ret), ret);
40  if (!fMockDb)
41  DbEnv(0).remove(path.string().c_str(), 0);
42 }
43 
44 CDBEnv::CDBEnv() : dbenv(DB_CXX_NO_EXCEPTIONS)
45 {
46  fDbEnvInit = false;
47  fMockDb = false;
48 }
49 
51 {
52  EnvShutdown();
53 }
54 
56 {
57  EnvShutdown();
58 }
59 
60 bool CDBEnv::Open(const boost::filesystem::path& pathIn)
61 {
62  if (fDbEnvInit)
63  return true;
64 
65  boost::this_thread::interruption_point();
66 
67  path = pathIn;
68  filesystem::path pathLogDir = path / "database";
69  filesystem::create_directory(pathLogDir);
70  filesystem::path pathErrorFile = path / "db.log";
71  printf("dbenv.open LogDir=%s ErrorFile=%s\n", pathLogDir.string().c_str(), pathErrorFile.string().c_str());
72 
73  unsigned int nEnvFlags = 0;
74  if (GetBoolArg("-privdb", true))
75  nEnvFlags |= DB_PRIVATE;
76 
77  dbenv.set_lg_dir(pathLogDir.string().c_str());
78  dbenv.set_cachesize(0, 0x100000, 1); // 1 MiB should be enough for just the wallet
79  dbenv.set_lg_bsize(0x10000);
80  dbenv.set_lg_max(1048576);
81  dbenv.set_lk_max_locks(40000);
82  dbenv.set_lk_max_objects(40000);
83  dbenv.set_errfile(fopen(pathErrorFile.string().c_str(), "a"));
84  dbenv.set_flags(DB_AUTO_COMMIT, 1);
85  dbenv.set_flags(DB_TXN_WRITE_NOSYNC, 1);
86  dbenv.log_set_config(DB_LOG_AUTO_REMOVE, 1);
87  int ret = dbenv.open(path.string().c_str(),
88  DB_CREATE |
89  DB_INIT_LOCK |
90  DB_INIT_LOG |
91  DB_INIT_MPOOL |
92  DB_INIT_TXN |
93  DB_THREAD |
94  DB_RECOVER |
95  nEnvFlags,
96  S_IRUSR | S_IWUSR);
97  if (ret != 0)
98  return error("CDB() : error %s (%d) opening database environment", DbEnv::strerror(ret), ret);
99 
100  fDbEnvInit = true;
101  fMockDb = false;
102  return true;
103 }
104 
106 {
107  if (fDbEnvInit)
108  throw runtime_error("CDBEnv::MakeMock(): already initialized");
109 
110  boost::this_thread::interruption_point();
111 
112  printf("CDBEnv::MakeMock()\n");
113 
114  dbenv.set_cachesize(1, 0, 1);
115  dbenv.set_lg_bsize(10485760*4);
116  dbenv.set_lg_max(10485760);
117  dbenv.set_lk_max_locks(10000);
118  dbenv.set_lk_max_objects(10000);
119  dbenv.set_flags(DB_AUTO_COMMIT, 1);
120  dbenv.log_set_config(DB_LOG_IN_MEMORY, 1);
121  int ret = dbenv.open(NULL,
122  DB_CREATE |
123  DB_INIT_LOCK |
124  DB_INIT_LOG |
125  DB_INIT_MPOOL |
126  DB_INIT_TXN |
127  DB_THREAD |
128  DB_PRIVATE,
129  S_IRUSR | S_IWUSR);
130  if (ret > 0)
131  throw runtime_error(strprintf("CDBEnv::MakeMock(): error %d opening database environment", ret));
132 
133  fDbEnvInit = true;
134  fMockDb = true;
135 }
136 
137 CDBEnv::VerifyResult CDBEnv::Verify(std::string strFile, bool (*recoverFunc)(CDBEnv& dbenv, std::string strFile))
138 {
139  LOCK(cs_db);
140  assert(mapFileUseCount.count(strFile) == 0);
141 
142  Db db(&dbenv, 0);
143  int result = db.verify(strFile.c_str(), NULL, NULL, 0);
144  if (result == 0)
145  return VERIFY_OK;
146  else if (recoverFunc == NULL)
147  return RECOVER_FAIL;
148 
149  // Try to recover:
150  bool fRecovered = (*recoverFunc)(*this, strFile);
151  return (fRecovered ? RECOVER_OK : RECOVER_FAIL);
152 }
153 
154 bool CDBEnv::Salvage(std::string strFile, bool fAggressive,
155  std::vector<CDBEnv::KeyValPair >& vResult)
156 {
157  LOCK(cs_db);
158  assert(mapFileUseCount.count(strFile) == 0);
159 
160  u_int32_t flags = DB_SALVAGE;
161  if (fAggressive) flags |= DB_AGGRESSIVE;
162 
163  stringstream strDump;
164 
165  Db db(&dbenv, 0);
166  int result = db.verify(strFile.c_str(), NULL, &strDump, flags);
167  if (result == DB_VERIFY_BAD)
168  {
169  printf("Error: Salvage found errors, all data may not be recoverable.\n");
170  if (!fAggressive)
171  {
172  printf("Error: Rerun with aggressive mode to ignore errors and continue.\n");
173  return false;
174  }
175  }
176  if (result != 0 && result != DB_VERIFY_BAD)
177  {
178  printf("ERROR: db salvage failed: %d\n",result);
179  return false;
180  }
181 
182  // Format of bdb dump is ascii lines:
183  // header lines...
184  // HEADER=END
185  // hexadecimal key
186  // hexadecimal value
187  // ... repeated
188  // DATA=END
189 
190  string strLine;
191  while (!strDump.eof() && strLine != "HEADER=END")
192  getline(strDump, strLine); // Skip past header
193 
194  std::string keyHex, valueHex;
195  while (!strDump.eof() && keyHex != "DATA=END")
196  {
197  getline(strDump, keyHex);
198  if (keyHex != "DATA_END")
199  {
200  getline(strDump, valueHex);
201  vResult.push_back(make_pair(ParseHex(keyHex),ParseHex(valueHex)));
202  }
203  }
204 
205  return (result == 0);
206 }
207 
208 
209 void CDBEnv::CheckpointLSN(std::string strFile)
210 {
211  dbenv.txn_checkpoint(0, 0, 0);
212  if (fMockDb)
213  return;
214  dbenv.lsn_reset(strFile.c_str(), 0);
215 }
216 
217 
218 CDB::CDB(const char *pszFile, const char* pszMode) :
219  pdb(NULL), activeTxn(NULL)
220 {
221  int ret;
222  if (pszFile == NULL)
223  return;
224 
225  fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w'));
226  bool fCreate = strchr(pszMode, 'c');
227  unsigned int nFlags = DB_THREAD;
228  if (fCreate)
229  nFlags |= DB_CREATE;
230 
231  {
232  LOCK(bitdb.cs_db);
233  if (!bitdb.Open(GetDataDir()))
234  throw runtime_error("env open failed");
235 
236  strFile = pszFile;
237  ++bitdb.mapFileUseCount[strFile];
238  pdb = bitdb.mapDb[strFile];
239  if (pdb == NULL)
240  {
241  pdb = new Db(&bitdb.dbenv, 0);
242 
243  bool fMockDb = bitdb.IsMock();
244  if (fMockDb)
245  {
246  DbMpoolFile*mpf = pdb->get_mpf();
247  ret = mpf->set_flags(DB_MPOOL_NOFILE, 1);
248  if (ret != 0)
249  throw runtime_error(strprintf("CDB() : failed to configure for no temp file backing for database %s", pszFile));
250  }
251 
252  ret = pdb->open(NULL, // Txn pointer
253  fMockDb ? NULL : pszFile, // Filename
254  fMockDb ? pszFile : "main", // Logical db name
255  DB_BTREE, // Database type
256  nFlags, // Flags
257  0);
258 
259  if (ret != 0)
260  {
261  delete pdb;
262  pdb = NULL;
263  --bitdb.mapFileUseCount[strFile];
264  strFile = "";
265  throw runtime_error(strprintf("CDB() : can't open database file %s, error %d", pszFile, ret));
266  }
267 
268  if (fCreate && !Exists(string("version")))
269  {
270  bool fTmp = fReadOnly;
271  fReadOnly = false;
272  WriteVersion(CLIENT_VERSION);
273  fReadOnly = fTmp;
274  }
275 
276  bitdb.mapDb[strFile] = pdb;
277  }
278  }
279 }
280 
282 {
283  if (activeTxn)
284  return;
285 
286  // Flush database activity from memory pool to disk log
287  unsigned int nMinutes = 0;
288  if (fReadOnly)
289  nMinutes = 1;
290 
291  bitdb.dbenv.txn_checkpoint(nMinutes ? GetArg("-dblogsize", 100)*1024 : 0, nMinutes, 0);
292 }
293 
295 {
296  if (!pdb)
297  return;
298  if (activeTxn)
299  activeTxn->abort();
300  activeTxn = NULL;
301  pdb = NULL;
302 
303  Flush();
304 
305  {
306  LOCK(bitdb.cs_db);
307  --bitdb.mapFileUseCount[strFile];
308  }
309 }
310 
311 void CDBEnv::CloseDb(const string& strFile)
312 {
313  {
314  LOCK(cs_db);
315  if (mapDb[strFile] != NULL)
316  {
317  // Close the database handle
318  Db* pdb = mapDb[strFile];
319  pdb->close(0);
320  delete pdb;
321  mapDb[strFile] = NULL;
322  }
323  }
324 }
325 
326 bool CDBEnv::RemoveDb(const string& strFile)
327 {
328  this->CloseDb(strFile);
329 
330  LOCK(cs_db);
331  int rc = dbenv.dbremove(NULL, strFile.c_str(), NULL, DB_AUTO_COMMIT);
332  return (rc == 0);
333 }
334 
335 bool CDB::Rewrite(const string& strFile, const char* pszSkip)
336 {
337  while (true)
338  {
339  {
340  LOCK(bitdb.cs_db);
341  if (!bitdb.mapFileUseCount.count(strFile) || bitdb.mapFileUseCount[strFile] == 0)
342  {
343  // Flush log data to the dat file
344  bitdb.CloseDb(strFile);
345  bitdb.CheckpointLSN(strFile);
346  bitdb.mapFileUseCount.erase(strFile);
347 
348  bool fSuccess = true;
349  printf("Rewriting %s...\n", strFile.c_str());
350  string strFileRes = strFile + ".rewrite";
351  { // surround usage of db with extra {}
352  CDB db(strFile.c_str(), "r");
353  Db* pdbCopy = new Db(&bitdb.dbenv, 0);
354 
355  int ret = pdbCopy->open(NULL, // Txn pointer
356  strFileRes.c_str(), // Filename
357  "main", // Logical db name
358  DB_BTREE, // Database type
359  DB_CREATE, // Flags
360  0);
361  if (ret > 0)
362  {
363  printf("Cannot create database file %s\n", strFileRes.c_str());
364  fSuccess = false;
365  }
366 
367  Dbc* pcursor = db.GetCursor();
368  if (pcursor)
369  while (fSuccess)
370  {
371  CDataStream ssKey(SER_DISK, CLIENT_VERSION);
372  CDataStream ssValue(SER_DISK, CLIENT_VERSION);
373  int ret = db.ReadAtCursor(pcursor, ssKey, ssValue, DB_NEXT);
374  if (ret == DB_NOTFOUND)
375  {
376  pcursor->close();
377  break;
378  }
379  else if (ret != 0)
380  {
381  pcursor->close();
382  fSuccess = false;
383  break;
384  }
385  if (pszSkip &&
386  strncmp(&ssKey[0], pszSkip, std::min(ssKey.size(), strlen(pszSkip))) == 0)
387  continue;
388  if (strncmp(&ssKey[0], "\x07version", 8) == 0)
389  {
390  // Update version:
391  ssValue.clear();
392  ssValue << CLIENT_VERSION;
393  }
394  Dbt datKey(&ssKey[0], ssKey.size());
395  Dbt datValue(&ssValue[0], ssValue.size());
396  int ret2 = pdbCopy->put(NULL, &datKey, &datValue, DB_NOOVERWRITE);
397  if (ret2 > 0)
398  fSuccess = false;
399  }
400  if (fSuccess)
401  {
402  db.Close();
403  bitdb.CloseDb(strFile);
404  if (pdbCopy->close(0))
405  fSuccess = false;
406  delete pdbCopy;
407  }
408  }
409  if (fSuccess)
410  {
411  Db dbA(&bitdb.dbenv, 0);
412  if (dbA.remove(strFile.c_str(), NULL, 0))
413  fSuccess = false;
414  Db dbB(&bitdb.dbenv, 0);
415  if (dbB.rename(strFileRes.c_str(), NULL, strFile.c_str(), 0))
416  fSuccess = false;
417  }
418  if (!fSuccess)
419  printf("Rewriting of %s FAILED!\n", strFileRes.c_str());
420  return fSuccess;
421  }
422  }
423  MilliSleep(100);
424  }
425  return false;
426 }
427 
428 
429 void CDBEnv::Flush(bool fShutdown)
430 {
431  int64 nStart = GetTimeMillis();
432  // Flush log data to the actual data file
433  // on all files that are not in use
434  printf("Flush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started");
435  if (!fDbEnvInit)
436  return;
437  {
438  LOCK(cs_db);
439  map<string, int>::iterator mi = mapFileUseCount.begin();
440  while (mi != mapFileUseCount.end())
441  {
442  string strFile = (*mi).first;
443  int nRefCount = (*mi).second;
444  printf("%s refcount=%d\n", strFile.c_str(), nRefCount);
445  if (nRefCount == 0)
446  {
447  // Move log data to the dat file
448  CloseDb(strFile);
449  printf("%s checkpoint\n", strFile.c_str());
450  dbenv.txn_checkpoint(0, 0, 0);
451  printf("%s detach\n", strFile.c_str());
452  if (!fMockDb)
453  dbenv.lsn_reset(strFile.c_str(), 0);
454  printf("%s closed\n", strFile.c_str());
455  mapFileUseCount.erase(mi++);
456  }
457  else
458  mi++;
459  }
460  printf("DBFlush(%s)%s ended %15"PRI64d"ms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started", GetTimeMillis() - nStart);
461  if (fShutdown)
462  {
463  char** listp;
464  if (mapFileUseCount.empty())
465  {
466  dbenv.log_archive(&listp, DB_ARCH_REMOVE);
467  Close();
468  if (!fMockDb)
469  boost::filesystem::remove_all(path / "database");
470  }
471  }
472  }
473 }
474 
475 
476 
477 
478 
479 
480 
481 
482 
483 
484 
485 //
486 // CAddrDB
487 //
488 
489 
491 {
492  pathAddr = GetDataDir() / "peers.dat";
493 }
494 
495 bool CAddrDB::Write(const CAddrMan& addr)
496 {
497  // Generate random temporary filename
498  unsigned short randv = 0;
499  RAND_bytes((unsigned char *)&randv, sizeof(randv));
500  std::string tmpfn = strprintf("peers.dat.%04x", randv);
501 
502  // serialize addresses, checksum data up to that point, then append csum
503  CDataStream ssPeers(SER_DISK, CLIENT_VERSION);
504  ssPeers << FLATDATA(pchMessageStart);
505  ssPeers << addr;
506  uint256 hash = Hash(ssPeers.begin(), ssPeers.end());
507  ssPeers << hash;
508 
509  // open temp output file, and associate with CAutoFile
510  boost::filesystem::path pathTmp = GetDataDir() / tmpfn;
511  FILE *file = fopen(pathTmp.string().c_str(), "wb");
512  CAutoFile fileout = CAutoFile(file, SER_DISK, CLIENT_VERSION);
513  if (!fileout)
514  return error("CAddrman::Write() : open failed");
515 
516  // Write and commit header, data
517  try {
518  fileout << ssPeers;
519  }
520  catch (std::exception &e) {
521  return error("CAddrman::Write() : I/O error");
522  }
523  FileCommit(fileout);
524  fileout.fclose();
525 
526  // replace existing peers.dat, if any, with new peers.dat.XXXX
527  if (!RenameOver(pathTmp, pathAddr))
528  return error("CAddrman::Write() : Rename-into-place failed");
529 
530  return true;
531 }
532 
534 {
535  // open input file, and associate with CAutoFile
536  FILE *file = fopen(pathAddr.string().c_str(), "rb");
537  CAutoFile filein = CAutoFile(file, SER_DISK, CLIENT_VERSION);
538  if (!filein)
539  return error("CAddrman::Read() : open failed");
540 
541  // use file size to size memory buffer
542  int fileSize = GetFilesize(filein);
543  int dataSize = fileSize - sizeof(uint256);
544  //Don't try to resize to a negative number if file is small
545  if ( dataSize < 0 ) dataSize = 0;
546  vector<unsigned char> vchData;
547  vchData.resize(dataSize);
548  uint256 hashIn;
549 
550  // read data and checksum from file
551  try {
552  filein.read((char *)&vchData[0], dataSize);
553  filein >> hashIn;
554  }
555  catch (std::exception &e) {
556  return error("CAddrman::Read() 2 : I/O error or stream data corrupted");
557  }
558  filein.fclose();
559 
560  CDataStream ssPeers(vchData, SER_DISK, CLIENT_VERSION);
561 
562  // verify stored checksum matches input data
563  uint256 hashTmp = Hash(ssPeers.begin(), ssPeers.end());
564  if (hashIn != hashTmp)
565  return error("CAddrman::Read() : checksum mismatch; data corrupted");
566 
567  unsigned char pchMsgTmp[4];
568  try {
569  // de-serialize file header (pchMessageStart magic number) and
570  ssPeers >> FLATDATA(pchMsgTmp);
571 
572  // verify the network matches ours
573  if (memcmp(pchMsgTmp, pchMessageStart, sizeof(pchMsgTmp)))
574  return error("CAddrman::Read() : invalid network magic number");
575 
576  // de-serialize address data into one CAddrMan object
577  ssPeers >> addr;
578  }
579  catch (std::exception &e) {
580  return error("CAddrman::Read() : I/O error or stream data corrupted");
581  }
582 
583  return true;
584 }
585 
bool error(const char *format,...)
Definition: util.cpp:358
const boost::filesystem::path & GetDataDir(bool fNetSpecific)
Definition: util.cpp:1060
unsigned int nWalletDBUpdated
Definition: db.cpp:21
#define strprintf(format,...)
Definition: util.h:169
std::map< std::string, int > mapFileUseCount
Definition: db.h:43
#define PRI64d
Definition: util.h:51
DbTxn * activeTxn
Definition: db.h:96
void CheckpointLSN(std::string strFile)
Definition: db.cpp:209
const_iterator begin() const
Definition: serialize.h:884
Definition: util.cpp:28
void FileCommit(FILE *fileout)
Definition: util.cpp:1158
static bool Rewrite(const std::string &strFile, const char *pszSkip=NULL)
Definition: db.cpp:335
std::string strFile
Definition: db.h:95
CAddrDB()
Definition: db.cpp:490
void Close()
Definition: db.cpp:294
bool fReadOnly
Definition: db.h:97
void Flush(bool fShutdown)
Definition: db.cpp:429
void EnvShutdown()
Definition: db.cpp:31
bool Exists(const K &key)
Definition: db.h:196
Double ended buffer combining vector and stream-like interfaces.
Definition: serialize.h:799
bool RenameOver(boost::filesystem::path src, boost::filesystem::path dest)
Definition: util.cpp:1147
~CDBEnv()
Definition: db.cpp:50
#define FLATDATA(obj)
Definition: serialize.h:304
bool fDbEnvInit
Definition: db.h:34
boost::filesystem::path path
Definition: db.h:36
CDBEnv bitdb
Definition: db.cpp:29
Stochastical (IP) address manager.
Definition: addrman.h:165
int64 GetTimeMillis()
Definition: util.h:340
void MilliSleep(int64 n)
Definition: util.h:106
void MakeMock()
Definition: db.cpp:105
VerifyResult
Definition: db.h:57
bool GetBoolArg(const std::string &strArg, bool fDefault)
Return boolean argument or default value.
Definition: util.cpp:600
bool Write(const CAddrMan &addr)
Definition: db.cpp:495
std::map< std::string, Db * > mapDb
Definition: db.h:44
#define printf
Definition: rpcdump.cpp:12
#define LOCK(cs)
Definition: sync.h:108
size_type size() const
Definition: serialize.h:888
RAII class that provides access to a Berkeley database.
Definition: db.h:91
uint256 Hash(const T1 pbegin, const T1 pend)
Definition: hash.h:16
CCriticalSection cs_db
Definition: db.h:41
bool IsMock()
Definition: db.h:49
bool RemoveDb(const std::string &strFile)
Definition: db.cpp:326
void Close()
Definition: db.cpp:55
256-bit unsigned integer
Definition: uint256.h:537
int GetFilesize(FILE *file)
Definition: util.cpp:1174
bool Salvage(std::string strFile, bool fAggressive, std::vector< KeyValPair > &vResult)
Definition: db.cpp:154
boost::filesystem::path pathAddr
Definition: db.h:320
void CloseDb(const std::string &strFile)
Definition: db.cpp:311
bool WriteVersion(int nVersion)
Definition: db.h:301
Definition: db.h:31
CDB(const char *pszFile, const char *pszMode="r+")
Definition: db.cpp:218
bool fMockDb
Definition: db.h:35
VerifyResult Verify(std::string strFile, bool(*recoverFunc)(CDBEnv &dbenv, std::string strFile))
Definition: db.cpp:137
DbEnv dbenv
Definition: db.h:42
void clear()
Definition: serialize.h:894
std::string GetArg(const std::string &strArg, const std::string &strDefault)
Return string argument or default value.
Definition: util.cpp:586
Db * pdb
Definition: db.h:94
unsigned char pchMessageStart[4]
Definition: main.cpp:3178
CDBEnv()
Definition: db.cpp:44
bool Open(const boost::filesystem::path &path)
Definition: db.cpp:60
vector< unsigned char > ParseHex(const char *psz)
Definition: util.cpp:500
uint32_t hash
Definition: cache.cc:34
bool Read(CAddrMan &addr)
Definition: db.cpp:533
RAII wrapper for FILE*.
Definition: serialize.h:1108
void Flush()
Definition: db.cpp:281
const_iterator end() const
Definition: serialize.h:886
long long int64
Definition: serialize.h:25