Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/db.cpp
Show First 20 Lines • Show All 53 Lines • ▼ Show 20 Lines | for (const auto &item : env.m_fileids) { | ||||
std::end(item.second.value)), | std::end(item.second.value)), | ||||
item.first)); | item.first)); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
CCriticalSection cs_db; | CCriticalSection cs_db; | ||||
//!< Map from directory name to open db environment. | //!< Map from directory name to db environment. | ||||
std::map<std::string, BerkeleyEnvironment> g_dbenvs GUARDED_BY(cs_db); | std::map<std::string, std::weak_ptr<BerkeleyEnvironment>> | ||||
g_dbenvs GUARDED_BY(cs_db); | |||||
} // namespace | } // namespace | ||||
bool WalletDatabaseFileId::operator==(const WalletDatabaseFileId &rhs) const { | bool WalletDatabaseFileId::operator==(const WalletDatabaseFileId &rhs) const { | ||||
return memcmp(value, &rhs.value, sizeof(value)) == 0; | return memcmp(value, &rhs.value, sizeof(value)) == 0; | ||||
} | } | ||||
static void SplitWalletPath(const fs::path &wallet_path, | static void SplitWalletPath(const fs::path &wallet_path, | ||||
fs::path &env_directory, | fs::path &env_directory, | ||||
Show All 18 Lines | bool IsWalletLoaded(const fs::path &wallet_path) { | ||||
SplitWalletPath(wallet_path, env_directory, database_filename); | SplitWalletPath(wallet_path, env_directory, database_filename); | ||||
LOCK(cs_db); | LOCK(cs_db); | ||||
auto env = g_dbenvs.find(env_directory.string()); | auto env = g_dbenvs.find(env_directory.string()); | ||||
if (env == g_dbenvs.end()) { | if (env == g_dbenvs.end()) { | ||||
return false; | return false; | ||||
} | } | ||||
return env->second.IsDatabaseLoaded(database_filename); | auto database = env->second.lock(); | ||||
return database && database->IsDatabaseLoaded(database_filename); | |||||
} | } | ||||
BerkeleyEnvironment *GetWalletEnv(const fs::path &wallet_path, | /** | ||||
std::string &database_filename) { | * @param[in] wallet_path Path to wallet directory. Or (for backwards | ||||
* compatibility only) a path to a berkeley btree data file inside a wallet | |||||
* directory. | |||||
* @param[out] database_filename Filename of berkeley btree data file inside the | |||||
* wallet directory. | |||||
* @return A shared pointer to the BerkeleyEnvironment object for the wallet | |||||
* directory, never empty because ~BerkeleyEnvironment erases the weak pointer | |||||
* from the g_dbenvs map. | |||||
* @post A new BerkeleyEnvironment weak pointer is inserted into g_dbenvs if the | |||||
* directory path key was not already in the map. | |||||
*/ | |||||
std::shared_ptr<BerkeleyEnvironment> | |||||
GetWalletEnv(const fs::path &wallet_path, std::string &database_filename) { | |||||
fs::path env_directory; | fs::path env_directory; | ||||
SplitWalletPath(wallet_path, env_directory, database_filename); | SplitWalletPath(wallet_path, env_directory, database_filename); | ||||
LOCK(cs_db); | LOCK(cs_db); | ||||
// Note: An ununsed temporary BerkeleyEnvironment object may be created | auto inserted = g_dbenvs.emplace(env_directory.string(), | ||||
// inside the emplace function if the key already exists. This is a little | std::weak_ptr<BerkeleyEnvironment>()); | ||||
// inefficient, but not a big concern since the map will be changed in the | if (inserted.second) { | ||||
// future to hold pointers instead of objects, anyway. | auto env = | ||||
return &g_dbenvs | std::make_shared<BerkeleyEnvironment>(env_directory.string()); | ||||
.emplace(std::piecewise_construct, | inserted.first->second = env; | ||||
std::forward_as_tuple(env_directory.string()), | return env; | ||||
std::forward_as_tuple(env_directory)) | } | ||||
.first->second; | return inserted.first->second.lock(); | ||||
} | } | ||||
// | // | ||||
// BerkeleyBatch | // BerkeleyBatch | ||||
// | // | ||||
void BerkeleyEnvironment::Close() { | void BerkeleyEnvironment::Close() { | ||||
if (!fDbEnvInit) { | if (!fDbEnvInit) { | ||||
Show All 30 Lines | |||||
} | } | ||||
BerkeleyEnvironment::BerkeleyEnvironment(const fs::path &dir_path) | BerkeleyEnvironment::BerkeleyEnvironment(const fs::path &dir_path) | ||||
: strPath(dir_path.string()) { | : strPath(dir_path.string()) { | ||||
Reset(); | Reset(); | ||||
} | } | ||||
BerkeleyEnvironment::~BerkeleyEnvironment() { | BerkeleyEnvironment::~BerkeleyEnvironment() { | ||||
LOCK(cs_db); | |||||
g_dbenvs.erase(strPath); | |||||
Close(); | Close(); | ||||
} | } | ||||
bool BerkeleyEnvironment::Open(bool retry) { | bool BerkeleyEnvironment::Open(bool retry) { | ||||
if (fDbEnvInit) { | if (fDbEnvInit) { | ||||
return true; | return true; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 70 Lines • ▼ Show 20 Lines | if (ret != 0) { | ||||
} | } | ||||
} | } | ||||
fDbEnvInit = true; | fDbEnvInit = true; | ||||
fMockDb = false; | fMockDb = false; | ||||
return true; | return true; | ||||
} | } | ||||
void BerkeleyEnvironment::MakeMock() { | //! Construct an in-memory mock Berkeley environment for testing and as a | ||||
if (fDbEnvInit) { | //! place-holder for g_dbenvs emplace | ||||
throw std::runtime_error( | BerkeleyEnvironment::BerkeleyEnvironment() { | ||||
"BerkeleyEnvironment::MakeMock: Already initialized"); | Reset(); | ||||
} | |||||
boost::this_thread::interruption_point(); | boost::this_thread::interruption_point(); | ||||
LogPrint(BCLog::DB, "BerkeleyEnvironment::MakeMock\n"); | LogPrint(BCLog::DB, "BerkeleyEnvironment::MakeMock\n"); | ||||
dbenv->set_cachesize(1, 0, 1); | dbenv->set_cachesize(1, 0, 1); | ||||
dbenv->set_lg_bsize(10485760 * 4); | dbenv->set_lg_bsize(10485760 * 4); | ||||
dbenv->set_lg_max(10485760); | dbenv->set_lg_max(10485760); | ||||
Show All 39 Lines | |||||
} | } | ||||
bool BerkeleyBatch::Recover(const fs::path &file_path, void *callbackDataIn, | bool BerkeleyBatch::Recover(const fs::path &file_path, void *callbackDataIn, | ||||
bool (*recoverKVcallback)(void *callbackData, | bool (*recoverKVcallback)(void *callbackData, | ||||
CDataStream ssKey, | CDataStream ssKey, | ||||
CDataStream ssValue), | CDataStream ssValue), | ||||
std::string &newFilename) { | std::string &newFilename) { | ||||
std::string filename; | std::string filename; | ||||
BerkeleyEnvironment *env = GetWalletEnv(file_path, filename); | std::shared_ptr<BerkeleyEnvironment> env = | ||||
GetWalletEnv(file_path, filename); | |||||
// Recovery procedure: | // Recovery procedure: | ||||
// Move wallet file to walletfilename.timestamp.bak | // Move wallet file to walletfilename.timestamp.bak | ||||
// Call Salvage with fAggressive=true to get as much data as possible. | // Call Salvage with fAggressive=true to get as much data as possible. | ||||
// Rewrite salvaged data to fresh wallet file. | // Rewrite salvaged data to fresh wallet file. | ||||
// Set -rescan so any missing transactions will be found. | // Set -rescan so any missing transactions will be found. | ||||
int64_t now = GetTime(); | int64_t now = GetTime(); | ||||
newFilename = strprintf("%s.%d.bak", filename, now); | newFilename = strprintf("%s.%d.bak", filename, now); | ||||
▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | bool BerkeleyBatch::Recover(const fs::path &file_path, void *callbackDataIn, | ||||
pdbCopy->close(0); | pdbCopy->close(0); | ||||
return fSuccess; | return fSuccess; | ||||
} | } | ||||
bool BerkeleyBatch::VerifyEnvironment(const fs::path &file_path, | bool BerkeleyBatch::VerifyEnvironment(const fs::path &file_path, | ||||
std::string &errorStr) { | std::string &errorStr) { | ||||
std::string walletFile; | std::string walletFile; | ||||
BerkeleyEnvironment *env = GetWalletEnv(file_path, walletFile); | std::shared_ptr<BerkeleyEnvironment> env = | ||||
GetWalletEnv(file_path, walletFile); | |||||
fs::path walletDir = env->Directory(); | fs::path walletDir = env->Directory(); | ||||
LogPrintf("Using BerkeleyDB version %s\n", DbEnv::version(0, 0, 0)); | LogPrintf("Using BerkeleyDB version %s\n", DbEnv::version(0, 0, 0)); | ||||
LogPrintf("Using wallet %s\n", walletFile); | LogPrintf("Using wallet %s\n", walletFile); | ||||
// Wallet file must be a plain filename without a directory | // Wallet file must be a plain filename without a directory | ||||
if (walletFile != fs::basename(walletFile) + fs::extension(walletFile)) { | if (walletFile != fs::basename(walletFile) + fs::extension(walletFile)) { | ||||
errorStr = strprintf(_("Wallet %s resides outside wallet directory %s"), | errorStr = strprintf(_("Wallet %s resides outside wallet directory %s"), | ||||
Show All 9 Lines | bool BerkeleyBatch::VerifyEnvironment(const fs::path &file_path, | ||||
return true; | return true; | ||||
} | } | ||||
bool BerkeleyBatch::VerifyDatabaseFile( | bool BerkeleyBatch::VerifyDatabaseFile( | ||||
const fs::path &file_path, std::string &warningStr, std::string &errorStr, | const fs::path &file_path, std::string &warningStr, std::string &errorStr, | ||||
BerkeleyEnvironment::recoverFunc_type recoverFunc) { | BerkeleyEnvironment::recoverFunc_type recoverFunc) { | ||||
std::string walletFile; | std::string walletFile; | ||||
BerkeleyEnvironment *env = GetWalletEnv(file_path, walletFile); | std::shared_ptr<BerkeleyEnvironment> env = | ||||
GetWalletEnv(file_path, walletFile); | |||||
fs::path walletDir = env->Directory(); | fs::path walletDir = env->Directory(); | ||||
if (fs::exists(walletDir / walletFile)) { | if (fs::exists(walletDir / walletFile)) { | ||||
std::string backup_filename; | std::string backup_filename; | ||||
BerkeleyEnvironment::VerifyResult r = | BerkeleyEnvironment::VerifyResult r = | ||||
env->Verify(walletFile, recoverFunc, backup_filename); | env->Verify(walletFile, recoverFunc, backup_filename); | ||||
if (r == BerkeleyEnvironment::VerifyResult::RECOVER_OK) { | if (r == BerkeleyEnvironment::VerifyResult::RECOVER_OK) { | ||||
warningStr = strprintf( | warningStr = strprintf( | ||||
▲ Show 20 Lines • Show All 95 Lines • ▼ Show 20 Lines | void BerkeleyEnvironment::CheckpointLSN(const std::string &strFile) { | ||||
dbenv->lsn_reset(strFile.c_str(), 0); | dbenv->lsn_reset(strFile.c_str(), 0); | ||||
} | } | ||||
BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase &database, const char *pszMode, | BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase &database, const char *pszMode, | ||||
bool fFlushOnCloseIn) | bool fFlushOnCloseIn) | ||||
: pdb(nullptr), activeTxn(nullptr) { | : pdb(nullptr), activeTxn(nullptr) { | ||||
fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w')); | fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w')); | ||||
fFlushOnClose = fFlushOnCloseIn; | fFlushOnClose = fFlushOnCloseIn; | ||||
env = database.env; | env = database.env.get(); | ||||
if (database.IsDummy()) { | if (database.IsDummy()) { | ||||
return; | return; | ||||
} | } | ||||
const std::string &strFilename = database.strFile; | const std::string &strFilename = database.strFile; | ||||
bool fCreate = strchr(pszMode, 'c') != nullptr; | bool fCreate = strchr(pszMode, 'c') != nullptr; | ||||
unsigned int nFlags = DB_THREAD; | unsigned int nFlags = DB_THREAD; | ||||
▲ Show 20 Lines • Show All 51 Lines • ▼ Show 20 Lines | if (fCreate) { | ||||
// paths. In the future a more relaxed check for equal inode and | // paths. In the future a more relaxed check for equal inode and | ||||
// device ids could be done instead, which would allow opening | // device ids could be done instead, which would allow opening | ||||
// different backup copies of a wallet at the same time. Maybe even | // different backup copies of a wallet at the same time. Maybe even | ||||
// more ideally, an exclusive lock for accessing the database could | // more ideally, an exclusive lock for accessing the database could | ||||
// be implemented, so no equality checks are needed at all. (Newer | // be implemented, so no equality checks are needed at all. (Newer | ||||
// versions of BDB have an set_lk_exclusive method for this | // versions of BDB have an set_lk_exclusive method for this | ||||
// purpose, but the older version we use does not.) | // purpose, but the older version we use does not.) | ||||
for (const auto &dbenv : g_dbenvs) { | for (const auto &dbenv : g_dbenvs) { | ||||
CheckUniqueFileid(dbenv.second, strFilename, *pdb_temp, | CheckUniqueFileid(*dbenv.second.lock().get(), strFilename, | ||||
this->env->m_fileids[strFilename]); | *pdb_temp, this->env->m_fileids[strFilename]); | ||||
} | } | ||||
pdb = pdb_temp.release(); | pdb = pdb_temp.release(); | ||||
database.m_db.reset(pdb); | database.m_db.reset(pdb); | ||||
if (fCreate && !Exists(std::string("version"))) { | if (fCreate && !Exists(std::string("version"))) { | ||||
bool fTmp = fReadOnly; | bool fTmp = fReadOnly; | ||||
fReadOnly = false; | fReadOnly = false; | ||||
▲ Show 20 Lines • Show All 87 Lines • ▼ Show 20 Lines | void BerkeleyEnvironment::ReloadDbEnv() { | ||||
Reset(); | Reset(); | ||||
Open(true); | Open(true); | ||||
} | } | ||||
bool BerkeleyBatch::Rewrite(BerkeleyDatabase &database, const char *pszSkip) { | bool BerkeleyBatch::Rewrite(BerkeleyDatabase &database, const char *pszSkip) { | ||||
if (database.IsDummy()) { | if (database.IsDummy()) { | ||||
return true; | return true; | ||||
} | } | ||||
BerkeleyEnvironment *env = database.env; | BerkeleyEnvironment *env = database.env.get(); | ||||
const std::string &strFile = database.strFile; | const std::string &strFile = database.strFile; | ||||
while (true) { | while (true) { | ||||
{ | { | ||||
LOCK(cs_db); | LOCK(cs_db); | ||||
if (!env->mapFileUseCount.count(strFile) || | if (!env->mapFileUseCount.count(strFile) || | ||||
env->mapFileUseCount[strFile] == 0) { | env->mapFileUseCount[strFile] == 0) { | ||||
// Flush log data to the dat file | // Flush log data to the dat file | ||||
env->CloseDb(strFile); | env->CloseDb(strFile); | ||||
▲ Show 20 Lines • Show All 145 Lines • ▼ Show 20 Lines | void BerkeleyEnvironment::Flush(bool fShutdown) { | ||||
} | } | ||||
} | } | ||||
bool BerkeleyBatch::PeriodicFlush(BerkeleyDatabase &database) { | bool BerkeleyBatch::PeriodicFlush(BerkeleyDatabase &database) { | ||||
if (database.IsDummy()) { | if (database.IsDummy()) { | ||||
return true; | return true; | ||||
} | } | ||||
bool ret = false; | bool ret = false; | ||||
BerkeleyEnvironment *env = database.env; | BerkeleyEnvironment *env = database.env.get(); | ||||
const std::string &strFile = database.strFile; | const std::string &strFile = database.strFile; | ||||
TRY_LOCK(cs_db, lockDb); | TRY_LOCK(cs_db, lockDb); | ||||
if (lockDb) { | if (lockDb) { | ||||
// Don't do this if any databases are in use | // Don't do this if any databases are in use | ||||
int nRefCount = 0; | int nRefCount = 0; | ||||
std::map<std::string, int>::iterator mit = env->mapFileUseCount.begin(); | std::map<std::string, int>::iterator mit = env->mapFileUseCount.begin(); | ||||
while (mit != env->mapFileUseCount.end()) { | while (mit != env->mapFileUseCount.end()) { | ||||
nRefCount += (*mit).second; | nRefCount += (*mit).second; | ||||
▲ Show 20 Lines • Show All 97 Lines • Show Last 20 Lines |