LCOV - code coverage report
Current view: top level - src/wallet - bdb.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 493 579 85.1 %
Date: 2026-06-25 07:23:43 Functions: 62 65 95.4 %

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

Generated by: LCOV version 1.16