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);
62 std::shared_ptr<BerkeleyEnvironment>
GetWalletEnv(
const fs::path& wallet_path, std::string& database_filename)
64 fs::path env_directory;
67 auto inserted = g_dbenvs.emplace(env_directory.string(), std::weak_ptr<BerkeleyEnvironment>());
68 if (inserted.second) {
69 auto env = std::make_shared<BerkeleyEnvironment>(env_directory.string());
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);
99 int ret =
dbenv->close(0);
101 LogPrintf(
"BerkeleyEnvironment::Close: Error %d closing database environment: %s\n", ret, DbEnv::strerror(ret));
103 DbEnv((u_int32_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 of bitcoin may be using it.\n",
strPath);
143 fs::path pathLogDir = pathIn /
"database";
145 fs::path pathErrorFile = pathIn /
"db.log";
146 LogPrintf(
"BerkeleyEnvironment::Open: LogDir=%s ErrorFile=%s\n", pathLogDir.string(), pathErrorFile.string());
148 unsigned int nEnvFlags = 0;
150 nEnvFlags |= DB_PRIVATE;
152 dbenv->set_lg_dir(pathLogDir.string().c_str());
153 dbenv->set_cachesize(0, 0x100000, 1);
154 dbenv->set_lg_bsize(0x10000);
155 dbenv->set_lg_max(1048576);
156 dbenv->set_lk_max_locks(40000);
157 dbenv->set_lk_max_objects(40000);
159 dbenv->set_flags(DB_AUTO_COMMIT, 1);
160 dbenv->set_flags(DB_TXN_WRITE_NOSYNC, 1);
161 dbenv->log_set_config(DB_LOG_AUTO_REMOVE, 1);
173 LogPrintf(
"BerkeleyEnvironment::Open: Error %d opening database environment: %s\n", ret, DbEnv::strerror(ret));
174 int ret2 =
dbenv->close(0);
176 LogPrintf(
"BerkeleyEnvironment::Open: Error %d closing failed database environment: %s\n", ret2, DbEnv::strerror(ret2));
180 if (ret == DB_RUNRECOVERY) {
181 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 dbenv->set_cachesize(1, 0, 1);
199 dbenv->set_lg_bsize(10485760 * 4);
200 dbenv->set_lg_max(10485760);
201 dbenv->set_lk_max_locks(10000);
202 dbenv->set_lk_max_objects(10000);
203 dbenv->set_flags(DB_AUTO_COMMIT, 1);
204 dbenv->log_set_config(DB_LOG_IN_MEMORY, 1);
205 int ret =
dbenv->open(
nullptr,
215 throw std::runtime_error(
strprintf(
"BerkeleyEnvironment::MakeMock: Error %d opening database environment.", ret));
224 m_dbt.set_flags(DB_DBT_MALLOC);
234 if (m_dbt.get_data() !=
nullptr) {
240 if (m_dbt.get_flags() & DB_DBT_MALLOC) {
241 free(m_dbt.get_data());
248 return m_dbt.get_data();
253 return m_dbt.get_size();
256 BerkeleyBatch::SafeDbt::operator Dbt*()
264 fs::path file_path = walletDir /
strFile;
267 LogPrintf(
"Using wallet %s\n", file_path.string());
269 if (!env->
Open(errorStr)) {
273 if (fs::exists(file_path))
275 assert(m_refcount == 0);
277 Db db(env->
dbenv.get(), 0);
278 int result = db.verify(
strFile.c_str(),
nullptr,
nullptr, 0);
280 errorStr =
strprintf(
_(
"%s corrupt. Try using the wallet tool bitcoin-wallet to salvage or restoring a backup."), file_path);
290 dbenv->txn_checkpoint(0, 0, 0);
293 dbenv->lsn_reset(strFile.c_str(), 0);
314 env = database.
env.get();
317 if (!
Exists(std::string(
"version"))) {
327 unsigned int nFlags = DB_THREAD | DB_CREATE;
332 if (!env->
Open(open_err))
333 throw std::runtime_error(
"BerkeleyDatabase: Failed to open database environment.");
335 if (
m_db ==
nullptr) {
337 std::unique_ptr<Db> pdb_temp = MakeUnique<Db>(env->
dbenv.get(), 0);
339 bool fMockDb = env->
IsMock();
341 DbMpoolFile* mpf = pdb_temp->get_mpf();
342 ret = mpf->set_flags(DB_MPOOL_NOFILE, 1);
344 throw std::runtime_error(
strprintf(
"BerkeleyDatabase: Failed to configure for no temp file backing for database %s",
strFile));
348 ret = pdb_temp->open(
nullptr,
349 fMockDb ?
nullptr :
strFile.c_str(),
350 fMockDb ?
strFile.c_str() :
"main",
356 throw std::runtime_error(
strprintf(
"BerkeleyDatabase: Error %d, can't open database %s", ret,
strFile));
364 m_db.reset(pdb_temp.release());
376 unsigned int nMinutes = 0;
419 database.m_db->close(0);
420 database.m_db.reset();
429 std::unique_lock<RecursiveMutex> lock(cs_db);
432 if (db.second.get().m_refcount > 0)
return false;
439 filenames.push_back(
it.first);
442 for (
const std::string& filename : filenames) {
463 bool fSuccess =
true;
465 std::string strFileRes =
strFile +
".rewrite";
468 std::unique_ptr<Db> pdbCopy = MakeUnique<Db>(env->
dbenv.get(), 0);
470 int ret = pdbCopy->open(
nullptr,
477 LogPrintf(
"BerkeleyBatch::Rewrite: Can't create database file %s\n", strFileRes);
494 strncmp(ssKey.
data(), pszSkip, std::min(ssKey.
size(), strlen(pszSkip))) == 0)
496 if (strncmp(ssKey.
data(),
"\x07version", 8) == 0) {
501 Dbt datKey(ssKey.
data(), ssKey.
size());
502 Dbt datValue(ssValue.
data(), ssValue.
size());
503 int ret2 = pdbCopy->put(
nullptr, &datKey, &datValue, DB_NOOVERWRITE);
512 if (pdbCopy->close(0))
519 Db dbA(env->
dbenv.get(), 0);
520 if (dbA.remove(
strFile.c_str(),
nullptr, 0))
522 Db dbB(env->
dbenv.get(), 0);
523 if (dbB.rename(strFileRes.c_str(),
nullptr,
strFile.c_str(), 0))
527 LogPrintf(
"BerkeleyBatch::Rewrite: Failed to rewrite database file %s\n", strFileRes);
545 bool no_dbs_accessed =
true;
547 std::string strFile = db_it.first;
548 int nRefCount = db_it.second.get().m_refcount;
549 if (nRefCount < 0)
continue;
551 if (nRefCount == 0) {
555 dbenv->txn_checkpoint(0, 0, 0);
558 dbenv->lsn_reset(strFile.c_str(), 0);
562 no_dbs_accessed =
false;
568 if (no_dbs_accessed) {
569 dbenv->log_archive(&listp, DB_ARCH_REMOVE);
572 fs::remove_all(fs::path(
strPath) /
"database");
583 if (!lockDb)
return false;
587 if (
it.second.get().m_refcount > 0)
return false;
620 fs::path pathDest(strDest);
621 if (fs::is_directory(pathDest))
625 if (fs::equivalent(pathSrc, pathDest)) {
626 LogPrintf(
"cannot backup to wallet source file %s\n", pathDest.string());
630 fs::copy_file(pathSrc, pathDest, fs::copy_option::overwrite_if_exists);
633 }
catch (
const fs::filesystem_error& e) {
670 if (
m_cursor ==
nullptr)
return false;
674 int ret =
m_cursor->get(datKey, datValue, DB_NEXT);
675 if (ret == DB_NOTFOUND) {
731 return DbEnv::version(
nullptr,
nullptr,
nullptr);
739 SafeDbt datKey(key.data(), key.size());
743 if (ret == 0 && datValue.get_data() !=
nullptr) {
744 value.
write((
char*)datValue.get_data(), datValue.get_size());
755 assert(!
"Write called on database in read-only mode");
757 SafeDbt datKey(key.data(), key.size());
759 SafeDbt datValue(value.data(), value.size());
761 int ret =
pdb->put(
activeTxn, datKey, datValue, (overwrite ? 0 : DB_NOOVERWRITE));
770 assert(!
"Erase called on database in read-only mode");
772 SafeDbt datKey(key.data(), key.size());
775 return (ret == 0 || ret == DB_NOTFOUND);
783 SafeDbt datKey(key.data(), key.size());
808 return MakeUnique<BerkeleyBatch>(*
this,
false, flush_on_close);
813 fs::path env_directory;
814 std::string data_filename;
816 return IsBDBFile(env_directory / data_filename);
821 std::unique_ptr<BerkeleyDatabase> db;
824 std::string data_filename;
825 std::shared_ptr<BerkeleyEnvironment> env =
GetWalletEnv(path, data_filename);
826 if (env->m_databases.count(data_filename)) {
827 error =
Untranslated(
strprintf(
"Refusing to load database. Data file '%s' is already loaded.", (env->Directory() / data_filename).
string()));
831 db = MakeUnique<BerkeleyDatabase>(std::move(env), std::move(data_filename));
834 if (options.
verify && !db->Verify(error)) {
845 if (!fs::exists(path))
return false;
849 boost::system::error_code ec;
850 auto size = fs::file_size(path, ec);
851 if (ec)
LogPrintf(
"%s: %s %s\n", __func__, ec.message(), path.string());
852 if (size < 4096)
return false;
855 if (!file.is_open())
return false;
857 file.seekg(12, std::ios::beg);
859 file.read((
char*) &data,
sizeof(data));
865 return data == 0x00053162 || data == 0x62310500;
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
#define LogPrint(category,...)
FILE * fopen(const fs::path &p, const char *mode)
std::shared_ptr< BerkeleyEnvironment > GetWalletEnv(const fs::path &wallet_path, std::string &database_filename)
Get BerkeleyEnvironment and database filename given a wallet path.
#define TRY_LOCK(cs, name)
std::unordered_map< std::string, WalletDatabaseFileId > m_fileids
bool GetBoolArg(const std::string &strArg, bool fDefault) const
Return boolean argument or default value.
int64_t GetTimeMillis()
Returns the system time (not mockable)
static void LogPrintf(const char *fmt, const Args &...args)
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)
static const bool DEFAULT_WALLET_PRIVDB
static const char * filenames[]
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 SplitWalletPath(const fs::path &wallet_path, fs::path &env_directory, std::string &database_filename)
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 error(const char *fmt, const Args &...args)
bool IsBDBFile(const fs::path &path)
Check format of database file.
bool WriteKey(CDataStream &&key, CDataStream &&value, bool overwrite=true) override
const void * get_data() const
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 ExistsBerkeleyDatabase(const fs::path &path)
Check if Berkeley database exists at specified path.
bool HasKey(CDataStream &&key) override
std::string BerkeleyDatabaseVersion()
RAII class that provides access to a Berkeley database.
bool PeriodicFlush() override
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
bool operator==(const WalletDatabaseFileId &rhs) const
void IncrementUpdateCounter() override
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
void UnlockDirectory(const fs::path &directory, const std::string &lockfile_name)
void Close() override
Flush to the database file and close the database.
u_int8_t value[DB_FILE_ID_LEN]
void Flush(bool fShutdown)
#define AssertLockNotHeld(cs)
bool Open(bilingual_str &error)
void CloseCursor() override
u_int32_t get_size() const
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...
std::unique_ptr< Db > m_db
Database pointer.
bool Verify(bilingual_str &error)
Verifies the environment and database file.
bool ReadKey(CDataStream &&key, CDataStream &value) override
bool TxnCommit() override
fs::path Directory() const
RAII class that automatically cleanses its data on destruction.
std::condition_variable_any m_db_in_use