// Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_DB_H #define BITCOIN_DB_H #include "clientversion.h" #include "serialize.h" #include "streams.h" #include "sync.h" #include "version.h" #include #include #include #include #include class CDiskBlockIndex; class COutPoint; struct CBlockLocator; extern unsigned int nWalletDBUpdated; void ThreadFlushWalletDB(const std::string& strWalletFile); class CDBEnv { private: bool fDbEnvInit; bool fMockDb; // Don't change into boost::filesystem::path, as that can result in // shutdown problems/crashes caused by a static initialized internal pointer. std::string strPath; void EnvShutdown(); public: mutable CCriticalSection cs_db; DbEnv dbenv; std::map mapFileUseCount; std::map mapDb; CDBEnv(); ~CDBEnv(); void MakeMock(); bool IsMock() { return fMockDb; } /** * Verify that database file strFile is OK. If it is not, * call the callback to try to recover. * This must be called BEFORE strFile is opened. * Returns true if strFile is OK. */ enum VerifyResult { VERIFY_OK, RECOVER_OK, RECOVER_FAIL }; VerifyResult Verify(std::string strFile, bool (*recoverFunc)(CDBEnv& dbenv, std::string strFile)); /** * Salvage data from a file that Verify says is bad. * fAggressive sets the DB_AGGRESSIVE flag (see berkeley DB->verify() method documentation). * Appends binary key/value pairs to vResult, returns true if successful. * NOTE: reads the entire database into memory, so cannot be used * for huge databases. */ typedef std::pair, std::vector > KeyValPair; bool Salvage(std::string strFile, bool fAggressive, std::vector& vResult); bool Open(const boost::filesystem::path& path); void Close(); void Flush(bool fShutdown); void CheckpointLSN(const std::string& strFile); void CloseDb(const std::string& strFile); bool RemoveDb(const std::string& strFile); DbTxn* TxnBegin(int flags = DB_TXN_WRITE_NOSYNC) { DbTxn* ptxn = NULL; int ret = dbenv.txn_begin(NULL, &ptxn, flags); if (!ptxn || ret != 0) return NULL; return ptxn; } }; extern CDBEnv bitdb; /** RAII class that provides access to a Berkeley database */ class CDB { protected: Db* pdb; std::string strFile; DbTxn* activeTxn; bool fReadOnly; explicit CDB(const std::string& strFilename, const char* pszMode = "r+"); ~CDB() { Close(); } public: void Flush(); void Close(); private: CDB(const CDB&); void operator=(const CDB&); protected: template bool Read(const K& key, T& value) { if (!pdb) return false; // Key CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(1000); ssKey << key; Dbt datKey(&ssKey[0], ssKey.size()); // Read Dbt datValue; datValue.set_flags(DB_DBT_MALLOC); int ret = pdb->get(activeTxn, &datKey, &datValue, 0); memset(datKey.get_data(), 0, datKey.get_size()); if (datValue.get_data() == NULL) return false; // Unserialize value try { CDataStream ssValue((char*)datValue.get_data(), (char*)datValue.get_data() + datValue.get_size(), SER_DISK, CLIENT_VERSION); ssValue >> value; } catch (const std::exception&) { return false; } // Clear and free memory memset(datValue.get_data(), 0, datValue.get_size()); free(datValue.get_data()); return (ret == 0); } template bool Write(const K& key, const T& value, bool fOverwrite = true) { if (!pdb) return false; if (fReadOnly) assert(!"Write called on database in read-only mode"); // Key CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(1000); ssKey << key; Dbt datKey(&ssKey[0], ssKey.size()); // Value CDataStream ssValue(SER_DISK, CLIENT_VERSION); ssValue.reserve(10000); ssValue << value; Dbt datValue(&ssValue[0], ssValue.size()); // Write int ret = pdb->put(activeTxn, &datKey, &datValue, (fOverwrite ? 0 : DB_NOOVERWRITE)); // Clear memory in case it was a private key memset(datKey.get_data(), 0, datKey.get_size()); memset(datValue.get_data(), 0, datValue.get_size()); return (ret == 0); } template bool Erase(const K& key) { if (!pdb) return false; if (fReadOnly) assert(!"Erase called on database in read-only mode"); // Key CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(1000); ssKey << key; Dbt datKey(&ssKey[0], ssKey.size()); // Erase int ret = pdb->del(activeTxn, &datKey, 0); // Clear memory memset(datKey.get_data(), 0, datKey.get_size()); return (ret == 0 || ret == DB_NOTFOUND); } template bool Exists(const K& key) { if (!pdb) return false; // Key CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(1000); ssKey << key; Dbt datKey(&ssKey[0], ssKey.size()); // Exists int ret = pdb->exists(activeTxn, &datKey, 0); // Clear memory memset(datKey.get_data(), 0, datKey.get_size()); return (ret == 0); } Dbc* GetCursor() { if (!pdb) return NULL; Dbc* pcursor = NULL; int ret = pdb->cursor(NULL, &pcursor, 0); if (ret != 0) return NULL; return pcursor; } int ReadAtCursor(Dbc* pcursor, CDataStream& ssKey, CDataStream& ssValue, unsigned int fFlags = DB_NEXT) { // Read at cursor Dbt datKey; if (fFlags == DB_SET || fFlags == DB_SET_RANGE || fFlags == DB_GET_BOTH || fFlags == DB_GET_BOTH_RANGE) { datKey.set_data(&ssKey[0]); datKey.set_size(ssKey.size()); } Dbt datValue; if (fFlags == DB_GET_BOTH || fFlags == DB_GET_BOTH_RANGE) { datValue.set_data(&ssValue[0]); datValue.set_size(ssValue.size()); } datKey.set_flags(DB_DBT_MALLOC); datValue.set_flags(DB_DBT_MALLOC); int ret = pcursor->get(&datKey, &datValue, fFlags); if (ret != 0) return ret; else if (datKey.get_data() == NULL || datValue.get_data() == NULL) return 99999; // Convert to streams ssKey.SetType(SER_DISK); ssKey.clear(); ssKey.write((char*)datKey.get_data(), datKey.get_size()); ssValue.SetType(SER_DISK); ssValue.clear(); ssValue.write((char*)datValue.get_data(), datValue.get_size()); // Clear and free memory memset(datKey.get_data(), 0, datKey.get_size()); memset(datValue.get_data(), 0, datValue.get_size()); free(datKey.get_data()); free(datValue.get_data()); return 0; } public: bool TxnBegin() { if (!pdb || activeTxn) return false; DbTxn* ptxn = bitdb.TxnBegin(); if (!ptxn) return false; activeTxn = ptxn; return true; } bool TxnCommit() { if (!pdb || !activeTxn) return false; int ret = activeTxn->commit(0); activeTxn = NULL; return (ret == 0); } bool TxnAbort() { if (!pdb || !activeTxn) return false; int ret = activeTxn->abort(); activeTxn = NULL; return (ret == 0); } bool ReadVersion(int& nVersion) { nVersion = 0; return Read(std::string("version"), nVersion); } bool WriteVersion(int nVersion) { return Write(std::string("version"), nVersion); } bool static Rewrite(const std::string& strFile, const char* pszSkip = NULL); }; #endif // BITCOIN_DB_H