35 const std::string &filename, Db &
db,
41 int ret =
db.get_mpf()->get_fileid(fileid.
value);
43 throw std::runtime_error(
44 strprintf(
"BerkeleyDatabase: Can't open database %s (get_fileid "
50 if (fileid == item.second && &fileid != &item.second) {
51 throw std::runtime_error(
52 strprintf(
"BerkeleyDatabase: Can't open database %s "
53 "(duplicates fileid %s "
55 filename,
HexStr(item.second.value), item.first));
63std::map<std::string, std::weak_ptr<BerkeleyEnvironment>>
85std::shared_ptr<BerkeleyEnvironment>
91 std::weak_ptr<BerkeleyEnvironment>());
92 if (inserted.second) {
93 auto env = std::make_shared<BerkeleyEnvironment>(env_directory);
94 inserted.first->second = env;
97 return inserted.first->second.lock();
115 database.
m_db->close(0);
116 database.
m_db.reset();
120 FILE *error_file =
nullptr;
121 dbenv->get_errfile(&error_file);
123 int ret =
dbenv->close(0);
125 LogPrintf(
"BerkeleyEnvironment::Close: Error %d closing database "
127 ret, DbEnv::strerror(ret));
130 DbEnv(uint32_t(0)).remove(
strPath.c_str(), 0);
141 dbenv.reset(
new DbEnv(DB_CXX_NO_EXCEPTIONS));
166 LogPrintf(
"Cannot obtain a lock on wallet directory %s. Another "
167 "instance of bitcoin may be using it.\n",
169 err =
strprintf(
_(
"Error initializing wallet database environment %s!"),
174 fs::path pathLogDir = pathIn /
"database";
176 fs::path pathErrorFile = pathIn /
"db.log";
177 LogPrintf(
"BerkeleyEnvironment::Open: LogDir=%s ErrorFile=%s\n",
180 unsigned int nEnvFlags = 0;
182 nEnvFlags |= DB_PRIVATE;
187 dbenv->set_cachesize(0, 0x100000, 1);
188 dbenv->set_lg_bsize(0x10000);
189 dbenv->set_lg_max(1048576);
190 dbenv->set_lk_max_locks(40000);
191 dbenv->set_lk_max_objects(40000);
194 dbenv->set_flags(DB_AUTO_COMMIT, 1);
195 dbenv->set_flags(DB_TXN_WRITE_NOSYNC, 1);
196 dbenv->log_set_config(DB_LOG_AUTO_REMOVE, 1);
199 DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL |
200 DB_INIT_TXN | DB_THREAD | DB_RECOVER | nEnvFlags,
203 LogPrintf(
"BerkeleyEnvironment::Open: Error %d opening database "
205 ret, DbEnv::strerror(ret));
206 int ret2 =
dbenv->close(0);
208 LogPrintf(
"BerkeleyEnvironment::Open: Error %d closing failed "
209 "database environment: %s\n",
210 ret2, DbEnv::strerror(ret2));
213 err =
strprintf(
_(
"Error initializing wallet database environment %s!"),
215 if (ret == DB_RUNRECOVERY) {
217 _(
"This error could occur if this wallet was not shutdown "
218 "cleanly and was last loaded using a build with a newer "
219 "version of Berkeley DB. If so, please use the software "
220 "that last loaded this wallet");
236 dbenv->set_cachesize(1, 0, 1);
237 dbenv->set_lg_bsize(10485760 * 4);
238 dbenv->set_lg_max(10485760);
239 dbenv->set_lk_max_locks(10000);
240 dbenv->set_lk_max_objects(10000);
241 dbenv->set_flags(DB_AUTO_COMMIT, 1);
242 dbenv->log_set_config(DB_LOG_IN_MEMORY, 1);
245 DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL |
246 DB_INIT_TXN | DB_THREAD | DB_PRIVATE,
249 throw std::runtime_error(
250 strprintf(
"BerkeleyEnvironment::MakeMock: Error %d opening "
251 "database environment.",
260 m_dbt.set_flags(DB_DBT_MALLOC);
266 if (m_dbt.get_data() !=
nullptr) {
272 if (m_dbt.get_flags() & DB_DBT_MALLOC) {
273 free(m_dbt.get_data());
279 return m_dbt.get_data();
283 return m_dbt.get_size();
286BerkeleyBatch::SafeDbt::operator Dbt *() {
305 int result =
db.verify(
strFile.c_str(),
nullptr,
nullptr, 0);
308 strprintf(
_(
"%s corrupt. Try using the wallet tool "
309 "bitcoin-wallet to salvage or restoring a backup."),
319 dbenv->txn_checkpoint(0, 0, 0);
323 dbenv->lsn_reset(
strFile.c_str(), 0);
338 bool fFlushOnCloseIn)
351 unsigned int nFlags = DB_THREAD | DB_CREATE;
356 if (!
env->Open(open_err)) {
357 throw std::runtime_error(
358 "BerkeleyDatabase: Failed to open database environment.");
361 if (
m_db ==
nullptr) {
363 std::unique_ptr<Db> pdb_temp =
364 std::make_unique<Db>(
env->dbenv.get(), 0);
366 bool fMockDb =
env->IsMock();
368 DbMpoolFile *mpf = pdb_temp->get_mpf();
369 ret = mpf->set_flags(DB_MPOOL_NOFILE, 1);
372 "BerkeleyDatabase: Failed to configure for no "
373 "temp file backing for database %s",
378 ret = pdb_temp->open(
380 fMockDb ?
nullptr :
strFile.c_str(),
381 fMockDb ?
strFile.c_str() :
"main",
388 "BerkeleyDatabase: Error %d, can't open database %s", ret,
398 m_db.reset(pdb_temp.release());
409 unsigned int nMinutes = 0;
457 database.
m_db->close(0);
458 database.
m_db.reset();
465 std::unique_lock<RecursiveMutex> lock(cs_db);
468 if (
db.second.get().m_refcount > 0) {
481 for (
const std::string &filename :
filenames) {
502 bool fSuccess =
true;
504 std::string strFileRes =
strFile +
".rewrite";
507 std::unique_ptr<Db> pdbCopy =
508 std::make_unique<Db>(
env->dbenv.get(), 0);
510 int ret = pdbCopy->open(
nullptr,
517 LogPrintf(
"BerkeleyBatch::Rewrite: Can't create "
518 "database file %s\n",
523 if (
db.StartCursor()) {
529 db.ReadAtCursor(ssKey, ssValue, complete);
538 strncmp((
const char *)ssKey.data(), pszSkip,
539 std::min(ssKey.size(),
540 strlen(pszSkip))) == 0) {
543 if (strncmp((
const char *)ssKey.data(),
544 "\x07version", 8) == 0) {
549 Dbt datKey(ssKey.data(), ssKey.size());
550 Dbt datValue(ssValue.data(), ssValue.size());
551 int ret2 = pdbCopy->put(
nullptr, &datKey, &datValue,
562 if (pdbCopy->close(0)) {
570 Db dbA(
env->dbenv.get(), 0);
571 if (dbA.remove(
strFile.c_str(),
nullptr, 0)) {
574 Db dbB(
env->dbenv.get(), 0);
575 if (dbB.rename(strFileRes.c_str(),
nullptr,
strFile.c_str(),
581 LogPrintf(
"BerkeleyBatch::Rewrite: Failed to rewrite "
582 "database file %s\n",
596 strPath, fShutdown ?
"true" :
"false",
603 bool no_dbs_accessed =
true;
605 std::string strFile = db_it.first;
606 int nRefCount = db_it.second.get().m_refcount;
612 "BerkeleyEnvironment::Flush: Flushing %s (refcount = %d)...\n",
614 if (nRefCount == 0) {
618 "BerkeleyEnvironment::Flush: %s checkpoint\n",
620 dbenv->txn_checkpoint(0, 0, 0);
622 "BerkeleyEnvironment::Flush: %s detach\n", strFile);
624 dbenv->lsn_reset(strFile.c_str(), 0);
627 "BerkeleyEnvironment::Flush: %s closed\n", strFile);
630 no_dbs_accessed =
false;
634 "BerkeleyEnvironment::Flush: Flush(%s)%s took %15dms\n",
635 fShutdown ?
"true" :
"false",
640 if (no_dbs_accessed) {
641 dbenv->log_archive(&listp, DB_ARCH_REMOVE);
659 for (
auto &it :
env->m_databases) {
660 if (it.second.get().m_refcount > 0) {
696 if (fs::is_directory(pathDest)) {
702 fs::equivalent(pathSrc, pathDest)) {
703 LogPrintf(
"cannot backup to wallet source file %s\n",
709 fs::copy_options::overwrite_existing);
713 }
catch (
const fs::filesystem_error &e) {
755 int ret =
m_cursor->get(datKey, datValue, DB_NEXT);
756 if (ret == DB_NOTFOUND) {
761 }
else if (datKey.
get_data() ==
nullptr || datValue.
get_data() ==
nullptr) {
812 return DbEnv::version(
nullptr,
nullptr,
nullptr);
820 SafeDbt datKey(key.data(), key.size());
824 if (ret == 0 && datValue.
get_data() !=
nullptr) {
838 assert(!
"Write called on database in read-only mode");
841 SafeDbt datKey(key.data(), key.size());
843 SafeDbt datValue(value.data(), value.size());
846 pdb->put(
activeTxn, datKey, datValue, (overwrite ? 0 : DB_NOOVERWRITE));
855 assert(!
"Erase called on database in read-only mode");
858 SafeDbt datKey(key.data(), key.size());
861 return (ret == 0 || ret == DB_NOTFOUND);
869 SafeDbt datKey(key.data(), key.size());
888 env->m_db_in_use.notify_all();
892std::unique_ptr<DatabaseBatch>
894 return std::make_unique<BerkeleyBatch>(*
this,
false, flush_on_close);
899 std::string data_filename;
904std::unique_ptr<BerkeleyDatabase>
907 std::unique_ptr<BerkeleyDatabase>
db;
911 std::string data_filename;
912 std::shared_ptr<BerkeleyEnvironment> env =
916 strprintf(
"Failed to load database. Data file '%s' is in the "
917 "process of being unloaded. Try again later.",
922 if (env->m_databases.count(data_filename)) {
924 "Refusing to load database. Data file '%s' is already loaded.",
929 db = std::make_unique<BerkeleyDatabase>(std::move(env),
930 std::move(data_filename));
933 if (options.
verify && !
db->Verify(error)) {
bool ExistsBerkeleyDatabase(const fs::path &path)
Check if Berkeley database exists at specified path.
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 BerkeleyDatabaseVersion()
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
bool IsBerkeleyBtree(const fs::path &path)
Check format of database file.
int64_t GetIntArg(const std::string &strArg, int64_t nDefault) const
Return integer 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.
uint32_t get_size() const
const void * get_data() const
RAII class that provides access to a Berkeley database.
bool HasKey(DataStream &&key) override
bool ReadKey(DataStream &&key, DataStream &value) override
bool TxnCommit() override
bool ReadAtCursor(DataStream &ssKey, DataStream &ssValue, bool &complete) override
bool StartCursor() override
void CloseCursor() override
bool WriteKey(DataStream &&key, DataStream &&value, bool overwrite=true) override
BerkeleyBatch(BerkeleyDatabase &database, const bool fReadOnly, bool fFlushOnCloseIn=true)
bool EraseKey(DataStream &&key) override
BerkeleyEnvironment * env
~BerkeleyBatch() override
BerkeleyDatabase & m_database
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
flush the wallet passively (TRY_LOCK) ideal to be called periodically
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
DbTxn * TxnBegin(int flags=DB_TXN_WRITE_NOSYNC)
fs::path Directory() const
std::map< std::string, std::reference_wrapper< BerkeleyDatabase > > m_databases
bool Open(bilingual_str &error)
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(Span< const value_type > src)
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...
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
void memory_cleanse(void *ptr, size_t len)
Secure overwrite a buffer (possibly containing secret data) with zero-bytes.
static constexpr int CLIENT_VERSION
bitcoind-res.rc includes this file, but it cannot cope with real c++ code.
void UnlockDirectory(const fs::path &directory, const std::string &lockfile_name)
bool TryCreateDirectories(const fs::path &p)
Ignores exceptions thrown by create_directories if the requested directory exists.
std::string HexStr(const Span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
#define LogPrint(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 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 std::string lockfile_name, bool probe_only)
const std::byte * BytePtr(const void *data)
Convert a data pointer to a std::byte data pointer.
bool operator==(const WalletDatabaseFileId &rhs) const
uint8_t value[DB_FILE_ID_LEN]
#define AssertLockNotHeld(cs)
#define TRY_LOCK(cs, name)
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[]
void SplitWalletPath(const fs::path &wallet_path, fs::path &env_directory, std::string &database_filename)