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());
273 if (fs::exists(file_path))
275 assert(m_refcount == 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);
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;
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;
586 for (
auto&
it :
env->m_databases) {
587 if (
it.second.get().m_refcount > 0)
return false;
619 fs::path pathSrc =
env->Directory() /
strFile;
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) {
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());
803 if (
env)
env->m_db_in_use.notify_all();
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));
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< BerkeleyDatabase > MakeBerkeleyDatabase(const fs::path &path, const DatabaseOptions &options, DatabaseStatus &status, bilingual_str &error)
Return object giving access to Berkeley database at specified path.
bool ExistsBerkeleyDatabase(const fs::path &path)
Check if Berkeley database exists at specified path.
std::string BerkeleyDatabaseVersion()
bool IsBDBFile(const fs::path &path)
Check format of database file.
std::shared_ptr< BerkeleyEnvironment > GetWalletEnv(const fs::path &wallet_path, std::string &database_filename)
Get BerkeleyEnvironment and database filename given a wallet path.
static const unsigned int DEFAULT_WALLET_DBLOGSIZE
static const bool DEFAULT_WALLET_PRIVDB
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
bool GetBoolArg(const std::string &strArg, bool fDefault) const
Return boolean argument or default value.
RAII class that automatically cleanses its data on destruction.
u_int32_t get_size() const
const void * get_data() const
RAII class that provides access to a Berkeley database.
bool TxnCommit() override
bool WriteKey(CDataStream &&key, CDataStream &&value, bool overwrite=true) override
bool ReadKey(CDataStream &&key, CDataStream &value) override
bool StartCursor() override
void CloseCursor() override
BerkeleyBatch(BerkeleyDatabase &database, const bool fReadOnly, bool fFlushOnCloseIn=true)
BerkeleyEnvironment * env
bool EraseKey(CDataStream &&key) override
~BerkeleyBatch() override
BerkeleyDatabase & m_database
bool HasKey(CDataStream &&key) override
bool ReadAtCursor(CDataStream &ssKey, CDataStream &ssValue, bool &complete) override
An instance of this class represents one database.
std::shared_ptr< BerkeleyEnvironment > env
Pointer to shared database environment.
void IncrementUpdateCounter() override
void ReloadDbEnv() override
std::unique_ptr< DatabaseBatch > MakeBatch(bool flush_on_close=true) override
Make a BerkeleyBatch connected to this database.
~BerkeleyDatabase() override
bool Rewrite(const char *pszSkip=nullptr) override
Rewrite the entire database on disk, with the exception of key pszSkip if non-zero.
void AddRef() override
Indicate the a new database user has began using the database.
void Flush() override
Make sure all changes are flushed to database file.
void Open() override
Open the database if it is not already opened.
bool PeriodicFlush() override
void RemoveRef() override
Indicate that database user has stopped using the database and that it could be flushed or closed.
void Close() override
Flush to the database file and close the database.
std::unique_ptr< Db > m_db
Database pointer.
bool Verify(bilingual_str &error)
Verifies the environment and database file.
bool Backup(const std::string &strDest) const override
Back up the entire database to a file.
std::unordered_map< std::string, WalletDatabaseFileId > m_fileids
fs::path Directory() const
std::map< std::string, std::reference_wrapper< BerkeleyDatabase > > m_databases
bool Open(bilingual_str &error)
DbTxn * TxnBegin(int flags=DB_TXN_WRITE_NOSYNC)
std::unique_ptr< DbEnv > dbenv
std::condition_variable_any m_db_in_use
void CheckpointLSN(const std::string &strFile)
void Flush(bool fShutdown)
BerkeleyEnvironment()
Construct an in-memory mock Berkeley environment for testing.
void CloseDb(const std::string &strFile)
Double ended buffer combining vector and stream-like interfaces.
void write(const char *pch, size_t nSize)
bool Write(const K &key, const T &value, bool fOverwrite=true)
bool Exists(const K &key)
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.
void SplitWalletPath(const fs::path &wallet_path, fs::path &env_directory, std::string &database_filename)
static void LogPrintf(const char *fmt, const Args &... args)
#define LogPrint(category,...)
FILE * fopen(const fs::path &p, const char *mode)
std::string get_filesystem_error_message(const fs::filesystem_error &e)
std::deque< CInv >::iterator it
std::string HexStr(const Span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
u_int8_t value[DB_FILE_ID_LEN]
bool operator==(const WalletDatabaseFileId &rhs) const
#define AssertLockNotHeld(cs)
#define TRY_LOCK(cs, name)
bool LockDirectory(const fs::path &directory, const std::string lockfile_name, bool probe_only)
void UnlockDirectory(const fs::path &directory, const std::string &lockfile_name)
bool TryCreateDirectories(const fs::path &p)
Ignores exceptions thrown by Boost's create_directories if the requested directory exists.
bool error(const char *fmt, const Args &... args)
int64_t GetTimeMillis()
Returns the system time (not mockable)
void UninterruptibleSleep(const std::chrono::microseconds &n)
bilingual_str _(const char *psz)
Translation function.
bilingual_str Untranslated(std::string original)
Mark a bilingual_str as untranslated.
static const char * filenames[]