34 const std::string &filename, Db &
db,
40 int ret =
db.get_mpf()->get_fileid(fileid.
value);
42 throw std::runtime_error(
43 strprintf(
"BerkeleyDatabase: Can't open database %s (get_fileid "
49 if (fileid == item.second && &fileid != &item.second) {
50 throw std::runtime_error(
51 strprintf(
"BerkeleyDatabase: Can't open database %s "
52 "(duplicates fileid %s "
54 filename,
HexStr(item.second.value), item.first));
62std::map<std::string, std::weak_ptr<BerkeleyEnvironment>>
82std::shared_ptr<BerkeleyEnvironment>
88 std::weak_ptr<BerkeleyEnvironment>());
89 if (inserted.second) {
90 auto env = std::make_shared<BerkeleyEnvironment>(env_directory);
91 inserted.first->second = env;
94 return inserted.first->second.lock();
112 database.
m_db->close(0);
113 database.
m_db.reset();
117 FILE *error_file =
nullptr;
118 dbenv->get_errfile(&error_file);
120 int ret =
dbenv->close(0);
122 LogPrintf(
"BerkeleyEnvironment::Close: Error %d closing database "
124 ret, DbEnv::strerror(ret));
127 DbEnv(uint32_t(0)).remove(
strPath.c_str(), 0);
138 dbenv.reset(
new DbEnv(DB_CXX_NO_EXCEPTIONS));
162 LogPrintf(
"Cannot obtain a lock on wallet directory %s. Another "
163 "instance of bitcoin may be using it.\n",
165 err =
strprintf(
_(
"Error initializing wallet database environment %s!"),
170 fs::path pathLogDir = pathIn /
"database";
172 fs::path pathErrorFile = pathIn /
"db.log";
173 LogPrintf(
"BerkeleyEnvironment::Open: LogDir=%s ErrorFile=%s\n",
176 unsigned int nEnvFlags = 0;
178 nEnvFlags |= DB_PRIVATE;
183 dbenv->set_cachesize(0, 0x100000, 1);
184 dbenv->set_lg_bsize(0x10000);
185 dbenv->set_lg_max(1048576);
186 dbenv->set_lk_max_locks(40000);
187 dbenv->set_lk_max_objects(40000);
190 dbenv->set_flags(DB_AUTO_COMMIT, 1);
191 dbenv->set_flags(DB_TXN_WRITE_NOSYNC, 1);
192 dbenv->log_set_config(DB_LOG_AUTO_REMOVE, 1);
195 DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL |
196 DB_INIT_TXN | DB_THREAD | DB_RECOVER | nEnvFlags,
199 LogPrintf(
"BerkeleyEnvironment::Open: Error %d opening database "
201 ret, DbEnv::strerror(ret));
202 int ret2 =
dbenv->close(0);
204 LogPrintf(
"BerkeleyEnvironment::Open: Error %d closing failed "
205 "database environment: %s\n",
206 ret2, DbEnv::strerror(ret2));
209 err =
strprintf(
_(
"Error initializing wallet database environment %s!"),
211 if (ret == DB_RUNRECOVERY) {
213 _(
"This error could occur if this wallet was not shutdown "
214 "cleanly and was last loaded using a build with a newer "
215 "version of Berkeley DB. If so, please use the software "
216 "that last loaded this wallet");
232 dbenv->set_cachesize(1, 0, 1);
233 dbenv->set_lg_bsize(10485760 * 4);
234 dbenv->set_lg_max(10485760);
235 dbenv->set_lk_max_locks(10000);
236 dbenv->set_lk_max_objects(10000);
237 dbenv->set_flags(DB_AUTO_COMMIT, 1);
238 dbenv->log_set_config(DB_LOG_IN_MEMORY, 1);
241 DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL |
242 DB_INIT_TXN | DB_THREAD | DB_PRIVATE,
245 throw std::runtime_error(
246 strprintf(
"BerkeleyEnvironment::MakeMock: Error %d opening "
247 "database environment.",
256 m_dbt.set_flags(DB_DBT_MALLOC);
262 if (m_dbt.get_data() !=
nullptr) {
268 if (m_dbt.get_flags() & DB_DBT_MALLOC) {
269 free(m_dbt.get_data());
275 return m_dbt.get_data();
279 return m_dbt.get_size();
282BerkeleyBatch::SafeDbt::operator Dbt *() {
301 int result =
db.verify(
strFile.c_str(),
nullptr,
nullptr, 0);
304 strprintf(
_(
"%s corrupt. Try using the wallet tool "
305 "bitcoin-wallet to salvage or restoring a backup."),
315 dbenv->txn_checkpoint(0, 0, 0);
319 dbenv->lsn_reset(
strFile.c_str(), 0);
334 bool fFlushOnCloseIn)
344 if (!
Exists(std::string(
"version"))) {
353 unsigned int nFlags = DB_THREAD | DB_CREATE;
358 if (!
env->Open(open_err)) {
359 throw std::runtime_error(
360 "BerkeleyDatabase: Failed to open database environment.");
363 if (
m_db ==
nullptr) {
365 std::unique_ptr<Db> pdb_temp =
366 std::make_unique<Db>(
env->dbenv.get(), 0);
368 bool fMockDb =
env->IsMock();
370 DbMpoolFile *mpf = pdb_temp->get_mpf();
371 ret = mpf->set_flags(DB_MPOOL_NOFILE, 1);
374 "BerkeleyDatabase: Failed to configure for no "
375 "temp file backing for database %s",
380 ret = pdb_temp->open(
382 fMockDb ?
nullptr :
strFile.c_str(),
383 fMockDb ?
strFile.c_str() :
"main",
390 "BerkeleyDatabase: Error %d, can't open database %s", ret,
400 m_db.reset(pdb_temp.release());
411 unsigned int nMinutes = 0;
459 database.
m_db->close(0);
460 database.
m_db.reset();
467 std::unique_lock<RecursiveMutex> lock(cs_db);
470 if (
db.second.get().m_refcount > 0) {
482 for (
const std::string &filename :
filenames) {
503 bool fSuccess =
true;
505 std::string strFileRes =
strFile +
".rewrite";
508 std::unique_ptr<Db> pdbCopy =
509 std::make_unique<Db>(
env->dbenv.get(), 0);
511 int ret = pdbCopy->open(
nullptr,
518 LogPrintf(
"BerkeleyBatch::Rewrite: Can't create "
519 "database file %s\n",
524 if (
db.StartCursor()) {
530 db.ReadAtCursor(ssKey, ssValue, complete);
539 strncmp((
const char *)ssKey.
data(), pszSkip,
540 std::min(ssKey.
size(),
541 strlen(pszSkip))) == 0) {
544 if (strncmp((
const char *)ssKey.
data(),
545 "\x07version", 8) == 0) {
550 Dbt datKey(ssKey.
data(), ssKey.
size());
551 Dbt datValue(ssValue.
data(), ssValue.
size());
552 int ret2 = pdbCopy->put(
nullptr, &datKey, &datValue,
563 if (pdbCopy->close(0)) {
571 Db dbA(
env->dbenv.get(), 0);
572 if (dbA.remove(
strFile.c_str(),
nullptr, 0)) {
575 Db dbB(
env->dbenv.get(), 0);
576 if (dbB.rename(strFileRes.c_str(),
nullptr,
strFile.c_str(),
582 LogPrintf(
"BerkeleyBatch::Rewrite: Failed to rewrite "
583 "database file %s\n",
597 strPath, fShutdown ?
"true" :
"false",
604 bool no_dbs_accessed =
true;
606 std::string strFile = db_it.first;
607 int nRefCount = db_it.second.get().m_refcount;
613 "BerkeleyEnvironment::Flush: Flushing %s (refcount = %d)...\n",
615 if (nRefCount == 0) {
619 "BerkeleyEnvironment::Flush: %s checkpoint\n",
621 dbenv->txn_checkpoint(0, 0, 0);
623 "BerkeleyEnvironment::Flush: %s detach\n", strFile);
625 dbenv->lsn_reset(strFile.c_str(), 0);
628 "BerkeleyEnvironment::Flush: %s closed\n", strFile);
631 no_dbs_accessed =
false;
635 "BerkeleyEnvironment::Flush: Flush(%s)%s took %15dms\n",
636 fShutdown ?
"true" :
"false",
641 if (no_dbs_accessed) {
642 dbenv->log_archive(&listp, DB_ARCH_REMOVE);
660 for (
auto &it :
env->m_databases) {
661 if (it.second.get().m_refcount > 0) {
697 if (fs::is_directory(pathDest)) {
703 fs::equivalent(pathSrc, pathDest)) {
704 LogPrintf(
"cannot backup to wallet source file %s\n",
710 fs::copy_options::overwrite_existing);
714 }
catch (
const fs::filesystem_error &e) {
756 int ret =
m_cursor->get(datKey, datValue, DB_NEXT);
757 if (ret == DB_NOTFOUND) {
762 }
else if (datKey.
get_data() ==
nullptr || datValue.
get_data() ==
nullptr) {
815 return DbEnv::version(
nullptr,
nullptr,
nullptr);
823 SafeDbt datKey(key.data(), key.size());
827 if (ret == 0 && datValue.
get_data() !=
nullptr) {
841 assert(!
"Write called on database in read-only mode");
844 SafeDbt datKey(key.data(), key.size());
846 SafeDbt datValue(value.data(), value.size());
849 pdb->put(
activeTxn, datKey, datValue, (overwrite ? 0 : DB_NOOVERWRITE));
858 assert(!
"Erase called on database in read-only mode");
861 SafeDbt datKey(key.data(), key.size());
864 return (ret == 0 || ret == DB_NOTFOUND);
872 SafeDbt datKey(key.data(), key.size());
891 env->m_db_in_use.notify_all();
895std::unique_ptr<DatabaseBatch>
897 return std::make_unique<BerkeleyBatch>(*
this,
false, flush_on_close);
902 std::string data_filename;
907std::unique_ptr<BerkeleyDatabase>
910 std::unique_ptr<BerkeleyDatabase>
db;
914 std::string data_filename;
915 std::shared_ptr<BerkeleyEnvironment> env =
917 if (env->m_databases.count(data_filename)) {
919 "Refusing to load database. Data file '%s' is already loaded.",
924 db = std::make_unique<BerkeleyDatabase>(std::move(env),
925 std::move(data_filename));
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 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
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)
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...
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.
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 create_directories if the requested directory exists.
bool error(const char *fmt, const Args &...args)
#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)
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[]
std::string HexStr(const Span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
void SplitWalletPath(const fs::path &wallet_path, fs::path &env_directory, std::string &database_filename)