LCOV - code coverage report
Current view: top level - src/wallet - bdb.cpp (source / functions) Coverage Total Hit
Test: test_bitcoin_coverage.info Lines: 64.0 % 545 349
Test Date: 2024-08-28 04:44:32 Functions: 83.1 % 59 49
Branches: 32.0 % 798 255

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

Generated by: LCOV version 2.0-1