28 const std::string &filename, Db &
db,
34 int ret =
db.get_mpf()->get_fileid(fileid.
value);
36 throw std::runtime_error(
37 strprintf(
"BerkeleyDatabase: Can't open database %s (get_fileid "
43 if (fileid == item.second && &fileid != &item.second) {
44 throw std::runtime_error(
45 strprintf(
"BerkeleyDatabase: Can't open database %s "
46 "(duplicates fileid %s "
48 filename,
HexStr(item.second.value), item.first));
56 std::map<std::string, std::weak_ptr<BerkeleyEnvironment>>
76 std::shared_ptr<BerkeleyEnvironment>
82 std::weak_ptr<BerkeleyEnvironment>());
83 if (inserted.second) {
84 auto env = std::make_shared<BerkeleyEnvironment>(env_directory);
85 inserted.first->second = env;
88 return inserted.first->second.lock();
106 database.
m_db->close(0);
107 database.
m_db.reset();
111 FILE *error_file =
nullptr;
112 dbenv->get_errfile(&error_file);
114 int ret =
dbenv->close(0);
116 LogPrintf(
"BerkeleyEnvironment::Close: Error %d closing database "
118 ret, DbEnv::strerror(ret));
121 DbEnv(uint32_t(0)).remove(
strPath.c_str(), 0);
132 dbenv.reset(
new DbEnv(DB_CXX_NO_EXCEPTIONS));
156 LogPrintf(
"Cannot obtain a lock on wallet directory %s. Another "
157 "instance of bitcoin may be using it.\n",
159 err =
strprintf(
_(
"Error initializing wallet database environment %s!"),
164 fs::path pathLogDir = pathIn /
"database";
166 fs::path pathErrorFile = pathIn /
"db.log";
167 LogPrintf(
"BerkeleyEnvironment::Open: LogDir=%s ErrorFile=%s\n",
170 unsigned int nEnvFlags = 0;
172 nEnvFlags |= DB_PRIVATE;
177 dbenv->set_cachesize(0, 0x100000, 1);
178 dbenv->set_lg_bsize(0x10000);
179 dbenv->set_lg_max(1048576);
180 dbenv->set_lk_max_locks(40000);
181 dbenv->set_lk_max_objects(40000);
184 dbenv->set_flags(DB_AUTO_COMMIT, 1);
185 dbenv->set_flags(DB_TXN_WRITE_NOSYNC, 1);
186 dbenv->log_set_config(DB_LOG_AUTO_REMOVE, 1);
189 DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL |
190 DB_INIT_TXN | DB_THREAD | DB_RECOVER | nEnvFlags,
193 LogPrintf(
"BerkeleyEnvironment::Open: Error %d opening database "
195 ret, DbEnv::strerror(ret));
196 int ret2 =
dbenv->close(0);
198 LogPrintf(
"BerkeleyEnvironment::Open: Error %d closing failed "
199 "database environment: %s\n",
200 ret2, DbEnv::strerror(ret2));
203 err =
strprintf(
_(
"Error initializing wallet database environment %s!"),
205 if (ret == DB_RUNRECOVERY) {
207 _(
"This error could occur if this wallet was not shutdown "
208 "cleanly and was last loaded using a build with a newer "
209 "version of Berkeley DB. If so, please use the software "
210 "that last loaded this wallet");
226 dbenv->set_cachesize(1, 0, 1);
227 dbenv->set_lg_bsize(10485760 * 4);
228 dbenv->set_lg_max(10485760);
229 dbenv->set_lk_max_locks(10000);
230 dbenv->set_lk_max_objects(10000);
231 dbenv->set_flags(DB_AUTO_COMMIT, 1);
232 dbenv->log_set_config(DB_LOG_IN_MEMORY, 1);
235 DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL |
236 DB_INIT_TXN | DB_THREAD | DB_PRIVATE,
239 throw std::runtime_error(
240 strprintf(
"BerkeleyEnvironment::MakeMock: Error %d opening "
241 "database environment.",
250 m_dbt.set_flags(DB_DBT_MALLOC);
256 if (m_dbt.get_data() !=
nullptr) {
262 if (m_dbt.get_flags() & DB_DBT_MALLOC) {
263 free(m_dbt.get_data());
269 return m_dbt.get_data();
273 return m_dbt.get_size();
276 BerkeleyBatch::SafeDbt::operator Dbt *() {
295 int result =
db.verify(
strFile.c_str(),
nullptr,
nullptr, 0);
298 strprintf(
_(
"%s corrupt. Try using the wallet tool "
299 "bitcoin-wallet to salvage or restoring a backup."),
309 dbenv->txn_checkpoint(0, 0, 0);
313 dbenv->lsn_reset(
strFile.c_str(), 0);
328 bool fFlushOnCloseIn)
338 if (!
Exists(std::string(
"version"))) {
347 unsigned int nFlags = DB_THREAD | DB_CREATE;
352 if (!
env->Open(open_err)) {
353 throw std::runtime_error(
354 "BerkeleyDatabase: Failed to open database environment.");
357 if (
m_db ==
nullptr) {
359 std::unique_ptr<Db> pdb_temp =
360 std::make_unique<Db>(
env->dbenv.get(), 0);
362 bool fMockDb =
env->IsMock();
364 DbMpoolFile *mpf = pdb_temp->get_mpf();
365 ret = mpf->set_flags(DB_MPOOL_NOFILE, 1);
368 "BerkeleyDatabase: Failed to configure for no "
369 "temp file backing for database %s",
374 ret = pdb_temp->open(
376 fMockDb ?
nullptr :
strFile.c_str(),
377 fMockDb ?
strFile.c_str() :
"main",
384 "BerkeleyDatabase: Error %d, can't open database %s", ret,
394 m_db.reset(pdb_temp.release());
405 unsigned int nMinutes = 0;
453 database.
m_db->close(0);
454 database.
m_db.reset();
461 std::unique_lock<RecursiveMutex> lock(cs_db);
464 if (
db.second.get().m_refcount > 0) {
476 for (
const std::string &filename :
filenames) {
497 bool fSuccess =
true;
499 std::string strFileRes =
strFile +
".rewrite";
502 std::unique_ptr<Db> pdbCopy =
503 std::make_unique<Db>(
env->dbenv.get(), 0);
505 int ret = pdbCopy->open(
nullptr,
512 LogPrintf(
"BerkeleyBatch::Rewrite: Can't create "
513 "database file %s\n",
518 if (
db.StartCursor()) {
524 db.ReadAtCursor(ssKey, ssValue, complete);
533 strncmp(ssKey.
data(), pszSkip,
534 std::min(ssKey.
size(),
535 strlen(pszSkip))) == 0) {
538 if (strncmp(ssKey.
data(),
"\x07version", 8) == 0) {
543 Dbt datKey(ssKey.
data(), ssKey.
size());
544 Dbt datValue(ssValue.
data(), ssValue.
size());
545 int ret2 = pdbCopy->put(
nullptr, &datKey, &datValue,
556 if (pdbCopy->close(0)) {
564 Db dbA(
env->dbenv.get(), 0);
565 if (dbA.remove(
strFile.c_str(),
nullptr, 0)) {
568 Db dbB(
env->dbenv.get(), 0);
569 if (dbB.rename(strFileRes.c_str(),
nullptr,
strFile.c_str(),
575 LogPrintf(
"BerkeleyBatch::Rewrite: Failed to rewrite "
576 "database file %s\n",
590 strPath, fShutdown ?
"true" :
"false",
597 bool no_dbs_accessed =
true;
599 std::string strFile = db_it.first;
600 int nRefCount = db_it.second.get().m_refcount;
606 "BerkeleyEnvironment::Flush: Flushing %s (refcount = %d)...\n",
608 if (nRefCount == 0) {
612 "BerkeleyEnvironment::Flush: %s checkpoint\n",
614 dbenv->txn_checkpoint(0, 0, 0);
616 "BerkeleyEnvironment::Flush: %s detach\n", strFile);
618 dbenv->lsn_reset(strFile.c_str(), 0);
621 "BerkeleyEnvironment::Flush: %s closed\n", strFile);
624 no_dbs_accessed =
false;
628 "BerkeleyEnvironment::Flush: Flush(%s)%s took %15dms\n",
629 fShutdown ?
"true" :
"false",
634 if (no_dbs_accessed) {
635 dbenv->log_archive(&listp, DB_ARCH_REMOVE);
653 for (
auto &it :
env->m_databases) {
654 if (it.second.get().m_refcount > 0) {
690 if (fs::is_directory(pathDest)) {
695 if (fs::equivalent(pathSrc, pathDest)) {
696 LogPrintf(
"cannot backup to wallet source file %s\n",
701 fs::copy_file(pathSrc, pathDest,
702 fs::copy_option::overwrite_if_exists);
706 }
catch (
const fs::filesystem_error &e) {
748 int ret =
m_cursor->get(datKey, datValue, DB_NEXT);
749 if (ret == DB_NOTFOUND) {
754 }
else if (datKey.
get_data() ==
nullptr || datValue.
get_data() ==
nullptr) {
807 return DbEnv::version(
nullptr,
nullptr,
nullptr);
815 SafeDbt datKey(key.data(), key.size());
819 if (ret == 0 && datValue.
get_data() !=
nullptr) {
833 assert(!
"Write called on database in read-only mode");
836 SafeDbt datKey(key.data(), key.size());
838 SafeDbt datValue(value.data(), value.size());
841 pdb->put(
activeTxn, datKey, datValue, (overwrite ? 0 : DB_NOOVERWRITE));
850 assert(!
"Erase called on database in read-only mode");
853 SafeDbt datKey(key.data(), key.size());
856 return (ret == 0 || ret == DB_NOTFOUND);
864 SafeDbt datKey(key.data(), key.size());
883 env->m_db_in_use.notify_all();
887 std::unique_ptr<DatabaseBatch>
889 return std::make_unique<BerkeleyBatch>(*
this,
false, flush_on_close);
894 std::string data_filename;
899 std::unique_ptr<BerkeleyDatabase>
902 std::unique_ptr<BerkeleyDatabase>
db;
906 std::string data_filename;
907 std::shared_ptr<BerkeleyEnvironment> env =
909 if (env->m_databases.count(data_filename)) {
911 "Refusing to load database. Data file '%s' is already loaded.",
916 db = std::make_unique<BerkeleyDatabase>(std::move(env),
917 std::move(data_filename));
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()
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
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...
Path class wrapper to prepare application code for transition from boost::filesystem library to std::...
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.
#define LogPrint(category,...)
Filesystem operations and types.
static auto quoted(const std::string &s)
static bool exists(const path &p)
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)
std::string HexStr(const Span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
bool operator==(const WalletDatabaseFileId &rhs) const
uint8_t value[DB_FILE_ID_LEN]
#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[]
void SplitWalletPath(const fs::path &wallet_path, fs::path &env_directory, std::string &database_filename)