33 int ret = db.get_mpf()->get_fileid(fileid.
value);
35 throw std::runtime_error(
strprintf(
"BerkeleyDatabase: Can't open database %s (get_fileid failed with %d)", filename, ret));
39 if (fileid == item.second && &fileid != &item.second) {
40 throw std::runtime_error(
strprintf(
"BerkeleyDatabase: Can't open database %s (duplicates fileid %s from %s)", filename,
41 HexStr(item.second.value), item.first));
47 std::map<std::string, std::weak_ptr<BerkeleyEnvironment>> g_dbenvs
GUARDED_BY(cs_db);
61 std::shared_ptr<BerkeleyEnvironment>
GetBerkeleyEnv(
const fs::path& env_directory)
64 auto inserted = g_dbenvs.emplace(env_directory.string(), std::weak_ptr<BerkeleyEnvironment>());
65 if (inserted.second) {
66 auto env = std::make_shared<BerkeleyEnvironment>(env_directory.string());
67 inserted.first->second = env;
70 return inserted.first->second.lock();
88 database.
m_db->close(0);
89 database.
m_db.reset();
93 FILE* error_file =
nullptr;
94 dbenv->get_errfile(&error_file);
96 int ret =
dbenv->close(0);
98 LogPrintf(
"BerkeleyEnvironment::Close: Error %d closing database environment: %s\n", ret, DbEnv::strerror(ret));
100 DbEnv((u_int32_t)0).remove(
strPath.c_str(), 0);
102 if (error_file) fclose(error_file);
109 dbenv.reset(
new DbEnv(DB_CXX_NO_EXCEPTIONS));
135 LogPrintf(
"Cannot obtain a lock on wallet directory %s. Another instance of bitcoin may be using it.\n",
strPath);
140 fs::path pathLogDir = pathIn /
"database";
142 fs::path pathErrorFile = pathIn /
"db.log";
143 LogPrintf(
"BerkeleyEnvironment::Open: LogDir=%s ErrorFile=%s\n", pathLogDir.string(), pathErrorFile.string());
145 unsigned int nEnvFlags = 0;
147 nEnvFlags |= DB_PRIVATE;
149 dbenv->set_lg_dir(pathLogDir.string().c_str());
150 dbenv->set_cachesize(0, 0x100000, 1);
151 dbenv->set_lg_bsize(0x10000);
152 dbenv->set_lg_max(1048576);
153 dbenv->set_lk_max_locks(40000);
154 dbenv->set_lk_max_objects(40000);
156 dbenv->set_flags(DB_AUTO_COMMIT, 1);
157 dbenv->set_flags(DB_TXN_WRITE_NOSYNC, 1);
158 dbenv->log_set_config(DB_LOG_AUTO_REMOVE, 1);
170 LogPrintf(
"BerkeleyEnvironment::Open: Error %d opening database environment: %s\n", ret, DbEnv::strerror(ret));
171 int ret2 =
dbenv->close(0);
173 LogPrintf(
"BerkeleyEnvironment::Open: Error %d closing failed database environment: %s\n", ret2, DbEnv::strerror(ret2));
177 if (ret == DB_RUNRECOVERY) {
178 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");
195 dbenv->set_cachesize(1, 0, 1);
196 dbenv->set_lg_bsize(10485760 * 4);
197 dbenv->set_lg_max(10485760);
198 dbenv->set_lk_max_locks(10000);
199 dbenv->set_lk_max_objects(10000);
200 dbenv->set_flags(DB_AUTO_COMMIT, 1);
201 dbenv->log_set_config(DB_LOG_IN_MEMORY, 1);
202 int ret =
dbenv->open(
nullptr,
212 throw std::runtime_error(
strprintf(
"BerkeleyEnvironment::MakeMock: Error %d opening database environment.", ret));
221 m_dbt.set_flags(DB_DBT_MALLOC);
231 if (m_dbt.get_data() !=
nullptr) {
237 if (m_dbt.get_flags() & DB_DBT_MALLOC) {
238 free(m_dbt.get_data());
245 return m_dbt.get_data();
250 return m_dbt.get_size();
253 BerkeleyBatch::SafeDbt::operator Dbt*()
261 fs::path file_path = walletDir /
strFile;
264 LogPrintf(
"Using wallet %s\n", file_path.string());
270 if (fs::exists(file_path))
275 int result = db.verify(
strFile.c_str(),
nullptr,
nullptr, 0);
277 errorStr =
strprintf(
_(
"%s corrupt. Try using the wallet tool bitcoin-wallet to salvage or restoring a backup."), file_path);
287 dbenv->txn_checkpoint(0, 0, 0);
290 dbenv->lsn_reset(
strFile.c_str(), 0);
314 if (!
Exists(std::string(
"version"))) {
324 unsigned int nFlags = DB_THREAD | DB_CREATE;
329 if (!
env->Open(open_err))
330 throw std::runtime_error(
"BerkeleyDatabase: Failed to open database environment.");
332 if (
m_db ==
nullptr) {
334 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));
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;
439 for (
const std::string& filename :
filenames) {
460 bool fSuccess =
true;
462 std::string strFileRes =
strFile +
".rewrite";
465 std::unique_ptr<Db> pdbCopy = std::make_unique<Db>(
env->dbenv.get(), 0);
467 int ret = pdbCopy->open(
nullptr,
474 LogPrintf(
"BerkeleyBatch::Rewrite: Can't create database file %s\n", strFileRes);
491 strncmp((
const char*)ssKey.
data(), pszSkip, std::min(ssKey.
size(), strlen(pszSkip))) == 0)
493 if (strncmp((
const char*)ssKey.
data(),
"\x07version", 8) == 0) {
498 Dbt datKey(ssKey.
data(), ssKey.
size());
499 Dbt datValue(ssValue.
data(), ssValue.
size());
500 int ret2 = pdbCopy->put(
nullptr, &datKey, &datValue, DB_NOOVERWRITE);
509 if (pdbCopy->close(0))
516 Db dbA(
env->dbenv.get(), 0);
517 if (dbA.remove(
strFile.c_str(),
nullptr, 0))
519 Db dbB(
env->dbenv.get(), 0);
520 if (dbB.rename(strFileRes.c_str(),
nullptr,
strFile.c_str(), 0))
524 LogPrintf(
"BerkeleyBatch::Rewrite: Failed to rewrite database file %s\n", strFileRes);
542 bool no_dbs_accessed =
true;
544 std::string strFile = db_it.first;
545 int nRefCount = db_it.second.get().m_refcount;
546 if (nRefCount < 0)
continue;
548 if (nRefCount == 0) {
552 dbenv->txn_checkpoint(0, 0, 0);
555 dbenv->lsn_reset(strFile.c_str(), 0);
559 no_dbs_accessed =
false;
565 if (no_dbs_accessed) {
566 dbenv->log_archive(&listp, DB_ARCH_REMOVE);
569 fs::remove_all(fs::path(
strPath) /
"database");
580 if (!lockDb)
return false;
583 for (
auto& it :
env->m_databases) {
584 if (it.second.get().m_refcount > 0)
return false;
616 fs::path pathSrc =
env->Directory() /
strFile;
617 fs::path pathDest(strDest);
618 if (fs::is_directory(pathDest))
622 if (fs::equivalent(pathSrc, pathDest)) {
623 LogPrintf(
"cannot backup to wallet source file %s\n", pathDest.string());
627 fs::copy_file(pathSrc, pathDest, fs::copy_option::overwrite_if_exists);
630 }
catch (
const fs::filesystem_error& e) {
667 if (
m_cursor ==
nullptr)
return false;
671 int ret =
m_cursor->get(datKey, datValue, DB_NEXT);
672 if (ret == DB_NOTFOUND) {
729 DbEnv::version(&major, &minor,
nullptr);
734 if (major != DB_VERSION_MAJOR || minor < DB_VERSION_MINOR) {
735 LogPrintf(
"BerkeleyDB database version conflict: header version is %d.%d, library version is %d.%d\n",
736 DB_VERSION_MAJOR, DB_VERSION_MINOR, major, minor);
745 return DbEnv::version(
nullptr,
nullptr,
nullptr);
753 SafeDbt datKey(key.data(), key.size());
757 if (ret == 0 && datValue.get_data() !=
nullptr) {
758 value.
write((
char*)datValue.get_data(), datValue.get_size());
769 assert(!
"Write called on database in read-only mode");
771 SafeDbt datKey(key.data(), key.size());
773 SafeDbt datValue(value.data(), value.size());
775 int ret =
pdb->put(
activeTxn, datKey, datValue, (overwrite ? 0 : DB_NOOVERWRITE));
784 assert(!
"Erase called on database in read-only mode");
786 SafeDbt datKey(key.data(), key.size());
789 return (ret == 0 || ret == DB_NOTFOUND);
797 SafeDbt datKey(key.data(), key.size());
817 if (
env)
env->m_db_in_use.notify_all();
822 return std::make_unique<BerkeleyBatch>(*
this,
false, flush_on_close);
828 std::unique_ptr<BerkeleyDatabase> db;
831 std::string data_filename = data_file.filename().string();
832 std::shared_ptr<BerkeleyEnvironment> env =
GetBerkeleyEnv(data_file.parent_path());
833 if (env->m_databases.count(data_filename)) {
834 error =
Untranslated(
strprintf(
"Refusing to load database. Data file '%s' is already loaded.", (env->Directory() / data_filename).
string()));
838 db = std::make_unique<BerkeleyDatabase>(std::move(env), std::move(data_filename));
std::unique_ptr< DatabaseBatch > MakeBatch(bool flush_on_close=true) override
Make a BerkeleyBatch connected to this database.
void ReloadDbEnv() override
void Open() override
Open the database if it is not already opened.
BerkeleyEnvironment()
Construct an in-memory mock Berkeley environment for testing.
bool StartCursor() override
fs::path BDBDataFile(const fs::path &wallet_path)
#define LogPrint(category,...)
FILE * fopen(const fs::path &p, const char *mode)
#define TRY_LOCK(cs, name)
std::unordered_map< std::string, WalletDatabaseFileId > m_fileids
int64_t GetTimeMillis()
Returns the system time (not mockable)
bilingual_str Untranslated(std::string original)
Mark a bilingual_str as untranslated.
BerkeleyDatabase & m_database
bool TryCreateDirectories(const fs::path &p)
Ignores exceptions thrown by Boost's create_directories if the requested directory exists...
BerkeleyBatch(BerkeleyDatabase &database, const bool fReadOnly, bool fFlushOnCloseIn=true)
~BerkeleyBatch() override
An instance of this class represents one database.
void Flush() override
Make sure all changes are flushed to database file.
static const unsigned int DEFAULT_WALLET_DBLOGSIZE
Double ended buffer combining vector and stream-like interfaces.
void write(const char *pch, size_t nSize)
bool GetBoolArg(const std::string &strArg, bool fDefault) const
Return boolean argument or default value.
static const bool DEFAULT_WALLET_PRIVDB
static const char * filenames[]
fs::path Directory() const
u_int32_t get_size() const
DbTxn * TxnBegin(int flags=DB_TXN_WRITE_NOSYNC)
std::shared_ptr< BerkeleyEnvironment > env
Pointer to shared database environment.
bool Rewrite(const char *pszSkip=nullptr) override
Rewrite the entire database on disk, with the exception of key pszSkip if non-zero.
bool Write(const K &key, const T &value, bool fOverwrite=true)
std::map< std::string, std::reference_wrapper< BerkeleyDatabase > > m_databases
std::unique_ptr< DbEnv > dbenv
bool Backup(const std::string &strDest) const override
Back up the entire database to a file.
void CheckpointLSN(const std::string &strFile)
bool LockDirectory(const fs::path &directory, const std::string lockfile_name, bool probe_only)
void memory_cleanse(void *ptr, size_t len)
Secure overwrite a buffer (possibly containing secret data) with zero-bytes.
bool WriteKey(CDataStream &&key, CDataStream &&value, bool overwrite=true) override
void RemoveRef() override
Indicate that database user has stopped using the database and that it could be flushed or closed...
bilingual_str _(const char *psz)
Translation function.
bool HasKey(CDataStream &&key) override
std::string BerkeleyDatabaseVersion()
RAII class that provides access to a Berkeley database.
bool PeriodicFlush() override
bool operator==(const WalletDatabaseFileId &rhs) const
std::string HexStr(const Span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
~BerkeleyDatabase() override
bool EraseKey(CDataStream &&key) override
bool ReadAtCursor(CDataStream &ssKey, CDataStream &ssValue, bool &complete) override
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::string get_filesystem_error_message(const fs::filesystem_error &e)
void AddRef() override
Indicate the a new database user has began using the database.
void UninterruptibleSleep(const std::chrono::microseconds &n)
std::atomic< unsigned int > nUpdateCounter
void IncrementUpdateCounter() override
void UnlockDirectory(const fs::path &directory, const std::string &lockfile_name)
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
void Close() override
Flush to the database file and close the database.
BerkeleyEnvironment * env
u_int8_t value[DB_FILE_ID_LEN]
void Flush(bool fShutdown)
#define AssertLockNotHeld(cs)
bool Open(bilingual_str &error)
void CloseCursor() override
void CloseDb(const std::string &strFile)
bool Exists(const K &key)
static const int CLIENT_VERSION
bitcoind-res.rc includes this file, but it cannot cope with real c++ code.
std::atomic< int > m_refcount
Counts the number of active database users to be sure that the database is not closed while someone i...
bool BerkeleyDatabaseSanityCheck()
Perform sanity check of runtime BDB version versus linked BDB version.
std::unique_ptr< Db > m_db
Database pointer.
bool error(const char *fmt, const Args &... args)
bool Verify(bilingual_str &error)
Verifies the environment and database file.
bool ReadKey(CDataStream &&key, CDataStream &value) override
std::shared_ptr< BerkeleyEnvironment > GetBerkeleyEnv(const fs::path &env_directory)
Get BerkeleyEnvironment given a directory path.
const void * get_data() const
bool TxnCommit() override
RAII class that automatically cleanses its data on destruction.
std::condition_variable_any m_db_in_use