Branch data Line data Source code
# 1 : : // Copyright (c) 2009-2010 Satoshi Nakamoto
# 2 : : // Copyright (c) 2009-2021 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 : : #include <fs.h>
# 7 : : #include <wallet/bdb.h>
# 8 : : #include <wallet/db.h>
# 9 : :
# 10 : : #include <util/strencodings.h>
# 11 : : #include <util/translation.h>
# 12 : :
# 13 : : #include <stdint.h>
# 14 : :
# 15 : : #ifndef WIN32
# 16 : : #include <sys/stat.h>
# 17 : : #endif
# 18 : :
# 19 : : namespace wallet {
# 20 : : namespace {
# 21 : :
# 22 : : //! Make sure database has a unique fileid within the environment. If it
# 23 : : //! doesn't, throw an error. BDB caches do not work properly when more than one
# 24 : : //! open database has the same fileid (values written to one database may show
# 25 : : //! up in reads to other databases).
# 26 : : //!
# 27 : : //! BerkeleyDB generates unique fileids by default
# 28 : : //! (https://docs.oracle.com/cd/E17275_01/html/programmer_reference/program_copy.html),
# 29 : : //! so bitcoin should never create different databases with the same fileid, but
# 30 : : //! this error can be triggered if users manually copy database files.
# 31 : : void CheckUniqueFileid(const BerkeleyEnvironment& env, const std::string& filename, Db& db, WalletDatabaseFileId& fileid)
# 32 : 1114 : {
# 33 [ - + ]: 1114 : if (env.IsMock()) return;
# 34 : :
# 35 : 1114 : int ret = db.get_mpf()->get_fileid(fileid.value);
# 36 [ - + ]: 1114 : if (ret != 0) {
# 37 : 0 : throw std::runtime_error(strprintf("BerkeleyDatabase: Can't open database %s (get_fileid failed with %d)", filename, ret));
# 38 : 0 : }
# 39 : :
# 40 [ + + ]: 1150 : for (const auto& item : env.m_fileids) {
# 41 [ + + ][ + + ]: 1150 : if (fileid == item.second && &fileid != &item.second) {
# 42 : 6 : throw std::runtime_error(strprintf("BerkeleyDatabase: Can't open database %s (duplicates fileid %s from %s)", filename,
# 43 : 6 : HexStr(item.second.value), item.first));
# 44 : 6 : }
# 45 : 1150 : }
# 46 : 1114 : }
# 47 : :
# 48 : : RecursiveMutex cs_db;
# 49 : : std::map<std::string, std::weak_ptr<BerkeleyEnvironment>> g_dbenvs GUARDED_BY(cs_db); //!< Map from directory name to db environment.
# 50 : : } // namespace
# 51 : :
# 52 : : bool WalletDatabaseFileId::operator==(const WalletDatabaseFileId& rhs) const
# 53 : 1150 : {
# 54 : 1150 : return memcmp(value, &rhs.value, sizeof(value)) == 0;
# 55 : 1150 : }
# 56 : :
# 57 : : /**
# 58 : : * @param[in] env_directory Path to environment directory
# 59 : : * @return A shared pointer to the BerkeleyEnvironment object for the wallet directory, never empty because ~BerkeleyEnvironment
# 60 : : * erases the weak pointer from the g_dbenvs map.
# 61 : : * @post A new BerkeleyEnvironment weak pointer is inserted into g_dbenvs if the directory path key was not already in the map.
# 62 : : */
# 63 : : std::shared_ptr<BerkeleyEnvironment> GetBerkeleyEnv(const fs::path& env_directory, bool use_shared_memory)
# 64 : 611 : {
# 65 : 611 : LOCK(cs_db);
# 66 : 611 : auto inserted = g_dbenvs.emplace(fs::PathToString(env_directory), std::weak_ptr<BerkeleyEnvironment>());
# 67 [ + + ]: 611 : if (inserted.second) {
# 68 : 583 : auto env = std::make_shared<BerkeleyEnvironment>(env_directory, use_shared_memory);
# 69 : 583 : inserted.first->second = env;
# 70 : 583 : return env;
# 71 : 583 : }
# 72 : 28 : return inserted.first->second.lock();
# 73 : 611 : }
# 74 : :
# 75 : : //
# 76 : : // BerkeleyBatch
# 77 : : //
# 78 : :
# 79 : : void BerkeleyEnvironment::Close()
# 80 : 979 : {
# 81 [ + + ]: 979 : if (!fDbEnvInit)
# 82 : 394 : return;
# 83 : :
# 84 : 585 : fDbEnvInit = false;
# 85 : :
# 86 [ + + ]: 585 : for (auto& db : m_databases) {
# 87 : 402 : BerkeleyDatabase& database = db.second.get();
# 88 : 402 : assert(database.m_refcount <= 0);
# 89 [ - + ]: 402 : if (database.m_db) {
# 90 : 0 : database.m_db->close(0);
# 91 : 0 : database.m_db.reset();
# 92 : 0 : }
# 93 : 402 : }
# 94 : :
# 95 : 585 : FILE* error_file = nullptr;
# 96 : 585 : dbenv->get_errfile(&error_file);
# 97 : :
# 98 : 585 : int ret = dbenv->close(0);
# 99 [ - + ]: 585 : if (ret != 0)
# 100 : 0 : LogPrintf("BerkeleyEnvironment::Close: Error %d closing database environment: %s\n", ret, DbEnv::strerror(ret));
# 101 [ + - ]: 585 : if (!fMockDb)
# 102 : 585 : DbEnv((u_int32_t)0).remove(strPath.c_str(), 0);
# 103 : :
# 104 [ + - ]: 585 : if (error_file) fclose(error_file);
# 105 : :
# 106 : 585 : UnlockDirectory(fs::PathFromString(strPath), ".walletlock");
# 107 : 585 : }
# 108 : :
# 109 : : void BerkeleyEnvironment::Reset()
# 110 : 601 : {
# 111 : 601 : dbenv.reset(new DbEnv(DB_CXX_NO_EXCEPTIONS));
# 112 : 601 : fDbEnvInit = false;
# 113 : 601 : fMockDb = false;
# 114 : 601 : }
# 115 : :
# 116 : : BerkeleyEnvironment::BerkeleyEnvironment(const fs::path& dir_path, bool use_shared_memory) : strPath(fs::PathToString(dir_path)), m_use_shared_memory(use_shared_memory)
# 117 : 583 : {
# 118 : 583 : Reset();
# 119 : 583 : }
# 120 : :
# 121 : : BerkeleyEnvironment::~BerkeleyEnvironment()
# 122 : 583 : {
# 123 : 583 : LOCK(cs_db);
# 124 : 583 : g_dbenvs.erase(strPath);
# 125 : 583 : Close();
# 126 : 583 : }
# 127 : :
# 128 : : bool BerkeleyEnvironment::Open(bilingual_str& err)
# 129 : 113537 : {
# 130 [ + + ]: 113537 : if (fDbEnvInit) {
# 131 : 112944 : return true;
# 132 : 112944 : }
# 133 : :
# 134 : 593 : fs::path pathIn = fs::PathFromString(strPath);
# 135 : 593 : TryCreateDirectories(pathIn);
# 136 [ + + ]: 593 : if (!LockDirectory(pathIn, ".walletlock")) {
# 137 : 6 : LogPrintf("Cannot obtain a lock on wallet directory %s. Another instance may be using it.\n", strPath);
# 138 : 6 : err = strprintf(_("Error initializing wallet database environment %s!"), fs::quoted(fs::PathToString(Directory())));
# 139 : 6 : return false;
# 140 : 6 : }
# 141 : :
# 142 : 587 : fs::path pathLogDir = pathIn / "database";
# 143 : 587 : TryCreateDirectories(pathLogDir);
# 144 : 587 : fs::path pathErrorFile = pathIn / "db.log";
# 145 : 587 : LogPrintf("BerkeleyEnvironment::Open: LogDir=%s ErrorFile=%s\n", fs::PathToString(pathLogDir), fs::PathToString(pathErrorFile));
# 146 : :
# 147 : 587 : unsigned int nEnvFlags = 0;
# 148 [ + + ]: 587 : if (!m_use_shared_memory) {
# 149 : 585 : nEnvFlags |= DB_PRIVATE;
# 150 : 585 : }
# 151 : :
# 152 : 587 : dbenv->set_lg_dir(fs::PathToString(pathLogDir).c_str());
# 153 : 587 : dbenv->set_cachesize(0, 0x100000, 1); // 1 MiB should be enough for just the wallet
# 154 : 587 : dbenv->set_lg_bsize(0x10000);
# 155 : 587 : dbenv->set_lg_max(1048576);
# 156 : 587 : dbenv->set_lk_max_locks(40000);
# 157 : 587 : dbenv->set_lk_max_objects(40000);
# 158 : 587 : dbenv->set_errfile(fsbridge::fopen(pathErrorFile, "a")); /// debug
# 159 : 587 : dbenv->set_flags(DB_AUTO_COMMIT, 1);
# 160 : 587 : dbenv->set_flags(DB_TXN_WRITE_NOSYNC, 1);
# 161 : 587 : dbenv->log_set_config(DB_LOG_AUTO_REMOVE, 1);
# 162 : 587 : int ret = dbenv->open(strPath.c_str(),
# 163 : 587 : DB_CREATE |
# 164 : 587 : DB_INIT_LOCK |
# 165 : 587 : DB_INIT_LOG |
# 166 : 587 : DB_INIT_MPOOL |
# 167 : 587 : DB_INIT_TXN |
# 168 : 587 : DB_THREAD |
# 169 : 587 : DB_RECOVER |
# 170 : 587 : nEnvFlags,
# 171 : 587 : S_IRUSR | S_IWUSR);
# 172 [ - + ]: 587 : if (ret != 0) {
# 173 : 0 : LogPrintf("BerkeleyEnvironment::Open: Error %d opening database environment: %s\n", ret, DbEnv::strerror(ret));
# 174 : 0 : int ret2 = dbenv->close(0);
# 175 [ # # ]: 0 : if (ret2 != 0) {
# 176 : 0 : LogPrintf("BerkeleyEnvironment::Open: Error %d closing failed database environment: %s\n", ret2, DbEnv::strerror(ret2));
# 177 : 0 : }
# 178 : 0 : Reset();
# 179 : 0 : err = strprintf(_("Error initializing wallet database environment %s!"), fs::quoted(fs::PathToString(Directory())));
# 180 [ # # ]: 0 : if (ret == DB_RUNRECOVERY) {
# 181 : 0 : err += Untranslated(" ") + _("This error could occur if this wallet was not shutdown cleanly and was last loaded using a build with a newer version of Berkeley DB. If so, please use the software that last loaded this wallet");
# 182 : 0 : }
# 183 : 0 : return false;
# 184 : 0 : }
# 185 : :
# 186 : 587 : fDbEnvInit = true;
# 187 : 587 : fMockDb = false;
# 188 : 587 : return true;
# 189 : 587 : }
# 190 : :
# 191 : : //! Construct an in-memory mock Berkeley environment for testing
# 192 : : BerkeleyEnvironment::BerkeleyEnvironment() : m_use_shared_memory(false)
# 193 : 0 : {
# 194 : 0 : Reset();
# 195 : :
# 196 [ # # ]: 0 : LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::MakeMock\n");
# 197 : :
# 198 : 0 : dbenv->set_cachesize(1, 0, 1);
# 199 : 0 : dbenv->set_lg_bsize(10485760 * 4);
# 200 : 0 : dbenv->set_lg_max(10485760);
# 201 : 0 : dbenv->set_lk_max_locks(10000);
# 202 : 0 : dbenv->set_lk_max_objects(10000);
# 203 : 0 : dbenv->set_flags(DB_AUTO_COMMIT, 1);
# 204 : 0 : dbenv->log_set_config(DB_LOG_IN_MEMORY, 1);
# 205 : 0 : int ret = dbenv->open(nullptr,
# 206 : 0 : DB_CREATE |
# 207 : 0 : DB_INIT_LOCK |
# 208 : 0 : DB_INIT_LOG |
# 209 : 0 : DB_INIT_MPOOL |
# 210 : 0 : DB_INIT_TXN |
# 211 : 0 : DB_THREAD |
# 212 : 0 : DB_PRIVATE,
# 213 : 0 : S_IRUSR | S_IWUSR);
# 214 [ # # ]: 0 : if (ret > 0) {
# 215 : 0 : throw std::runtime_error(strprintf("BerkeleyEnvironment::MakeMock: Error %d opening database environment.", ret));
# 216 : 0 : }
# 217 : :
# 218 : 0 : fDbEnvInit = true;
# 219 : 0 : fMockDb = true;
# 220 : 0 : }
# 221 : :
# 222 : : BerkeleyBatch::SafeDbt::SafeDbt()
# 223 : 114479 : {
# 224 : 114479 : m_dbt.set_flags(DB_DBT_MALLOC);
# 225 : 114479 : }
# 226 : :
# 227 : : BerkeleyBatch::SafeDbt::SafeDbt(void* data, size_t size)
# 228 : : : m_dbt(data, size)
# 229 : 501672 : {
# 230 : 501672 : }
# 231 : :
# 232 : : BerkeleyBatch::SafeDbt::~SafeDbt()
# 233 : 616151 : {
# 234 [ + + ]: 616151 : if (m_dbt.get_data() != nullptr) {
# 235 : : // Clear memory, e.g. in case it was a private key
# 236 : 614553 : memory_cleanse(m_dbt.get_data(), m_dbt.get_size());
# 237 : : // under DB_DBT_MALLOC, data is malloced by the Dbt, but must be
# 238 : : // freed by the caller.
# 239 : : // https://docs.oracle.com/cd/E17275_01/html/api_reference/C/dbt.html
# 240 [ + + ]: 614553 : if (m_dbt.get_flags() & DB_DBT_MALLOC) {
# 241 : 112881 : free(m_dbt.get_data());
# 242 : 112881 : }
# 243 : 614553 : }
# 244 : 616151 : }
# 245 : :
# 246 : : const void* BerkeleyBatch::SafeDbt::get_data() const
# 247 : 225762 : {
# 248 : 225762 : return m_dbt.get_data();
# 249 : 225762 : }
# 250 : :
# 251 : : u_int32_t BerkeleyBatch::SafeDbt::get_size() const
# 252 : 112881 : {
# 253 : 112881 : return m_dbt.get_size();
# 254 : 112881 : }
# 255 : :
# 256 : : BerkeleyBatch::SafeDbt::operator Dbt*()
# 257 : 616151 : {
# 258 : 616151 : return &m_dbt;
# 259 : 616151 : }
# 260 : :
# 261 : : bool BerkeleyDatabase::Verify(bilingual_str& errorStr)
# 262 : 514 : {
# 263 : 514 : fs::path walletDir = env->Directory();
# 264 : 514 : fs::path file_path = walletDir / strFile;
# 265 : :
# 266 : 514 : LogPrintf("Using BerkeleyDB version %s\n", BerkeleyDatabaseVersion());
# 267 : 514 : LogPrintf("Using wallet %s\n", fs::PathToString(file_path));
# 268 : :
# 269 [ + + ]: 514 : if (!env->Open(errorStr)) {
# 270 : 6 : return false;
# 271 : 6 : }
# 272 : :
# 273 [ + + ]: 508 : if (fs::exists(file_path))
# 274 : 232 : {
# 275 : 232 : assert(m_refcount == 0);
# 276 : :
# 277 : 0 : Db db(env->dbenv.get(), 0);
# 278 : 232 : int result = db.verify(strFile.c_str(), nullptr, nullptr, 0);
# 279 [ - + ]: 232 : if (result != 0) {
# 280 : 0 : errorStr = strprintf(_("%s corrupt. Try using the wallet tool bitcoin-wallet to salvage or restoring a backup."), fs::quoted(fs::PathToString(file_path)));
# 281 : 0 : return false;
# 282 : 0 : }
# 283 : 232 : }
# 284 : : // also return true if files does not exists
# 285 : 508 : return true;
# 286 : 508 : }
# 287 : :
# 288 : : void BerkeleyEnvironment::CheckpointLSN(const std::string& strFile)
# 289 : 309 : {
# 290 : 309 : dbenv->txn_checkpoint(0, 0, 0);
# 291 [ - + ]: 309 : if (fMockDb)
# 292 : 0 : return;
# 293 : 309 : dbenv->lsn_reset(strFile.c_str(), 0);
# 294 : 309 : }
# 295 : :
# 296 : : BerkeleyDatabase::~BerkeleyDatabase()
# 297 : 598 : {
# 298 [ + - ]: 598 : if (env) {
# 299 : 598 : LOCK(cs_db);
# 300 : 598 : env->CloseDb(strFile);
# 301 : 598 : assert(!m_db);
# 302 : 0 : size_t erased = env->m_databases.erase(strFile);
# 303 : 598 : assert(erased == 1);
# 304 : 0 : env->m_fileids.erase(strFile);
# 305 : 598 : }
# 306 : 598 : }
# 307 : :
# 308 : : BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase& database, const bool read_only, bool fFlushOnCloseIn) : pdb(nullptr), activeTxn(nullptr), m_cursor(nullptr), m_database(database)
# 309 : 113004 : {
# 310 : 113004 : database.AddRef();
# 311 : 113004 : database.Open();
# 312 : 113004 : fReadOnly = read_only;
# 313 : 113004 : fFlushOnClose = fFlushOnCloseIn;
# 314 : 113004 : env = database.env.get();
# 315 : 113004 : pdb = database.m_db.get();
# 316 : 113004 : strFile = database.strFile;
# 317 [ + + ]: 113004 : if (!Exists(std::string("version"))) {
# 318 : 274 : bool fTmp = fReadOnly;
# 319 : 274 : fReadOnly = false;
# 320 : 274 : Write(std::string("version"), CLIENT_VERSION);
# 321 : 274 : fReadOnly = fTmp;
# 322 : 274 : }
# 323 : 113004 : }
# 324 : :
# 325 : : void BerkeleyDatabase::Open()
# 326 : 113004 : {
# 327 : 113004 : unsigned int nFlags = DB_THREAD | DB_CREATE;
# 328 : :
# 329 : 113004 : {
# 330 : 113004 : LOCK(cs_db);
# 331 : 113004 : bilingual_str open_err;
# 332 [ - + ]: 113004 : if (!env->Open(open_err))
# 333 : 0 : throw std::runtime_error("BerkeleyDatabase: Failed to open database environment.");
# 334 : :
# 335 [ + + ]: 113004 : if (m_db == nullptr) {
# 336 : 1114 : int ret;
# 337 : 1114 : std::unique_ptr<Db> pdb_temp = std::make_unique<Db>(env->dbenv.get(), 0);
# 338 : :
# 339 : 1114 : bool fMockDb = env->IsMock();
# 340 [ - + ]: 1114 : if (fMockDb) {
# 341 : 0 : DbMpoolFile* mpf = pdb_temp->get_mpf();
# 342 : 0 : ret = mpf->set_flags(DB_MPOOL_NOFILE, 1);
# 343 [ # # ]: 0 : if (ret != 0) {
# 344 : 0 : throw std::runtime_error(strprintf("BerkeleyDatabase: Failed to configure for no temp file backing for database %s", strFile));
# 345 : 0 : }
# 346 : 0 : }
# 347 : :
# 348 : 1114 : ret = pdb_temp->open(nullptr, // Txn pointer
# 349 [ - + ]: 1114 : fMockDb ? nullptr : strFile.c_str(), // Filename
# 350 [ - + ]: 1114 : fMockDb ? strFile.c_str() : "main", // Logical db name
# 351 : 1114 : DB_BTREE, // Database type
# 352 : 1114 : nFlags, // Flags
# 353 : 1114 : 0);
# 354 : :
# 355 [ - + ]: 1114 : if (ret != 0) {
# 356 : 0 : throw std::runtime_error(strprintf("BerkeleyDatabase: Error %d, can't open database %s", ret, strFile));
# 357 : 0 : }
# 358 : :
# 359 : : // Call CheckUniqueFileid on the containing BDB environment to
# 360 : : // avoid BDB data consistency bugs that happen when different data
# 361 : : // files in the same environment have the same fileid.
# 362 : 1114 : CheckUniqueFileid(*env, strFile, *pdb_temp, this->env->m_fileids[strFile]);
# 363 : :
# 364 : 1114 : m_db.reset(pdb_temp.release());
# 365 : :
# 366 : 1114 : }
# 367 : 113004 : }
# 368 : 113004 : }
# 369 : :
# 370 : : void BerkeleyBatch::Flush()
# 371 : 78097 : {
# 372 [ + + ]: 78097 : if (activeTxn)
# 373 : 1 : return;
# 374 : :
# 375 : : // Flush database activity from memory pool to disk log
# 376 : 78096 : unsigned int nMinutes = 0;
# 377 [ + + ]: 78096 : if (fReadOnly)
# 378 : 18 : nMinutes = 1;
# 379 : :
# 380 [ + - ]: 78096 : if (env) { // env is nullptr for dummy databases (i.e. in tests). Don't actually flush if env is nullptr so we don't segfault
# 381 [ + + ]: 78096 : env->dbenv->txn_checkpoint(nMinutes ? m_database.m_max_log_mb * 1024 : 0, nMinutes, 0);
# 382 : 78096 : }
# 383 : 78096 : }
# 384 : :
# 385 : : void BerkeleyDatabase::IncrementUpdateCounter()
# 386 : 184138 : {
# 387 : 184138 : ++nUpdateCounter;
# 388 : 184138 : }
# 389 : :
# 390 : : BerkeleyBatch::~BerkeleyBatch()
# 391 : 112998 : {
# 392 : 112998 : Close();
# 393 : 112998 : m_database.RemoveRef();
# 394 : 112998 : }
# 395 : :
# 396 : : void BerkeleyBatch::Close()
# 397 : 113016 : {
# 398 [ + + ]: 113016 : if (!pdb)
# 399 : 18 : return;
# 400 [ - + ]: 112998 : if (activeTxn)
# 401 : 0 : activeTxn->abort();
# 402 : 112998 : activeTxn = nullptr;
# 403 : 112998 : pdb = nullptr;
# 404 : 112998 : CloseCursor();
# 405 : :
# 406 [ + + ]: 112998 : if (fFlushOnClose)
# 407 : 77971 : Flush();
# 408 : 112998 : }
# 409 : :
# 410 : : void BerkeleyEnvironment::CloseDb(const std::string& strFile)
# 411 : 1795 : {
# 412 : 1795 : {
# 413 : 1795 : LOCK(cs_db);
# 414 : 1795 : auto it = m_databases.find(strFile);
# 415 : 1795 : assert(it != m_databases.end());
# 416 : 0 : BerkeleyDatabase& database = it->second.get();
# 417 [ + + ]: 1795 : if (database.m_db) {
# 418 : : // Close the database handle
# 419 : 1108 : database.m_db->close(0);
# 420 : 1108 : database.m_db.reset();
# 421 : 1108 : }
# 422 : 1795 : }
# 423 : 1795 : }
# 424 : :
# 425 : : void BerkeleyEnvironment::ReloadDbEnv()
# 426 : 18 : {
# 427 : : // Make sure that no Db's are in use
# 428 : 18 : AssertLockNotHeld(cs_db);
# 429 : 18 : std::unique_lock<RecursiveMutex> lock(cs_db);
# 430 : 18 : m_db_in_use.wait(lock, [this](){
# 431 [ + + ]: 18 : for (auto& db : m_databases) {
# 432 [ - + ]: 18 : if (db.second.get().m_refcount > 0) return false;
# 433 : 18 : }
# 434 : 18 : return true;
# 435 : 18 : });
# 436 : :
# 437 : 18 : std::vector<std::string> filenames;
# 438 [ + + ]: 18 : for (auto it : m_databases) {
# 439 : 18 : filenames.push_back(it.first);
# 440 : 18 : }
# 441 : : // Close the individual Db's
# 442 [ + + ]: 18 : for (const std::string& filename : filenames) {
# 443 : 18 : CloseDb(filename);
# 444 : 18 : }
# 445 : : // Reset the environment
# 446 : 18 : Flush(true); // This will flush and close the environment
# 447 : 18 : Reset();
# 448 : 18 : bilingual_str open_err;
# 449 : 18 : Open(open_err);
# 450 : 18 : }
# 451 : :
# 452 : : bool BerkeleyDatabase::Rewrite(const char* pszSkip)
# 453 : 18 : {
# 454 : 18 : while (true) {
# 455 : 18 : {
# 456 : 18 : LOCK(cs_db);
# 457 [ + - ]: 18 : if (m_refcount <= 0) {
# 458 : : // Flush log data to the dat file
# 459 : 18 : env->CloseDb(strFile);
# 460 : 18 : env->CheckpointLSN(strFile);
# 461 : 18 : m_refcount = -1;
# 462 : :
# 463 : 18 : bool fSuccess = true;
# 464 : 18 : LogPrintf("BerkeleyBatch::Rewrite: Rewriting %s...\n", strFile);
# 465 : 18 : std::string strFileRes = strFile + ".rewrite";
# 466 : 18 : { // surround usage of db with extra {}
# 467 : 18 : BerkeleyBatch db(*this, true);
# 468 : 18 : std::unique_ptr<Db> pdbCopy = std::make_unique<Db>(env->dbenv.get(), 0);
# 469 : :
# 470 : 18 : int ret = pdbCopy->open(nullptr, // Txn pointer
# 471 : 18 : strFileRes.c_str(), // Filename
# 472 : 18 : "main", // Logical db name
# 473 : 18 : DB_BTREE, // Database type
# 474 : 18 : DB_CREATE, // Flags
# 475 : 18 : 0);
# 476 [ - + ]: 18 : if (ret > 0) {
# 477 : 0 : LogPrintf("BerkeleyBatch::Rewrite: Can't create database file %s\n", strFileRes);
# 478 : 0 : fSuccess = false;
# 479 : 0 : }
# 480 : :
# 481 [ + - ]: 18 : if (db.StartCursor()) {
# 482 [ + - ]: 1832 : while (fSuccess) {
# 483 : 1832 : CDataStream ssKey(SER_DISK, CLIENT_VERSION);
# 484 : 1832 : CDataStream ssValue(SER_DISK, CLIENT_VERSION);
# 485 : 1832 : bool complete;
# 486 : 1832 : bool ret1 = db.ReadAtCursor(ssKey, ssValue, complete);
# 487 [ + + ]: 1832 : if (complete) {
# 488 : 18 : break;
# 489 [ - + ]: 1814 : } else if (!ret1) {
# 490 : 0 : fSuccess = false;
# 491 : 0 : break;
# 492 : 0 : }
# 493 [ - + ][ - + ]: 1814 : if (pszSkip &&
# 494 [ # # ]: 1814 : strncmp((const char*)ssKey.data(), pszSkip, std::min(ssKey.size(), strlen(pszSkip))) == 0)
# 495 : 0 : continue;
# 496 [ + + ]: 1814 : if (strncmp((const char*)ssKey.data(), "\x07version", 8) == 0) {
# 497 : : // Update version:
# 498 : 18 : ssValue.clear();
# 499 : 18 : ssValue << CLIENT_VERSION;
# 500 : 18 : }
# 501 : 1814 : Dbt datKey(ssKey.data(), ssKey.size());
# 502 : 1814 : Dbt datValue(ssValue.data(), ssValue.size());
# 503 : 1814 : int ret2 = pdbCopy->put(nullptr, &datKey, &datValue, DB_NOOVERWRITE);
# 504 [ - + ]: 1814 : if (ret2 > 0)
# 505 : 0 : fSuccess = false;
# 506 : 1814 : }
# 507 : 18 : db.CloseCursor();
# 508 : 18 : }
# 509 [ + - ]: 18 : if (fSuccess) {
# 510 : 18 : db.Close();
# 511 : 18 : env->CloseDb(strFile);
# 512 [ - + ]: 18 : if (pdbCopy->close(0))
# 513 : 0 : fSuccess = false;
# 514 : 18 : } else {
# 515 : 0 : pdbCopy->close(0);
# 516 : 0 : }
# 517 : 18 : }
# 518 [ + - ]: 18 : if (fSuccess) {
# 519 : 18 : Db dbA(env->dbenv.get(), 0);
# 520 [ - + ]: 18 : if (dbA.remove(strFile.c_str(), nullptr, 0))
# 521 : 0 : fSuccess = false;
# 522 : 18 : Db dbB(env->dbenv.get(), 0);
# 523 [ - + ]: 18 : if (dbB.rename(strFileRes.c_str(), nullptr, strFile.c_str(), 0))
# 524 : 0 : fSuccess = false;
# 525 : 18 : }
# 526 [ - + ]: 18 : if (!fSuccess)
# 527 : 0 : LogPrintf("BerkeleyBatch::Rewrite: Failed to rewrite database file %s\n", strFileRes);
# 528 : 18 : return fSuccess;
# 529 : 18 : }
# 530 : 18 : }
# 531 : 0 : UninterruptibleSleep(std::chrono::milliseconds{100});
# 532 : 0 : }
# 533 : 18 : }
# 534 : :
# 535 : :
# 536 : : void BerkeleyEnvironment::Flush(bool fShutdown)
# 537 : 1265 : {
# 538 : 1265 : int64_t nStart = GetTimeMillis();
# 539 : : // Flush log data to the actual data file on all files that are not in use
# 540 [ + + ][ + + ]: 1265 : LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: [%s] Flush(%s)%s\n", strPath, fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started");
# [ + + ]
# 541 [ + + ]: 1265 : if (!fDbEnvInit)
# 542 : 381 : return;
# 543 : 884 : {
# 544 : 884 : LOCK(cs_db);
# 545 : 884 : bool no_dbs_accessed = true;
# 546 [ + + ]: 922 : for (auto& db_it : m_databases) {
# 547 : 922 : std::string strFile = db_it.first;
# 548 : 922 : int nRefCount = db_it.second.get().m_refcount;
# 549 [ + + ]: 922 : if (nRefCount < 0) continue;
# 550 [ + + ]: 858 : LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: Flushing %s (refcount = %d)...\n", strFile, nRefCount);
# 551 [ + + ]: 858 : if (nRefCount == 0) {
# 552 : : // Move log data to the dat file
# 553 : 852 : CloseDb(strFile);
# 554 [ + + ]: 852 : LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: %s checkpoint\n", strFile);
# 555 : 852 : dbenv->txn_checkpoint(0, 0, 0);
# 556 [ + + ]: 852 : LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: %s detach\n", strFile);
# 557 [ + - ]: 852 : if (!fMockDb)
# 558 : 852 : dbenv->lsn_reset(strFile.c_str(), 0);
# 559 [ + + ]: 852 : LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: %s closed\n", strFile);
# 560 : 852 : nRefCount = -1;
# 561 : 852 : } else {
# 562 : 6 : no_dbs_accessed = false;
# 563 : 6 : }
# 564 : 858 : }
# 565 [ + + ][ + - ]: 884 : LogPrint(BCLog::WALLETDB, "BerkeleyEnvironment::Flush: Flush(%s)%s took %15dms\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " database not started", GetTimeMillis() - nStart);
# [ + + ]
# 566 [ + + ]: 884 : if (fShutdown) {
# 567 : 396 : char** listp;
# 568 [ + - ]: 396 : if (no_dbs_accessed) {
# 569 : 396 : dbenv->log_archive(&listp, DB_ARCH_REMOVE);
# 570 : 396 : Close();
# 571 [ + - ]: 396 : if (!fMockDb) {
# 572 : 396 : fs::remove_all(fs::PathFromString(strPath) / "database");
# 573 : 396 : }
# 574 : 396 : }
# 575 : 396 : }
# 576 : 884 : }
# 577 : 884 : }
# 578 : :
# 579 : : bool BerkeleyDatabase::PeriodicFlush()
# 580 : 275 : {
# 581 : : // Don't flush if we can't acquire the lock.
# 582 : 275 : TRY_LOCK(cs_db, lockDb);
# 583 [ + + ]: 275 : if (!lockDb) return false;
# 584 : :
# 585 : : // Don't flush if any databases are in use
# 586 [ + + ]: 264 : for (auto& it : env->m_databases) {
# 587 [ - + ]: 264 : if (it.second.get().m_refcount > 0) return false;
# 588 : 264 : }
# 589 : :
# 590 : : // Don't flush if there haven't been any batch writes for this database.
# 591 [ - + ]: 264 : if (m_refcount < 0) return false;
# 592 : :
# 593 [ + - ]: 264 : LogPrint(BCLog::WALLETDB, "Flushing %s\n", strFile);
# 594 : 264 : int64_t nStart = GetTimeMillis();
# 595 : :
# 596 : : // Flush wallet file so it's self contained
# 597 : 264 : env->CloseDb(strFile);
# 598 : 264 : env->CheckpointLSN(strFile);
# 599 : 264 : m_refcount = -1;
# 600 : :
# 601 [ + - ]: 264 : LogPrint(BCLog::WALLETDB, "Flushed %s %dms\n", strFile, GetTimeMillis() - nStart);
# 602 : :
# 603 : 264 : return true;
# 604 : 264 : }
# 605 : :
# 606 : : bool BerkeleyDatabase::Backup(const std::string& strDest) const
# 607 : 27 : {
# 608 : 27 : while (true)
# 609 : 27 : {
# 610 : 27 : {
# 611 : 27 : LOCK(cs_db);
# 612 [ + - ]: 27 : if (m_refcount <= 0)
# 613 : 27 : {
# 614 : : // Flush log data to the dat file
# 615 : 27 : env->CloseDb(strFile);
# 616 : 27 : env->CheckpointLSN(strFile);
# 617 : :
# 618 : : // Copy wallet file
# 619 : 27 : fs::path pathSrc = env->Directory() / strFile;
# 620 : 27 : fs::path pathDest(fs::PathFromString(strDest));
# 621 [ + + ]: 27 : if (fs::is_directory(pathDest))
# 622 : 2 : pathDest /= fs::PathFromString(strFile);
# 623 : :
# 624 : 27 : try {
# 625 [ + + ][ + - ]: 27 : if (fs::exists(pathDest) && fs::equivalent(pathSrc, pathDest)) {
# 626 : 4 : LogPrintf("cannot backup to wallet source file %s\n", fs::PathToString(pathDest));
# 627 : 4 : return false;
# 628 : 4 : }
# 629 : :
# 630 : 23 : fs::copy_file(pathSrc, pathDest, fs::copy_options::overwrite_existing);
# 631 : 23 : LogPrintf("copied %s to %s\n", strFile, fs::PathToString(pathDest));
# 632 : 23 : return true;
# 633 : 27 : } catch (const fs::filesystem_error& e) {
# 634 : 0 : LogPrintf("error copying %s to %s - %s\n", strFile, fs::PathToString(pathDest), fsbridge::get_filesystem_error_message(e));
# 635 : 0 : return false;
# 636 : 0 : }
# 637 : 27 : }
# 638 : 27 : }
# 639 : 0 : UninterruptibleSleep(std::chrono::milliseconds{100});
# 640 : 0 : }
# 641 : 27 : }
# 642 : :
# 643 : : void BerkeleyDatabase::Flush()
# 644 : 856 : {
# 645 : 856 : env->Flush(false);
# 646 : 856 : }
# 647 : :
# 648 : : void BerkeleyDatabase::Close()
# 649 : 391 : {
# 650 : 391 : env->Flush(true);
# 651 : 391 : }
# 652 : :
# 653 : : void BerkeleyDatabase::ReloadDbEnv()
# 654 : 18 : {
# 655 : 18 : env->ReloadDbEnv();
# 656 : 18 : }
# 657 : :
# 658 : : bool BerkeleyBatch::StartCursor()
# 659 : 524 : {
# 660 : 524 : assert(!m_cursor);
# 661 [ - + ]: 524 : if (!pdb)
# 662 : 0 : return false;
# 663 : 524 : int ret = pdb->cursor(nullptr, &m_cursor, 0);
# 664 : 524 : return ret == 0;
# 665 : 524 : }
# 666 : :
# 667 : : bool BerkeleyBatch::ReadAtCursor(CDataStream& ssKey, CDataStream& ssValue, bool& complete)
# 668 : 49369 : {
# 669 : 49369 : complete = false;
# 670 [ - + ]: 49369 : if (m_cursor == nullptr) return false;
# 671 : : // Read at cursor
# 672 : 49369 : SafeDbt datKey;
# 673 : 49369 : SafeDbt datValue;
# 674 : 49369 : int ret = m_cursor->get(datKey, datValue, DB_NEXT);
# 675 [ + + ]: 49369 : if (ret == DB_NOTFOUND) {
# 676 : 524 : complete = true;
# 677 : 524 : }
# 678 [ + + ]: 49369 : if (ret != 0)
# 679 : 524 : return false;
# 680 [ - + ][ - + ]: 48845 : else if (datKey.get_data() == nullptr || datValue.get_data() == nullptr)
# 681 : 0 : return false;
# 682 : :
# 683 : : // Convert to streams
# 684 : 48845 : ssKey.SetType(SER_DISK);
# 685 : 48845 : ssKey.clear();
# 686 : 48845 : ssKey.write({BytePtr(datKey.get_data()), datKey.get_size()});
# 687 : 48845 : ssValue.SetType(SER_DISK);
# 688 : 48845 : ssValue.clear();
# 689 : 48845 : ssValue.write({BytePtr(datValue.get_data()), datValue.get_size()});
# 690 : 48845 : return true;
# 691 : 49369 : }
# 692 : :
# 693 : : void BerkeleyBatch::CloseCursor()
# 694 : 113522 : {
# 695 [ + + ]: 113522 : if (!m_cursor) return;
# 696 : 524 : m_cursor->close();
# 697 : 524 : m_cursor = nullptr;
# 698 : 524 : }
# 699 : :
# 700 : : bool BerkeleyBatch::TxnBegin()
# 701 : 25 : {
# 702 [ - + ][ - + ]: 25 : if (!pdb || activeTxn)
# 703 : 0 : return false;
# 704 : 25 : DbTxn* ptxn = env->TxnBegin();
# 705 [ - + ]: 25 : if (!ptxn)
# 706 : 0 : return false;
# 707 : 25 : activeTxn = ptxn;
# 708 : 25 : return true;
# 709 : 25 : }
# 710 : :
# 711 : : bool BerkeleyBatch::TxnCommit()
# 712 : 21 : {
# 713 [ - + ][ - + ]: 21 : if (!pdb || !activeTxn)
# 714 : 0 : return false;
# 715 : 21 : int ret = activeTxn->commit(0);
# 716 : 21 : activeTxn = nullptr;
# 717 : 21 : return (ret == 0);
# 718 : 21 : }
# 719 : :
# 720 : : bool BerkeleyBatch::TxnAbort()
# 721 : 4 : {
# 722 [ - + ][ - + ]: 4 : if (!pdb || !activeTxn)
# 723 : 0 : return false;
# 724 : 4 : int ret = activeTxn->abort();
# 725 : 4 : activeTxn = nullptr;
# 726 : 4 : return (ret == 0);
# 727 : 4 : }
# 728 : :
# 729 : : bool BerkeleyDatabaseSanityCheck()
# 730 : 1634 : {
# 731 : 1634 : int major, minor;
# 732 : 1634 : DbEnv::version(&major, &minor, nullptr);
# 733 : :
# 734 : : /* If the major version differs, or the minor version of library is *older*
# 735 : : * than the header that was compiled against, flag an error.
# 736 : : */
# 737 [ - + ][ - + ]: 1634 : if (major != DB_VERSION_MAJOR || minor < DB_VERSION_MINOR) {
# 738 : 0 : LogPrintf("BerkeleyDB database version conflict: header version is %d.%d, library version is %d.%d\n",
# 739 : 0 : DB_VERSION_MAJOR, DB_VERSION_MINOR, major, minor);
# 740 : 0 : return false;
# 741 : 0 : }
# 742 : :
# 743 : 1634 : return true;
# 744 : 1634 : }
# 745 : :
# 746 : : std::string BerkeleyDatabaseVersion()
# 747 : 514 : {
# 748 : 514 : return DbEnv::version(nullptr, nullptr, nullptr);
# 749 : 514 : }
# 750 : :
# 751 : : bool BerkeleyBatch::ReadKey(CDataStream&& key, CDataStream& value)
# 752 : 15741 : {
# 753 [ - + ]: 15741 : if (!pdb)
# 754 : 0 : return false;
# 755 : :
# 756 : 15741 : SafeDbt datKey(key.data(), key.size());
# 757 : :
# 758 : 15741 : SafeDbt datValue;
# 759 : 15741 : int ret = pdb->get(activeTxn, datKey, datValue, 0);
# 760 [ + + ][ + - ]: 15741 : if (ret == 0 && datValue.get_data() != nullptr) {
# 761 : 15191 : value.write({BytePtr(datValue.get_data()), datValue.get_size()});
# 762 : 15191 : return true;
# 763 : 15191 : }
# 764 : 550 : return false;
# 765 : 15741 : }
# 766 : :
# 767 : : bool BerkeleyBatch::WriteKey(CDataStream&& key, CDataStream&& value, bool overwrite)
# 768 : 179534 : {
# 769 [ - + ]: 179534 : if (!pdb)
# 770 : 0 : return false;
# 771 [ - + ]: 179534 : if (fReadOnly)
# 772 : 0 : assert(!"Write called on database in read-only mode");
# 773 : :
# 774 : 0 : SafeDbt datKey(key.data(), key.size());
# 775 : :
# 776 : 179534 : SafeDbt datValue(value.data(), value.size());
# 777 : :
# 778 [ + + ]: 179534 : int ret = pdb->put(activeTxn, datKey, datValue, (overwrite ? 0 : DB_NOOVERWRITE));
# 779 : 179534 : return (ret == 0);
# 780 : 179534 : }
# 781 : :
# 782 : : bool BerkeleyBatch::EraseKey(CDataStream&& key)
# 783 : 13865 : {
# 784 [ - + ]: 13865 : if (!pdb)
# 785 : 0 : return false;
# 786 [ - + ]: 13865 : if (fReadOnly)
# 787 : 0 : assert(!"Erase called on database in read-only mode");
# 788 : :
# 789 : 0 : SafeDbt datKey(key.data(), key.size());
# 790 : :
# 791 : 13865 : int ret = pdb->del(activeTxn, datKey, 0);
# 792 [ + + ][ + - ]: 13865 : return (ret == 0 || ret == DB_NOTFOUND);
# 793 : 13865 : }
# 794 : :
# 795 : : bool BerkeleyBatch::HasKey(CDataStream&& key)
# 796 : 112998 : {
# 797 [ - + ]: 112998 : if (!pdb)
# 798 : 0 : return false;
# 799 : :
# 800 : 112998 : SafeDbt datKey(key.data(), key.size());
# 801 : :
# 802 : 112998 : int ret = pdb->exists(activeTxn, datKey, 0);
# 803 : 112998 : return ret == 0;
# 804 : 112998 : }
# 805 : :
# 806 : : void BerkeleyDatabase::AddRef()
# 807 : 113004 : {
# 808 : 113004 : LOCK(cs_db);
# 809 [ + + ]: 113004 : if (m_refcount < 0) {
# 810 : 282 : m_refcount = 1;
# 811 : 112722 : } else {
# 812 : 112722 : m_refcount++;
# 813 : 112722 : }
# 814 : 113004 : }
# 815 : :
# 816 : : void BerkeleyDatabase::RemoveRef()
# 817 : 112998 : {
# 818 : 112998 : LOCK(cs_db);
# 819 : 112998 : m_refcount--;
# 820 [ + - ]: 112998 : if (env) env->m_db_in_use.notify_all();
# 821 : 112998 : }
# 822 : :
# 823 : : std::unique_ptr<DatabaseBatch> BerkeleyDatabase::MakeBatch(bool flush_on_close)
# 824 : 112986 : {
# 825 : 112986 : return std::make_unique<BerkeleyBatch>(*this, false, flush_on_close);
# 826 : 112986 : }
# 827 : :
# 828 : : std::unique_ptr<BerkeleyDatabase> MakeBerkeleyDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error)
# 829 : 602 : {
# 830 : 602 : fs::path data_file = BDBDataFile(path);
# 831 : 602 : std::unique_ptr<BerkeleyDatabase> db;
# 832 : 602 : {
# 833 : 602 : LOCK(cs_db); // Lock env.m_databases until insert in BerkeleyDatabase constructor
# 834 : 602 : std::string data_filename = fs::PathToString(data_file.filename());
# 835 : 602 : std::shared_ptr<BerkeleyEnvironment> env = GetBerkeleyEnv(data_file.parent_path(), options.use_shared_memory);
# 836 [ + + ]: 602 : if (env->m_databases.count(data_filename)) {
# 837 : 4 : error = Untranslated(strprintf("Refusing to load database. Data file '%s' is already loaded.", fs::PathToString(env->Directory() / data_filename)));
# 838 : 4 : status = DatabaseStatus::FAILED_ALREADY_LOADED;
# 839 : 4 : return nullptr;
# 840 : 4 : }
# 841 : 598 : db = std::make_unique<BerkeleyDatabase>(std::move(env), std::move(data_filename), options);
# 842 : 598 : }
# 843 : :
# 844 [ + + ][ + + ]: 598 : if (options.verify && !db->Verify(error)) {
# 845 : 6 : status = DatabaseStatus::FAILED_VERIFY;
# 846 : 6 : return nullptr;
# 847 : 6 : }
# 848 : :
# 849 : 592 : status = DatabaseStatus::SUCCESS;
# 850 : 592 : return db;
# 851 : 598 : }
# 852 : : } // namespace wallet
|