47void CheckUniqueFileid(
const BerkeleyEnvironment& env,
const std::string& filename, Db& db, WalletDatabaseFileId& fileid)
49 if (env.IsMock())
return;
51 int ret = db.get_mpf()->get_fileid(fileid.value);
53 throw std::runtime_error(
strprintf(
"BerkeleyDatabase: Can't open database %s (get_fileid failed with %d)", filename,
ret));
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));
65std::map<std::string, std::weak_ptr<BerkeleyEnvironment>> g_dbenvs
GUARDED_BY(cs_db);
68static constexpr auto REVERSE_BYTE_ORDER{std::endian::native == std::endian::little ? 4321 : 1234};
84 auto inserted = g_dbenvs.emplace(
fs::PathToString(env_directory), std::weak_ptr<BerkeleyEnvironment>());
85 if (inserted.second) {
86 auto env = std::make_shared<BerkeleyEnvironment>(env_directory, use_shared_memory);
87 inserted.first->second = env;
90 return inserted.first->second.lock();
108 database.
m_db->close(0);
109 database.
m_db.reset();
113 FILE* error_file =
nullptr;
114 dbenv->get_errfile(&error_file);
118 LogPrintf(
"BerkeleyEnvironment::Close: Error %d closing database environment: %s\n",
ret, DbEnv::strerror(
ret));
120 DbEnv(uint32_t{0}).remove(
strPath.c_str(), 0);
122 if (error_file) fclose(error_file);
129 dbenv.reset(
new DbEnv(DB_CXX_NO_EXCEPTIONS));
155 LogPrintf(
"Cannot obtain a lock on wallet directory %s. Another instance may be using it.\n",
strPath);
160 fs::path pathLogDir = pathIn /
"database";
162 fs::path pathErrorFile = pathIn /
"db.log";
165 unsigned int nEnvFlags = 0;
167 nEnvFlags |= DB_PRIVATE;
171 dbenv->set_cachesize(0, 0x100000, 1);
172 dbenv->set_lg_bsize(0x10000);
173 dbenv->set_lg_max(1048576);
174 dbenv->set_lk_max_locks(40000);
175 dbenv->set_lk_max_objects(40000);
177 dbenv->set_flags(DB_AUTO_COMMIT, 1);
178 dbenv->set_flags(DB_TXN_WRITE_NOSYNC, 1);
179 dbenv->log_set_config(DB_LOG_AUTO_REMOVE, 1);
191 LogPrintf(
"BerkeleyEnvironment::Open: Error %d opening database environment: %s\n",
ret, DbEnv::strerror(
ret));
192 int ret2 =
dbenv->close(0);
194 LogPrintf(
"BerkeleyEnvironment::Open: Error %d closing failed database environment: %s\n", ret2, DbEnv::strerror(ret2));
198 if (
ret == DB_RUNRECOVERY) {
199 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");
216 dbenv->set_cachesize(1, 0, 1);
217 dbenv->set_lg_bsize(10485760 * 4);
218 dbenv->set_lg_max(10485760);
219 dbenv->set_lk_max_locks(10000);
220 dbenv->set_lk_max_objects(10000);
221 dbenv->set_flags(DB_AUTO_COMMIT, 1);
222 dbenv->log_set_config(DB_LOG_IN_MEMORY, 1);
233 throw std::runtime_error(
strprintf(
"BerkeleyEnvironment::MakeMock: Error %d opening database environment.",
ret));
262 m_dbt.set_flags(DB_DBT_MALLOC);
272 if (
m_dbt.get_data() !=
nullptr) {
278 if (
m_dbt.get_flags() & DB_DBT_MALLOC) {
279 free(
m_dbt.get_data());
286 return m_dbt.get_data();
291 return m_dbt.get_size();
294SafeDbt::operator Dbt*()
301 return {
reinterpret_cast<const std::byte*
>(dbt.
get_data()), dbt.
get_size()};
308 m_filename(
std::move(filename)),
309 m_max_log_mb(options.max_log_mb)
311 auto inserted = this->env->m_databases.emplace(
m_filename, std::ref(*
this));
323 if (!
env->Open(errorStr)) {
331 Db db(
env->dbenv.get(), 0);
333 int result = db.verify(strFile.c_str(),
nullptr,
nullptr, 0);
345 dbenv->txn_checkpoint(0, 0, 0);
348 dbenv->lsn_reset(strFile.c_str(), 0);
376 unsigned int nFlags = DB_THREAD | DB_CREATE;
381 if (!
env->Open(open_err))
382 throw std::runtime_error(
"BerkeleyDatabase: Failed to open database environment.");
384 if (
m_db ==
nullptr) {
386 std::unique_ptr<Db> pdb_temp = std::make_unique<Db>(
env->dbenv.get(), 0);
389 bool fMockDb =
env->IsMock();
391 DbMpoolFile* mpf = pdb_temp->get_mpf();
392 ret = mpf->set_flags(DB_MPOOL_NOFILE, 1);
394 throw std::runtime_error(
strprintf(
"BerkeleyDatabase: Failed to configure for no temp file backing for database %s", strFile));
402 ret = pdb_temp->open(
nullptr,
403 fMockDb ?
nullptr : strFile.c_str(),
404 fMockDb ? strFile.c_str() :
"main",
410 throw std::runtime_error(
strprintf(
"BerkeleyDatabase: Error %d, can't open database %s",
ret, strFile));
416 CheckUniqueFileid(*
env, strFile, *pdb_temp, this->env->m_fileids[strFile]);
418 m_db.reset(pdb_temp.release());
430 unsigned int nMinutes = 0;
472 database.
m_db->close(0);
473 database.
m_db.reset();
482 std::unique_lock<RecursiveMutex> lock(cs_db);
485 if (db.second.get().m_refcount > 0)
return false;
490 std::vector<fs::path> filenames;
493 filenames.push_back(it.first);
496 for (
const fs::path& filename : filenames) {
508 DbTxn* ptxn =
nullptr;
510 if (!ptxn ||
ret != 0)
524 env->CheckpointLSN(strFile);
527 bool fSuccess =
true;
528 LogPrintf(
"BerkeleyBatch::Rewrite: Rewriting %s...\n", strFile);
529 std::string strFileRes = strFile +
".rewrite";
532 std::unique_ptr<Db> pdbCopy = std::make_unique<Db>(
env->dbenv.get(), 0);
538 int ret = pdbCopy->open(
nullptr,
545 LogPrintf(
"BerkeleyBatch::Rewrite: Can't create database file %s\n", strFileRes);
549 std::unique_ptr<DatabaseCursor> cursor = db.
GetNewCursor();
562 strncmp((
const char*)ssKey.data(), pszSkip, std::min(ssKey.size(), strlen(pszSkip))) == 0)
564 if (strncmp((
const char*)ssKey.data(),
"\x07version", 8) == 0) {
569 Dbt datKey(ssKey.data(), ssKey.size());
570 Dbt datValue(ssValue.data(), ssValue.size());
571 int ret2 = pdbCopy->put(
nullptr, &datKey, &datValue, DB_NOOVERWRITE);
580 if (pdbCopy->close(0))
587 Db dbA(
env->dbenv.get(), 0);
588 if (dbA.remove(strFile.c_str(),
nullptr, 0))
590 Db dbB(
env->dbenv.get(), 0);
591 if (dbB.rename(strFileRes.c_str(),
nullptr, strFile.c_str(), 0))
595 LogPrintf(
"BerkeleyBatch::Rewrite: Failed to rewrite database file %s\n", strFileRes);
606 const auto start{SteadyClock::now()};
613 bool no_dbs_accessed =
true;
615 const fs::path& filename = db_it.first;
616 int nRefCount = db_it.second.get().m_refcount;
617 if (nRefCount < 0)
continue;
620 if (nRefCount == 0) {
624 dbenv->txn_checkpoint(0, 0, 0);
627 dbenv->lsn_reset(strFile.c_str(), 0);
631 no_dbs_accessed =
false;
634 LogDebug(
BCLog::WALLETDB,
"BerkeleyEnvironment::Flush: Flush(%s)%s took %15dms\n", fShutdown ?
"true" :
"false",
fDbEnvInit ?
"" :
" database not started", Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
637 if (no_dbs_accessed) {
638 dbenv->log_archive(&listp, DB_ARCH_REMOVE);
652 if (!lockDb)
return false;
655 for (
auto& it :
env->m_databases) {
656 if (it.second.get().m_refcount > 0)
return false;
664 const auto start{SteadyClock::now()};
668 env->CheckpointLSN(strFile);
671 LogDebug(
BCLog::WALLETDB,
"Flushed %s %dms\n", strFile, Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
687 env->CheckpointLSN(strFile);
692 if (fs::is_directory(pathDest))
696 if (
fs::exists(pathDest) && fs::equivalent(pathSrc, pathDest)) {
701 fs::copy_file(pathSrc, pathDest, fs::copy_options::overwrite_existing);
704 }
catch (
const fs::filesystem_error& e) {
732 if (!database.
m_db.get()) {
733 throw std::runtime_error(
STR_INTERNAL_BUG(
"BerkeleyDatabase does not exist"));
751 ret =
m_cursor->get(datKey, datValue, DB_SET_RANGE);
756 if (
ret == DB_NOTFOUND) {
770 ssKey.
write(raw_key);
785 if (!
pdb)
return nullptr;
786 return std::make_unique<BerkeleyCursor>(
m_database, *
this);
791 if (!
pdb)
return nullptr;
827 DbEnv::version(&major, &minor,
nullptr);
832 if (major != DB_VERSION_MAJOR || minor < DB_VERSION_MINOR) {
833 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);
843 return DbEnv::version(
nullptr,
nullptr,
nullptr);
851 SafeDbt datKey(key.data(), key.size());
868 assert(!
"Write called on database in read-only mode");
870 SafeDbt datKey(key.data(), key.size());
872 SafeDbt datValue(value.data(), value.size());
874 int ret =
pdb->put(
activeTxn, datKey, datValue, (overwrite ? 0 : DB_NOOVERWRITE));
883 assert(!
"Erase called on database in read-only mode");
885 SafeDbt datKey(key.data(), key.size());
888 return (
ret == 0 ||
ret == DB_NOTFOUND);
896 SafeDbt datKey(key.data(), key.size());
910 auto cursor{std::make_unique<BerkeleyCursor>(
m_database, *
this)};
914 Dbt prefix_key{
const_cast<std::byte*
>(
prefix.data()),
static_cast<uint32_t
>(
prefix.size())}, prefix_value{};
915 int ret{cursor->dbc()->get(&prefix_key, &prefix_value, DB_SET_RANGE)};
916 for (
int flag{DB_CURRENT};
ret == 0; flag = DB_NEXT) {
918 ret = cursor->dbc()->get(key, value, flag);
920 ret = cursor->dbc()->del(0);
923 return ret == 0 ||
ret == DB_NOTFOUND;
940 if (
env)
env->m_db_in_use.notify_all();
945 return std::make_unique<BerkeleyBatch>(*
this,
false, flush_on_close);
951 std::unique_ptr<BerkeleyDatabase> db;
956 if (env->m_databases.count(data_filename)) {
961 db = std::make_unique<BerkeleyDatabase>(std::move(env), std::move(data_filename), options);
964 if (options.
verify && !db->Verify(error)) {
#define BDB_DB_FILE_ID_LEN
#define STR_INTERNAL_BUG(msg)
#define Assume(val)
Assume is the identity function.
Double ended buffer combining vector and stream-like interfaces.
void write(Span< const value_type > src)
constexpr C * begin() const noexcept
constexpr C * end() const noexcept
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
RAII class that provides access to a Berkeley database.
bool ReadKey(DataStream &&key, DataStream &value) override
bool HasKey(DataStream &&key) override
BerkeleyDatabase & m_database
bool EraseKey(DataStream &&key) override
~BerkeleyBatch() override
BerkeleyBatch(BerkeleyDatabase &database, const bool fReadOnly, bool fFlushOnCloseIn=true)
std::unique_ptr< DatabaseCursor > GetNewPrefixCursor(Span< const std::byte > prefix) override
bool TxnCommit() override
BerkeleyEnvironment * env
std::unique_ptr< DatabaseCursor > GetNewCursor() override
bool ErasePrefix(Span< const std::byte > prefix) override
bool WriteKey(DataStream &&key, DataStream &&value, bool overwrite=true) override
~BerkeleyCursor() override
std::vector< std::byte > m_key_prefix
BerkeleyCursor(BerkeleyDatabase &database, const BerkeleyBatch &batch, Span< const std::byte > prefix={})
Status Next(DataStream &key, DataStream &value) override
An instance of this class represents one database.
bool Rewrite(const char *pszSkip=nullptr) override
Rewrite the entire database on disk, with the exception of key pszSkip if non-zero.
void Open() override
Open the database if it is not already opened.
bool Backup(const std::string &strDest) const override
Back up the entire database to a file.
void RemoveRef() override
Indicate that database user has stopped using the database and that it could be flushed or closed.
std::shared_ptr< BerkeleyEnvironment > env
Pointer to shared database environment.
void IncrementUpdateCounter() override
void ReloadDbEnv() override
void AddRef() override
Indicate that a new database user has begun using the database.
BerkeleyDatabase()=delete
std::unique_ptr< Db > m_db
Database pointer.
void Flush() override
Make sure all changes are flushed to database file.
bool PeriodicFlush() override
std::unique_ptr< DatabaseBatch > MakeBatch(bool flush_on_close=true) override
Make a BerkeleyBatch connected to this database.
bool Verify(bilingual_str &error)
Verifies the environment and database file.
~BerkeleyDatabase() override
void Close() override
Flush to the database file and close the database.
fs::path Directory() const
std::condition_variable_any m_db_in_use
void CloseDb(const fs::path &filename)
std::unique_ptr< DbEnv > dbenv
BerkeleyEnvironment()
Construct an in-memory mock Berkeley environment for testing.
void CheckpointLSN(const std::string &strFile)
void Flush(bool fShutdown)
DbTxn * TxnBegin(int flags)
bool Open(bilingual_str &error)
std::map< fs::path, std::reference_wrapper< BerkeleyDatabase > > m_databases
RAII class that automatically cleanses its data on destruction.
const void * get_data() const
uint32_t get_size() const
An instance of this class represents one database.
std::atomic< unsigned int > nUpdateCounter
std::atomic< int > m_refcount
Counts the number of active database users to be sure that the database is not closed while someone i...
void memory_cleanse(void *ptr, size_t len)
Secure overwrite a buffer (possibly containing secret data) with zero-bytes.
static const int CLIENT_VERSION
bitcoind-res.rc includes this file, but it cannot cope with real c++ code.
bool TryCreateDirectories(const fs::path &p)
Ignores exceptions thrown by create_directories if the requested directory exists.
void UnlockDirectory(const fs::path &directory, const fs::path &lockfile_name)
std::string HexStr(const Span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
#define LogDebug(category,...)
Filesystem operations and types.
static auto quoted(const std::string &s)
static bool exists(const path &p)
static bool copy_file(const path &from, const path &to, copy_options options)
static std::string PathToString(const path &path)
Convert path object to a byte string.
static path PathFromString(const std::string &string)
Convert byte string to path object.
FILE * fopen(const fs::path &p, const char *mode)
std::string get_filesystem_error_message(const fs::filesystem_error &e)
LockResult LockDirectory(const fs::path &directory, const fs::path &lockfile_name, bool probe_only)
std::unique_ptr< BerkeleyDatabase > MakeBerkeleyDatabase(const fs::path &path, const DatabaseOptions &options, DatabaseStatus &status, bilingual_str &error)
Return object giving access to Berkeley database at specified path.
static std::set< std::string > g_loading_wallet_set GUARDED_BY(g_loading_wallet_mutex)
std::string BerkeleyDatabaseVersion()
static constexpr auto REVERSE_BYTE_ORDER
fs::path BDBDataFile(const fs::path &wallet_path)
std::shared_ptr< BerkeleyEnvironment > GetBerkeleyEnv(const fs::path &env_directory, bool use_shared_memory)
Get BerkeleyEnvironment given a directory path.
bool BerkeleyDatabaseSanityCheck()
Perform sanity check of runtime BDB version versus linked BDB version.
static Span< const std::byte > SpanFromDbt(const SafeDbt &dbt)
bool verify
Check data integrity on load.
bool use_shared_memory
Let other processes access the database.
bool operator==(const WalletDatabaseFileId &rhs) const
uint8_t value[BDB_DB_FILE_ID_LEN]
#define AssertLockNotHeld(cs)
#define TRY_LOCK(cs, name)
void UninterruptibleSleep(const std::chrono::microseconds &n)
consteval auto _(util::TranslatedLiteral str)
bilingual_str Untranslated(std::string original)
Mark a bilingual_str as untranslated.