32 void CheckUniqueFileid(
const BerkeleyEnvironment& env,
const std::string& filename, Db& db, WalletDatabaseFileId& fileid)
34 if (env.IsMock())
return;
36 int ret = db.get_mpf()->get_fileid(fileid.value);
38 throw std::runtime_error(
strprintf(
"BerkeleyDatabase: Can't open database %s (get_fileid failed with %d)", filename,
ret));
41 for (
const auto& item : env.m_fileids) {
42 if (fileid == item.second && &fileid != &item.second) {
43 throw std::runtime_error(
strprintf(
"BerkeleyDatabase: Can't open database %s (duplicates fileid %s from %s)", filename,
44 HexStr(item.second.value), item.first));
50 std::map<std::string, std::weak_ptr<BerkeleyEnvironment>> g_dbenvs
GUARDED_BY(cs_db);
67 auto inserted = g_dbenvs.emplace(
fs::PathToString(env_directory), std::weak_ptr<BerkeleyEnvironment>());
68 if (inserted.second) {
69 auto env = std::make_shared<BerkeleyEnvironment>(env_directory, use_shared_memory);
70 inserted.first->second = env;
73 return inserted.first->second.lock();
91 database.
m_db->close(0);
92 database.
m_db.reset();
96 FILE* error_file =
nullptr;
97 dbenv->get_errfile(&error_file);
101 LogPrintf(
"BerkeleyEnvironment::Close: Error %d closing database environment: %s\n",
ret, DbEnv::strerror(
ret));
103 DbEnv((uint32_t)0).remove(
strPath.c_str(), 0);
105 if (error_file) fclose(error_file);
112 dbenv.reset(
new DbEnv(DB_CXX_NO_EXCEPTIONS));
138 LogPrintf(
"Cannot obtain a lock on wallet directory %s. Another instance may be using it.\n",
strPath);
143 fs::path pathLogDir = pathIn /
"database";
145 fs::path pathErrorFile = pathIn /
"db.log";
148 unsigned int nEnvFlags = 0;
150 nEnvFlags |= DB_PRIVATE;
154 dbenv->set_cachesize(0, 0x100000, 1);
155 dbenv->set_lg_bsize(0x10000);
156 dbenv->set_lg_max(1048576);
157 dbenv->set_lk_max_locks(40000);
158 dbenv->set_lk_max_objects(40000);
160 dbenv->set_flags(DB_AUTO_COMMIT, 1);
161 dbenv->set_flags(DB_TXN_WRITE_NOSYNC, 1);
162 dbenv->log_set_config(DB_LOG_AUTO_REMOVE, 1);
174 LogPrintf(
"BerkeleyEnvironment::Open: Error %d opening database environment: %s\n",
ret, DbEnv::strerror(
ret));
175 int ret2 =
dbenv->close(0);
177 LogPrintf(
"BerkeleyEnvironment::Open: Error %d closing failed database environment: %s\n", ret2, DbEnv::strerror(ret2));
181 if (
ret == DB_RUNRECOVERY) {
182 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");
199 dbenv->set_cachesize(1, 0, 1);
200 dbenv->set_lg_bsize(10485760 * 4);
201 dbenv->set_lg_max(10485760);
202 dbenv->set_lk_max_locks(10000);
203 dbenv->set_lk_max_objects(10000);
204 dbenv->set_flags(DB_AUTO_COMMIT, 1);
205 dbenv->log_set_config(DB_LOG_IN_MEMORY, 1);
216 throw std::runtime_error(
strprintf(
"BerkeleyEnvironment::MakeMock: Error %d opening database environment.",
ret));
225 m_dbt.set_flags(DB_DBT_MALLOC);
235 if (m_dbt.get_data() !=
nullptr) {
241 if (m_dbt.get_flags() & DB_DBT_MALLOC) {
242 free(m_dbt.get_data());
249 return m_dbt.get_data();
254 return m_dbt.get_size();
257 BerkeleyBatch::SafeDbt::operator Dbt*()
265 fs::path file_path = walletDir / m_filename;
280 int result = db.verify(
strFile.c_str(),
nullptr,
nullptr, 0);
292 dbenv->txn_checkpoint(0, 0, 0);
295 dbenv->lsn_reset(
strFile.c_str(), 0);
323 unsigned int nFlags = DB_THREAD | DB_CREATE;
328 if (!
env->Open(open_err))
329 throw std::runtime_error(
"BerkeleyDatabase: Failed to open database environment.");
331 if (
m_db ==
nullptr) {
333 std::unique_ptr<Db> pdb_temp = std::make_unique<Db>(
env->dbenv.get(), 0);
336 bool fMockDb =
env->IsMock();
338 DbMpoolFile* mpf = pdb_temp->get_mpf();
339 ret = mpf->set_flags(DB_MPOOL_NOFILE, 1);
341 throw std::runtime_error(
strprintf(
"BerkeleyDatabase: Failed to configure for no temp file backing for database %s", strFile));
345 ret = pdb_temp->open(
nullptr,
346 fMockDb ?
nullptr : strFile.c_str(),
347 fMockDb ? strFile.c_str() :
"main",
353 throw std::runtime_error(
strprintf(
"BerkeleyDatabase: Error %d, can't open database %s",
ret, strFile));
359 CheckUniqueFileid(*
env, strFile, *pdb_temp, this->env->m_fileids[strFile]);
361 m_db.reset(pdb_temp.release());
373 unsigned int nMinutes = 0;
416 database.
m_db->close(0);
417 database.m_db.reset();
426 std::unique_lock<RecursiveMutex> lock(cs_db);
429 if (db.second.get().m_refcount > 0)
return false;
458 env->CheckpointLSN(strFile);
461 bool fSuccess =
true;
462 LogPrintf(
"BerkeleyBatch::Rewrite: Rewriting %s...\n", strFile);
463 std::string strFileRes = strFile +
".rewrite";
466 std::unique_ptr<Db> pdbCopy = std::make_unique<Db>(
env->dbenv.get(), 0);
468 int ret = pdbCopy->open(
nullptr,
475 LogPrintf(
"BerkeleyBatch::Rewrite: Can't create database file %s\n", strFileRes);
492 strncmp((
const char*)ssKey.
data(), pszSkip, std::min(ssKey.
size(), strlen(pszSkip))) == 0)
494 if (strncmp((
const char*)ssKey.
data(),
"\x07version", 8) == 0) {
499 Dbt datKey(ssKey.
data(), ssKey.
size());
500 Dbt datValue(ssValue.
data(), ssValue.
size());
501 int ret2 = pdbCopy->put(
nullptr, &datKey, &datValue, DB_NOOVERWRITE);
510 if (pdbCopy->close(0))
517 Db dbA(
env->dbenv.get(), 0);
518 if (dbA.remove(strFile.c_str(),
nullptr, 0))
520 Db dbB(
env->dbenv.get(), 0);
521 if (dbB.rename(strFileRes.c_str(),
nullptr, strFile.c_str(), 0))
525 LogPrintf(
"BerkeleyBatch::Rewrite: Failed to rewrite database file %s\n", strFileRes);
536 const auto start{SteadyClock::now()};
543 bool no_dbs_accessed =
true;
545 const fs::path& filename = db_it.first;
546 int nRefCount = db_it.second.get().m_refcount;
547 if (nRefCount < 0)
continue;
550 if (nRefCount == 0) {
554 dbenv->txn_checkpoint(0, 0, 0);
557 dbenv->lsn_reset(strFile.c_str(), 0);
561 no_dbs_accessed =
false;
564 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));
567 if (no_dbs_accessed) {
568 dbenv->log_archive(&listp, DB_ARCH_REMOVE);
582 if (!lockDb)
return false;
585 for (
auto& it :
env->m_databases) {
586 if (it.second.get().m_refcount > 0)
return false;
594 const auto start{SteadyClock::now()};
598 env->CheckpointLSN(strFile);
601 LogPrint(
BCLog::WALLETDB,
"Flushed %s %dms\n", strFile, Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
617 env->CheckpointLSN(strFile);
622 if (fs::is_directory(pathDest))
626 if (
fs::exists(pathDest) && fs::equivalent(pathSrc, pathDest)) {
631 fs::copy_file(pathSrc, pathDest, fs::copy_options::overwrite_existing);
634 }
catch (
const fs::filesystem_error& e) {
671 if (
m_cursor ==
nullptr)
return false;
675 int ret =
m_cursor->get(datKey, datValue, DB_NEXT);
676 if (
ret == DB_NOTFOUND) {
733 DbEnv::version(&major, &minor,
nullptr);
738 if (major != DB_VERSION_MAJOR || minor < DB_VERSION_MINOR) {
739 LogPrintf(
"BerkeleyDB database version conflict: header version is %d.%d, library version is %d.%d\n",
740 DB_VERSION_MAJOR, DB_VERSION_MINOR, major, minor);
749 return DbEnv::version(
nullptr,
nullptr,
nullptr);
757 SafeDbt datKey(key.data(), key.size());
761 if (
ret == 0 && datValue.get_data() !=
nullptr) {
762 value.
write({
AsBytePtr(datValue.get_data()), datValue.get_size()});
773 assert(!
"Write called on database in read-only mode");
775 SafeDbt datKey(key.data(), key.size());
777 SafeDbt datValue(value.data(), value.size());
779 int ret =
pdb->put(
activeTxn, datKey, datValue, (overwrite ? 0 : DB_NOOVERWRITE));
788 assert(!
"Erase called on database in read-only mode");
790 SafeDbt datKey(key.data(), key.size());
793 return (
ret == 0 ||
ret == DB_NOTFOUND);
801 SafeDbt datKey(key.data(), key.size());
821 if (
env)
env->m_db_in_use.notify_all();
826 return std::make_unique<BerkeleyBatch>(*
this,
false, flush_on_close);
832 std::unique_ptr<BerkeleyDatabase> db;
837 if (env->m_databases.count(data_filename)) {
842 db = std::make_unique<BerkeleyDatabase>(std::move(env), std::move(data_filename), options);
bool EraseKey(CDataStream &&key) override
static path PathFromString(const std::string &string)
Convert byte string to path object.
BerkeleyBatch(BerkeleyDatabase &database, const bool fReadOnly, bool fFlushOnCloseIn=true)
fs::path Directory() const
void RemoveRef() override
Indicate that database user has stopped using the database and that it could be flushed or closed...
bool BerkeleyDatabaseSanityCheck()
Perform sanity check of runtime BDB version versus linked BDB version.
#define LogPrint(category,...)
FILE * fopen(const fs::path &p, const char *mode)
std::unique_ptr< DbEnv > dbenv
#define TRY_LOCK(cs, name)
bool TxnCommit() override
void CloseDb(const fs::path &filename)
bool ReadKey(CDataStream &&key, CDataStream &value) override
std::map< fs::path, std::reference_wrapper< BerkeleyDatabase > > m_databases
uint8_t value[DB_FILE_ID_LEN]
void AddRef() override
Indicate that a new database user has begun using the database.
std::unique_ptr< Db > m_db
Database pointer.
static int g_sqlite_count GUARDED_BY(g_sqlite_mutex)=0
bilingual_str Untranslated(std::string original)
Mark a bilingual_str as untranslated.
void Close() override
Flush to the database file and close the database.
void CheckpointLSN(const std::string &strFile)
void Flush(bool fShutdown)
bool Open(bilingual_str &error)
void IncrementUpdateCounter() override
std::unordered_map< std::string, WalletDatabaseFileId > m_fileids
std::condition_variable_any m_db_in_use
Double ended buffer combining vector and stream-like interfaces.
static const char * filenames[]
void write(Span< const value_type > src)
std::atomic< unsigned int > nUpdateCounter
std::shared_ptr< BerkeleyEnvironment > GetBerkeleyEnv(const fs::path &env_directory, bool use_shared_memory)
Get BerkeleyEnvironment given a directory path.
Filesystem operations and types.
bool operator==(const WalletDatabaseFileId &rhs) const
BerkeleyDatabase & m_database
bool Backup(const std::string &strDest) const override
Back up the entire database to a file.
DbTxn * TxnBegin(int flags=DB_TXN_WRITE_NOSYNC)
bool PeriodicFlush() override
bool Verify(bilingual_str &error)
Verifies the environment and database file.
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.
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 std::string PathToString(const path &path)
Convert path object to a byte string.
std::string HexStr(const Span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
const std::byte * AsBytePtr(const void *data)
Convert a data pointer to a std::byte data pointer.
void Flush() override
Make sure all changes are flushed to database file.
bilingual_str _(const char *psz)
Translation function.
~BerkeleyBatch() override
bool TryCreateDirectories(const fs::path &p)
Ignores exceptions thrown by create_directories if the requested directory exists.
const void * get_data() const
std::string BerkeleyDatabaseVersion()
std::unique_ptr< DatabaseBatch > MakeBatch(bool flush_on_close=true) override
Make a BerkeleyBatch connected to this database.
bool LockDirectory(const fs::path &directory, const fs::path &lockfile_name, bool probe_only)
bool use_shared_memory
Let other processes access the database.
An instance of this class represents one database.
RAII class that automatically cleanses its data on destruction.
void ReloadDbEnv() override
std::string get_filesystem_error_message(const fs::filesystem_error &e)
void CloseCursor() override
void UninterruptibleSleep(const std::chrono::microseconds &n)
void UnlockDirectory(const fs::path &directory, const fs::path &lockfile_name)
void Open() override
Open the database if it is not already opened.
fs::path BDBDataFile(const fs::path &wallet_path)
bool Rewrite(const char *pszSkip=nullptr) override
Rewrite the entire database on disk, with the exception of key pszSkip if non-zero.
#define AssertLockNotHeld(cs)
RAII class that provides access to a Berkeley database.
bool ReadAtCursor(CDataStream &ssKey, CDataStream &ssValue, bool &complete) override
static auto quoted(const std::string &s)
BerkeleyEnvironment * env
bool HasKey(CDataStream &&key) override
static bool exists(const path &p)
std::shared_ptr< BerkeleyEnvironment > env
Pointer to shared database environment.
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
bool verify
Check data integrity on load.
static const int CLIENT_VERSION
bitcoind-res.rc includes this file, but it cannot cope with real c++ code.
bool error(const char *fmt, const Args &... args)
uint32_t get_size() const
~BerkeleyDatabase() override
bool StartCursor() override
static bool copy_file(const path &from, const path &to, copy_options options)
bool WriteKey(CDataStream &&key, CDataStream &&value, bool overwrite=true) override
BerkeleyEnvironment()
Construct an in-memory mock Berkeley environment for testing.