LCOV - code coverage report
Current view: top level - src/wallet - bdb.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 504 561 89.8 %
Date: 2020-09-26 01:30:44 Functions: 63 64 98.4 %

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

Generated by: LCOV version 1.15