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