1 // Copyright (c) 2009-2010 Satoshi Nakamoto 2 // Copyright (c) 2009-2020 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 #ifndef BITCOIN_WALLET_BDB_H 7 #define BITCOIN_WALLET_BDB_H 8 9 #include <clientversion.h> 10 #include <fs.h> 11 #include <serialize.h> 12 #include <streams.h> 13 #include <util/system.h> 14 #include <wallet/db.h> 15 16 #include <atomic> 17 #include <map> 18 #include <memory> 19 #include <string> 20 #include <unordered_map> 21 #include <vector> 22 23 #if defined(__GNUC__) && !defined(__clang__) 24 #pragma GCC diagnostic push 25 #pragma GCC diagnostic ignored "-Wsuggest-override" 26 #endif 27 #include <db_cxx.h> 28 #if defined(__GNUC__) && !defined(__clang__) 29 #pragma GCC diagnostic pop 30 #endif 31 32 struct bilingual_str; 33 34 static const unsigned int DEFAULT_WALLET_DBLOGSIZE = 100; 35 static const bool DEFAULT_WALLET_PRIVDB = true; 36 37 struct WalletDatabaseFileId { 38 u_int8_t value[DB_FILE_ID_LEN]; 39 bool operator==(const WalletDatabaseFileId& rhs) const; 40 }; 41 42 class BerkeleyDatabase; 43 44 class BerkeleyEnvironment 45 { 46 private: 47 bool fDbEnvInit; 48 bool fMockDb; 49 // Don't change into fs::path, as that can result in 50 // shutdown problems/crashes caused by a static initialized internal pointer. 51 std::string strPath; 52 53 public: 54 std::unique_ptr<DbEnv> dbenv; 55 std::map<std::string, std::reference_wrapper<BerkeleyDatabase>> m_databases; 56 std::unordered_map<std::string, WalletDatabaseFileId> m_fileids; 57 std::condition_variable_any m_db_in_use; 58 59 explicit BerkeleyEnvironment(const fs::path& env_directory); 60 BerkeleyEnvironment(); 61 ~BerkeleyEnvironment(); 62 void Reset(); 63 IsMock()64 bool IsMock() const { return fMockDb; } IsInitialized()65 bool IsInitialized() const { return fDbEnvInit; } Directory()66 fs::path Directory() const { return strPath; } 67 68 bool Open(bilingual_str& error); 69 void Close(); 70 void Flush(bool fShutdown); 71 void CheckpointLSN(const std::string& strFile); 72 73 void CloseDb(const std::string& strFile); 74 void ReloadDbEnv(); 75 76 DbTxn* TxnBegin(int flags = DB_TXN_WRITE_NOSYNC) 77 { 78 DbTxn* ptxn = nullptr; 79 int ret = dbenv->txn_begin(nullptr, &ptxn, flags); 80 if (!ptxn || ret != 0) 81 return nullptr; 82 return ptxn; 83 } 84 }; 85 86 /** Get BerkeleyEnvironment given a directory path. */ 87 std::shared_ptr<BerkeleyEnvironment> GetBerkeleyEnv(const fs::path& env_directory); 88 89 class BerkeleyBatch; 90 91 /** An instance of this class represents one database. 92 * For BerkeleyDB this is just a (env, strFile) tuple. 93 **/ 94 class BerkeleyDatabase : public WalletDatabase 95 { 96 public: 97 BerkeleyDatabase() = delete; 98 99 /** Create DB handle to real database */ BerkeleyDatabase(std::shared_ptr<BerkeleyEnvironment> env,std::string filename)100 BerkeleyDatabase(std::shared_ptr<BerkeleyEnvironment> env, std::string filename) : 101 WalletDatabase(), env(std::move(env)), strFile(std::move(filename)) 102 { 103 auto inserted = this->env->m_databases.emplace(strFile, std::ref(*this)); 104 assert(inserted.second); 105 } 106 107 ~BerkeleyDatabase() override; 108 109 /** Open the database if it is not already opened. */ 110 void Open() override; 111 112 /** Rewrite the entire database on disk, with the exception of key pszSkip if non-zero 113 */ 114 bool Rewrite(const char* pszSkip=nullptr) override; 115 116 /** Indicate the a new database user has began using the database. */ 117 void AddRef() override; 118 /** Indicate that database user has stopped using the database and that it could be flushed or closed. */ 119 void RemoveRef() override; 120 121 /** Back up the entire database to a file. 122 */ 123 bool Backup(const std::string& strDest) const override; 124 125 /** Make sure all changes are flushed to database file. 126 */ 127 void Flush() override; 128 /** Flush to the database file and close the database. 129 * Also close the environment if no other databases are open in it. 130 */ 131 void Close() override; 132 /* flush the wallet passively (TRY_LOCK) 133 ideal to be called periodically */ 134 bool PeriodicFlush() override; 135 136 void IncrementUpdateCounter() override; 137 138 void ReloadDbEnv() override; 139 140 /** Verifies the environment and database file */ 141 bool Verify(bilingual_str& error); 142 143 /** Return path to main database filename */ Filename()144 std::string Filename() override { return (env->Directory() / strFile).string(); } 145 Format()146 std::string Format() override { return "bdb"; } 147 /** 148 * Pointer to shared database environment. 149 * 150 * Normally there is only one BerkeleyDatabase object per 151 * BerkeleyEnvivonment, but in the special, backwards compatible case where 152 * multiple wallet BDB data files are loaded from the same directory, this 153 * will point to a shared instance that gets freed when the last data file 154 * is closed. 155 */ 156 std::shared_ptr<BerkeleyEnvironment> env; 157 158 /** Database pointer. This is initialized lazily and reset during flushes, so it can be null. */ 159 std::unique_ptr<Db> m_db; 160 161 std::string strFile; 162 163 /** Make a BerkeleyBatch connected to this database */ 164 std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) override; 165 }; 166 167 /** RAII class that provides access to a Berkeley database */ 168 class BerkeleyBatch : public DatabaseBatch 169 { 170 /** RAII class that automatically cleanses its data on destruction */ 171 class SafeDbt final 172 { 173 Dbt m_dbt; 174 175 public: 176 // construct Dbt with internally-managed data 177 SafeDbt(); 178 // construct Dbt with provided data 179 SafeDbt(void* data, size_t size); 180 ~SafeDbt(); 181 182 // delegate to Dbt 183 const void* get_data() const; 184 u_int32_t get_size() const; 185 186 // conversion operator to access the underlying Dbt 187 operator Dbt*(); 188 }; 189 190 private: 191 bool ReadKey(CDataStream&& key, CDataStream& value) override; 192 bool WriteKey(CDataStream&& key, CDataStream&& value, bool overwrite = true) override; 193 bool EraseKey(CDataStream&& key) override; 194 bool HasKey(CDataStream&& key) override; 195 196 protected: 197 Db* pdb; 198 std::string strFile; 199 DbTxn* activeTxn; 200 Dbc* m_cursor; 201 bool fReadOnly; 202 bool fFlushOnClose; 203 BerkeleyEnvironment *env; 204 BerkeleyDatabase& m_database; 205 206 public: 207 explicit BerkeleyBatch(BerkeleyDatabase& database, const bool fReadOnly, bool fFlushOnCloseIn=true); 208 ~BerkeleyBatch() override; 209 210 BerkeleyBatch(const BerkeleyBatch&) = delete; 211 BerkeleyBatch& operator=(const BerkeleyBatch&) = delete; 212 213 void Flush() override; 214 void Close() override; 215 216 bool StartCursor() override; 217 bool ReadAtCursor(CDataStream& ssKey, CDataStream& ssValue, bool& complete) override; 218 void CloseCursor() override; 219 bool TxnBegin() override; 220 bool TxnCommit() override; 221 bool TxnAbort() override; 222 }; 223 224 std::string BerkeleyDatabaseVersion(); 225 226 /** Perform sanity check of runtime BDB version versus linked BDB version. 227 */ 228 bool BerkeleyDatabaseSanityCheck(); 229 230 //! Return object giving access to Berkeley database at specified path. 231 std::unique_ptr<BerkeleyDatabase> MakeBerkeleyDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error); 232 233 #endif // BITCOIN_WALLET_BDB_H 234