Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/db.cpp
Show All 24 Lines | |||||
//! doesn't, throw an error. BDB caches do not work properly when more than one | //! doesn't, throw an error. BDB caches do not work properly when more than one | ||||
//! open database has the same fileid (values written to one database may show | //! open database has the same fileid (values written to one database may show | ||||
//! up in reads to other databases). | //! up in reads to other databases). | ||||
//! | //! | ||||
//! BerkeleyDB generates unique fileids by default | //! BerkeleyDB generates unique fileids by default | ||||
//! (https://docs.oracle.com/cd/E17275_01/html/programmer_reference/program_copy.html), | //! (https://docs.oracle.com/cd/E17275_01/html/programmer_reference/program_copy.html), | ||||
//! so bitcoin should never create different databases with the same fileid, but | //! so bitcoin should never create different databases with the same fileid, but | ||||
//! this error can be triggered if users manually copy database files. | //! this error can be triggered if users manually copy database files. | ||||
void CheckUniqueFileid(const CDBEnv &env, const std::string &filename, Db &db) { | void CheckUniqueFileid(const BerkeleyEnvironment &env, | ||||
const std::string &filename, Db &db) { | |||||
if (env.IsMock()) { | if (env.IsMock()) { | ||||
return; | return; | ||||
} | } | ||||
u_int8_t fileid[DB_FILE_ID_LEN]; | u_int8_t fileid[DB_FILE_ID_LEN]; | ||||
int ret = db.get_mpf()->get_fileid(fileid); | int ret = db.get_mpf()->get_fileid(fileid); | ||||
if (ret != 0) { | if (ret != 0) { | ||||
throw std::runtime_error( | throw std::runtime_error(strprintf( | ||||
strprintf("CDB: Can't open database %s (get_fileid failed with %d)", | "BerkeleyBatch: Can't open database %s (get_fileid failed with %d)", | ||||
filename, ret)); | filename, ret)); | ||||
} | } | ||||
for (const auto &item : env.mapDb) { | for (const auto &item : env.mapDb) { | ||||
u_int8_t item_fileid[DB_FILE_ID_LEN]; | u_int8_t item_fileid[DB_FILE_ID_LEN]; | ||||
if (item.second && | if (item.second && | ||||
item.second->get_mpf()->get_fileid(item_fileid) == 0 && | item.second->get_mpf()->get_fileid(item_fileid) == 0 && | ||||
memcmp(fileid, item_fileid, sizeof(fileid)) == 0) { | memcmp(fileid, item_fileid, sizeof(fileid)) == 0) { | ||||
const char *item_filename = nullptr; | const char *item_filename = nullptr; | ||||
item.second->get_dbname(&item_filename, nullptr); | item.second->get_dbname(&item_filename, nullptr); | ||||
throw std::runtime_error(strprintf( | throw std::runtime_error(strprintf( | ||||
"CDB: Can't open database %s (duplicates fileid %s from %s)", | "BerkeleyBatch: Can't open database %s (duplicates fileid %s " | ||||
"from %s)", | |||||
filename, | filename, | ||||
HexStr(std::begin(item_fileid), std::end(item_fileid)), | HexStr(std::begin(item_fileid), std::end(item_fileid)), | ||||
item_filename ? item_filename : "(unknown database)")); | item_filename ? item_filename : "(unknown database)")); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
CCriticalSection cs_db; | CCriticalSection cs_db; | ||||
//!< Map from directory name to open db environment. | //!< Map from directory name to open db environment. | ||||
std::map<std::string, CDBEnv> g_dbenvs; | std::map<std::string, BerkeleyEnvironment> g_dbenvs; | ||||
} // namespace | } // namespace | ||||
CDBEnv *GetWalletEnv(const fs::path &wallet_path, | BerkeleyEnvironment *GetWalletEnv(const fs::path &wallet_path, | ||||
std::string &database_filename) { | std::string &database_filename) { | ||||
fs::path env_directory; | fs::path env_directory; | ||||
if (fs::is_regular_file(wallet_path)) { | if (fs::is_regular_file(wallet_path)) { | ||||
// Special case for backwards compatibility: if wallet path points to an | // Special case for backwards compatibility: if wallet path points to an | ||||
// existing file, treat it as the path to a BDB data file in a parent | // existing file, treat it as the path to a BDB data file in a parent | ||||
// directory that also contains BDB log files. | // directory that also contains BDB log files. | ||||
env_directory = wallet_path.parent_path(); | env_directory = wallet_path.parent_path(); | ||||
database_filename = wallet_path.filename().string(); | database_filename = wallet_path.filename().string(); | ||||
} else { | } else { | ||||
// Normal case: Interpret wallet path as a directory path containing | // Normal case: Interpret wallet path as a directory path containing | ||||
// data and log files. | // data and log files. | ||||
env_directory = wallet_path; | env_directory = wallet_path; | ||||
database_filename = "wallet.dat"; | database_filename = "wallet.dat"; | ||||
} | } | ||||
LOCK(cs_db); | LOCK(cs_db); | ||||
// Note: An ununsed temporary CDBEnv object may be created inside the | // Note: An ununsed temporary BerkeleyEnvironment object may be created | ||||
// emplace function if the key already exists. This is a little inefficient, | // inside the emplace function if the key already exists. This is a little | ||||
// but not a big concern since the map will be changed in the future to hold | // inefficient, but not a big concern since the map will be changed in the | ||||
// pointers instead of objects, anyway. | // future to hold pointers instead of objects, anyway. | ||||
return &g_dbenvs | return &g_dbenvs | ||||
.emplace(std::piecewise_construct, | .emplace(std::piecewise_construct, | ||||
std::forward_as_tuple(env_directory.string()), | std::forward_as_tuple(env_directory.string()), | ||||
std::forward_as_tuple(env_directory)) | std::forward_as_tuple(env_directory)) | ||||
.first->second; | .first->second; | ||||
} | } | ||||
// | // | ||||
// CDB | // BerkeleyBatch | ||||
// | // | ||||
void CDBEnv::Close() { | void BerkeleyEnvironment::Close() { | ||||
if (!fDbEnvInit) { | if (!fDbEnvInit) { | ||||
return; | return; | ||||
} | } | ||||
fDbEnvInit = false; | fDbEnvInit = false; | ||||
for (auto &db : mapDb) { | for (auto &db : mapDb) { | ||||
auto count = mapFileUseCount.find(db.first); | auto count = mapFileUseCount.find(db.first); | ||||
assert(count == mapFileUseCount.end() || count->second == 0); | assert(count == mapFileUseCount.end() || count->second == 0); | ||||
if (db.second) { | if (db.second) { | ||||
db.second->close(0); | db.second->close(0); | ||||
delete db.second; | delete db.second; | ||||
db.second = nullptr; | db.second = nullptr; | ||||
} | } | ||||
} | } | ||||
int ret = dbenv->close(0); | int ret = dbenv->close(0); | ||||
if (ret != 0) { | if (ret != 0) { | ||||
LogPrintf("CDBEnv::EnvShutdown: Error %d shutting down database " | LogPrintf("BerkeleyEnvironment::EnvShutdown: Error %d shutting down " | ||||
"environment: %s\n", | "database environment: %s\n", | ||||
ret, DbEnv::strerror(ret)); | ret, DbEnv::strerror(ret)); | ||||
} | } | ||||
if (!fMockDb) { | if (!fMockDb) { | ||||
DbEnv(uint32_t(0)).remove(strPath.c_str(), 0); | DbEnv(u_int32_t(0)).remove(strPath.c_str(), 0); | ||||
} | } | ||||
} | } | ||||
void CDBEnv::Reset() { | void BerkeleyEnvironment::Reset() { | ||||
dbenv.reset(new DbEnv(DB_CXX_NO_EXCEPTIONS)); | dbenv.reset(new DbEnv(DB_CXX_NO_EXCEPTIONS)); | ||||
fDbEnvInit = false; | fDbEnvInit = false; | ||||
fMockDb = false; | fMockDb = false; | ||||
} | } | ||||
CDBEnv::CDBEnv(const fs::path &dir_path) : strPath(dir_path.string()) { | BerkeleyEnvironment::BerkeleyEnvironment(const fs::path &dir_path) | ||||
: strPath(dir_path.string()) { | |||||
Reset(); | Reset(); | ||||
} | } | ||||
CDBEnv::~CDBEnv() { | BerkeleyEnvironment::~BerkeleyEnvironment() { | ||||
Close(); | Close(); | ||||
} | } | ||||
bool CDBEnv::Open(bool retry) { | bool BerkeleyEnvironment::Open(bool retry) { | ||||
if (fDbEnvInit) { | if (fDbEnvInit) { | ||||
return true; | return true; | ||||
} | } | ||||
boost::this_thread::interruption_point(); | boost::this_thread::interruption_point(); | ||||
fs::path pathIn = strPath; | fs::path pathIn = strPath; | ||||
TryCreateDirectories(pathIn); | TryCreateDirectories(pathIn); | ||||
if (!LockDirectory(pathIn, ".walletlock")) { | if (!LockDirectory(pathIn, ".walletlock")) { | ||||
LogPrintf("Cannot obtain a lock on wallet directory %s. Another " | LogPrintf("Cannot obtain a lock on wallet directory %s. Another " | ||||
"instance of bitcoin may be using it.\n", | "instance of bitcoin may be using it.\n", | ||||
strPath); | strPath); | ||||
return false; | return false; | ||||
} | } | ||||
fs::path pathLogDir = pathIn / "database"; | fs::path pathLogDir = pathIn / "database"; | ||||
TryCreateDirectories(pathLogDir); | TryCreateDirectories(pathLogDir); | ||||
fs::path pathErrorFile = pathIn / "db.log"; | fs::path pathErrorFile = pathIn / "db.log"; | ||||
LogPrintf("CDBEnv::Open: LogDir=%s ErrorFile=%s\n", pathLogDir.string(), | LogPrintf("BerkeleyEnvironment::Open: LogDir=%s ErrorFile=%s\n", | ||||
pathErrorFile.string()); | pathLogDir.string(), pathErrorFile.string()); | ||||
unsigned int nEnvFlags = 0; | unsigned int nEnvFlags = 0; | ||||
if (gArgs.GetBoolArg("-privdb", DEFAULT_WALLET_PRIVDB)) { | if (gArgs.GetBoolArg("-privdb", DEFAULT_WALLET_PRIVDB)) { | ||||
nEnvFlags |= DB_PRIVATE; | nEnvFlags |= DB_PRIVATE; | ||||
} | } | ||||
dbenv->set_lg_dir(pathLogDir.string().c_str()); | dbenv->set_lg_dir(pathLogDir.string().c_str()); | ||||
// 1 MiB should be enough for just the wallet | // 1 MiB should be enough for just the wallet | ||||
Show All 9 Lines | bool BerkeleyEnvironment::Open(bool retry) { | ||||
dbenv->log_set_config(DB_LOG_AUTO_REMOVE, 1); | dbenv->log_set_config(DB_LOG_AUTO_REMOVE, 1); | ||||
int ret = | int ret = | ||||
dbenv->open(strPath.c_str(), | dbenv->open(strPath.c_str(), | ||||
DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | | DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | | ||||
DB_INIT_TXN | DB_THREAD | DB_RECOVER | nEnvFlags, | DB_INIT_TXN | DB_THREAD | DB_RECOVER | nEnvFlags, | ||||
S_IRUSR | S_IWUSR); | S_IRUSR | S_IWUSR); | ||||
if (ret != 0) { | if (ret != 0) { | ||||
dbenv->close(0); | dbenv->close(0); | ||||
LogPrintf("CDBEnv::Open: Error %d opening database environment: %s\n", | LogPrintf("BerkeleyEnvironment::Open: Error %d opening database " | ||||
"environment: %s\n", | |||||
ret, DbEnv::strerror(ret)); | ret, DbEnv::strerror(ret)); | ||||
if (retry) { | if (retry) { | ||||
// try moving the database env out of the way | // try moving the database env out of the way | ||||
fs::path pathDatabaseBak = | fs::path pathDatabaseBak = | ||||
pathIn / strprintf("database.%d.bak", GetTime()); | pathIn / strprintf("database.%d.bak", GetTime()); | ||||
try { | try { | ||||
fs::rename(pathLogDir, pathDatabaseBak); | fs::rename(pathLogDir, pathDatabaseBak); | ||||
LogPrintf("Moved old %s to %s. Retrying.\n", | LogPrintf("Moved old %s to %s. Retrying.\n", | ||||
Show All 13 Lines | if (ret != 0) { | ||||
} | } | ||||
} | } | ||||
fDbEnvInit = true; | fDbEnvInit = true; | ||||
fMockDb = false; | fMockDb = false; | ||||
return true; | return true; | ||||
} | } | ||||
void CDBEnv::MakeMock() { | void BerkeleyEnvironment::MakeMock() { | ||||
if (fDbEnvInit) { | if (fDbEnvInit) { | ||||
throw std::runtime_error("CDBEnv::MakeMock: Already initialized"); | throw std::runtime_error( | ||||
"BerkeleyEnvironment::MakeMock: Already initialized"); | |||||
} | } | ||||
boost::this_thread::interruption_point(); | boost::this_thread::interruption_point(); | ||||
LogPrint(BCLog::DB, "CDBEnv::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); | ||||
dbenv->set_lk_max_locks(10000); | dbenv->set_lk_max_locks(10000); | ||||
dbenv->set_lk_max_objects(10000); | dbenv->set_lk_max_objects(10000); | ||||
dbenv->set_flags(DB_AUTO_COMMIT, 1); | dbenv->set_flags(DB_AUTO_COMMIT, 1); | ||||
dbenv->log_set_config(DB_LOG_IN_MEMORY, 1); | dbenv->log_set_config(DB_LOG_IN_MEMORY, 1); | ||||
int ret = | int ret = | ||||
dbenv->open(nullptr, | dbenv->open(nullptr, | ||||
DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | | DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | | ||||
DB_INIT_TXN | DB_THREAD | DB_PRIVATE, | DB_INIT_TXN | DB_THREAD | DB_PRIVATE, | ||||
S_IRUSR | S_IWUSR); | S_IRUSR | S_IWUSR); | ||||
if (ret > 0) { | if (ret > 0) { | ||||
throw std::runtime_error(strprintf( | throw std::runtime_error( | ||||
"CDBEnv::MakeMock: Error %d opening database environment.", ret)); | strprintf("BerkeleyEnvironment::MakeMock: Error %d opening " | ||||
"database environment.", | |||||
ret)); | |||||
} | } | ||||
fDbEnvInit = true; | fDbEnvInit = true; | ||||
fMockDb = true; | fMockDb = true; | ||||
} | } | ||||
CDBEnv::VerifyResult CDBEnv::Verify(const std::string &strFile, | BerkeleyEnvironment::VerifyResult | ||||
BerkeleyEnvironment::Verify(const std::string &strFile, | |||||
recoverFunc_type recoverFunc, | recoverFunc_type recoverFunc, | ||||
std::string &out_backup_filename) { | std::string &out_backup_filename) { | ||||
LOCK(cs_db); | LOCK(cs_db); | ||||
assert(mapFileUseCount.count(strFile) == 0); | assert(mapFileUseCount.count(strFile) == 0); | ||||
Db db(dbenv.get(), 0); | Db db(dbenv.get(), 0); | ||||
int result = db.verify(strFile.c_str(), nullptr, nullptr, 0); | int result = db.verify(strFile.c_str(), nullptr, nullptr, 0); | ||||
if (result == 0) { | if (result == 0) { | ||||
return VerifyResult::VERIFY_OK; | return VerifyResult::VERIFY_OK; | ||||
} else if (recoverFunc == nullptr) { | } else if (recoverFunc == nullptr) { | ||||
return VerifyResult::RECOVER_FAIL; | return VerifyResult::RECOVER_FAIL; | ||||
} | } | ||||
// Try to recover: | // Try to recover: | ||||
bool fRecovered = | bool fRecovered = | ||||
(*recoverFunc)(fs::path(strPath) / strFile, out_backup_filename); | (*recoverFunc)(fs::path(strPath) / strFile, out_backup_filename); | ||||
return (fRecovered ? VerifyResult::RECOVER_OK : VerifyResult::RECOVER_FAIL); | return (fRecovered ? VerifyResult::RECOVER_OK : VerifyResult::RECOVER_FAIL); | ||||
} | } | ||||
bool CDB::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; | ||||
CDBEnv *env = GetWalletEnv(file_path, filename); | 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); | ||||
int result = env->dbenv->dbrename(nullptr, filename.c_str(), nullptr, | int result = env->dbenv->dbrename(nullptr, filename.c_str(), nullptr, | ||||
newFilename.c_str(), DB_AUTO_COMMIT); | newFilename.c_str(), DB_AUTO_COMMIT); | ||||
if (result == 0) { | if (result == 0) { | ||||
LogPrintf("Renamed %s to %s\n", filename, newFilename); | LogPrintf("Renamed %s to %s\n", filename, newFilename); | ||||
} else { | } else { | ||||
LogPrintf("Failed to rename %s to %s\n", filename, newFilename); | LogPrintf("Failed to rename %s to %s\n", filename, newFilename); | ||||
return false; | return false; | ||||
} | } | ||||
std::vector<CDBEnv::KeyValPair> salvagedData; | std::vector<BerkeleyEnvironment::KeyValPair> salvagedData; | ||||
bool fSuccess = env->Salvage(newFilename, true, salvagedData); | bool fSuccess = env->Salvage(newFilename, true, salvagedData); | ||||
if (salvagedData.empty()) { | if (salvagedData.empty()) { | ||||
LogPrintf("Salvage(aggressive) found no records in %s.\n", newFilename); | LogPrintf("Salvage(aggressive) found no records in %s.\n", newFilename); | ||||
return false; | return false; | ||||
} | } | ||||
LogPrintf("Salvage(aggressive) found %u records\n", salvagedData.size()); | LogPrintf("Salvage(aggressive) found %u records\n", salvagedData.size()); | ||||
std::unique_ptr<Db> pdbCopy = std::make_unique<Db>(env->dbenv.get(), 0); | std::unique_ptr<Db> pdbCopy = std::make_unique<Db>(env->dbenv.get(), 0); | ||||
int ret = pdbCopy->open(nullptr, // Txn pointer | int ret = pdbCopy->open(nullptr, // Txn pointer | ||||
filename.c_str(), // Filename | filename.c_str(), // Filename | ||||
"main", // Logical db name | "main", // Logical db name | ||||
DB_BTREE, // Database type | DB_BTREE, // Database type | ||||
DB_CREATE, // Flags | DB_CREATE, // Flags | ||||
0); | 0); | ||||
if (ret > 0) { | if (ret > 0) { | ||||
LogPrintf("Cannot create database file %s\n", filename); | LogPrintf("Cannot create database file %s\n", filename); | ||||
pdbCopy->close(0); | pdbCopy->close(0); | ||||
return false; | return false; | ||||
} | } | ||||
DbTxn *ptxn = env->TxnBegin(); | DbTxn *ptxn = env->TxnBegin(); | ||||
for (CDBEnv::KeyValPair &row : salvagedData) { | for (BerkeleyEnvironment::KeyValPair &row : salvagedData) { | ||||
if (recoverKVcallback) { | if (recoverKVcallback) { | ||||
CDataStream ssKey(row.first, SER_DISK, CLIENT_VERSION); | CDataStream ssKey(row.first, SER_DISK, CLIENT_VERSION); | ||||
CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION); | CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION); | ||||
if (!(*recoverKVcallback)(callbackDataIn, ssKey, ssValue)) { | if (!(*recoverKVcallback)(callbackDataIn, ssKey, ssValue)) { | ||||
continue; | continue; | ||||
} | } | ||||
} | } | ||||
Dbt datKey(&row.first[0], row.first.size()); | Dbt datKey(&row.first[0], row.first.size()); | ||||
Dbt datValue(&row.second[0], row.second.size()); | Dbt datValue(&row.second[0], row.second.size()); | ||||
int ret2 = pdbCopy->put(ptxn, &datKey, &datValue, DB_NOOVERWRITE); | int ret2 = pdbCopy->put(ptxn, &datKey, &datValue, DB_NOOVERWRITE); | ||||
if (ret2 > 0) { | if (ret2 > 0) { | ||||
fSuccess = false; | fSuccess = false; | ||||
} | } | ||||
} | } | ||||
ptxn->commit(0); | ptxn->commit(0); | ||||
pdbCopy->close(0); | pdbCopy->close(0); | ||||
return fSuccess; | return fSuccess; | ||||
} | } | ||||
bool CDB::VerifyEnvironment(const fs::path &file_path, std::string &errorStr) { | bool BerkeleyBatch::VerifyEnvironment(const fs::path &file_path, | ||||
std::string &errorStr) { | |||||
std::string walletFile; | std::string walletFile; | ||||
CDBEnv *env = GetWalletEnv(file_path, walletFile); | 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"), | ||||
walletFile, walletDir.string()); | walletFile, walletDir.string()); | ||||
return false; | return false; | ||||
} | } | ||||
if (!env->Open(true /* retry */)) { | if (!env->Open(true /* retry */)) { | ||||
errorStr = strprintf( | errorStr = strprintf( | ||||
_("Error initializing wallet database environment %s!"), walletDir); | _("Error initializing wallet database environment %s!"), walletDir); | ||||
return false; | return false; | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
bool CDB::VerifyDatabaseFile(const fs::path &file_path, std::string &warningStr, | bool BerkeleyBatch::VerifyDatabaseFile( | ||||
std::string &errorStr, | const fs::path &file_path, std::string &warningStr, std::string &errorStr, | ||||
CDBEnv::recoverFunc_type recoverFunc) { | BerkeleyEnvironment::recoverFunc_type recoverFunc) { | ||||
std::string walletFile; | std::string walletFile; | ||||
CDBEnv *env = GetWalletEnv(file_path, walletFile); | 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; | ||||
CDBEnv::VerifyResult r = | BerkeleyEnvironment::VerifyResult r = | ||||
env->Verify(walletFile, recoverFunc, backup_filename); | env->Verify(walletFile, recoverFunc, backup_filename); | ||||
if (r == CDBEnv::VerifyResult::RECOVER_OK) { | if (r == BerkeleyEnvironment::VerifyResult::RECOVER_OK) { | ||||
warningStr = strprintf( | warningStr = strprintf( | ||||
_("Warning: Wallet file corrupt, data salvaged!" | _("Warning: Wallet file corrupt, data salvaged! Original %s " | ||||
" Original %s saved as %s in %s; if" | "saved as %s in %s; if your balance or transactions are " | ||||
" your balance or transactions are incorrect you should" | "incorrect you should restore from a backup."), | ||||
" restore from a backup."), | |||||
walletFile, backup_filename, walletDir); | walletFile, backup_filename, walletDir); | ||||
} | } | ||||
if (r == CDBEnv::VerifyResult::RECOVER_FAIL) { | if (r == BerkeleyEnvironment::VerifyResult::RECOVER_FAIL) { | ||||
errorStr = strprintf(_("%s corrupt, salvage failed"), walletFile); | errorStr = strprintf(_("%s corrupt, salvage failed"), walletFile); | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
// also return true if files does not exists | // also return true if files does not exists | ||||
return true; | return true; | ||||
} | } | ||||
/* End of headers, beginning of key/value data */ | /* End of headers, beginning of key/value data */ | ||||
static const char *HEADER_END = "HEADER=END"; | static const char *HEADER_END = "HEADER=END"; | ||||
/* End of key/value data */ | /* End of key/value data */ | ||||
static const char *DATA_END = "DATA=END"; | static const char *DATA_END = "DATA=END"; | ||||
bool CDBEnv::Salvage(const std::string &strFile, bool fAggressive, | bool BerkeleyEnvironment::Salvage( | ||||
std::vector<CDBEnv::KeyValPair> &vResult) { | const std::string &strFile, bool fAggressive, | ||||
std::vector<BerkeleyEnvironment::KeyValPair> &vResult) { | |||||
LOCK(cs_db); | LOCK(cs_db); | ||||
assert(mapFileUseCount.count(strFile) == 0); | assert(mapFileUseCount.count(strFile) == 0); | ||||
u_int32_t flags = DB_SALVAGE; | u_int32_t flags = DB_SALVAGE; | ||||
if (fAggressive) { | if (fAggressive) { | ||||
flags |= DB_AGGRESSIVE; | flags |= DB_AGGRESSIVE; | ||||
} | } | ||||
std::stringstream strDump; | std::stringstream strDump; | ||||
Db db(dbenv.get(), 0); | Db db(dbenv.get(), 0); | ||||
int result = db.verify(strFile.c_str(), nullptr, &strDump, flags); | int result = db.verify(strFile.c_str(), nullptr, &strDump, flags); | ||||
if (result == DB_VERIFY_BAD) { | if (result == DB_VERIFY_BAD) { | ||||
LogPrintf("CDBEnv::Salvage: Database salvage found errors, all data " | LogPrintf("BerkeleyEnvironment::Salvage: Database salvage found " | ||||
"may not be recoverable.\n"); | "errors, all data may not be recoverable.\n"); | ||||
if (!fAggressive) { | if (!fAggressive) { | ||||
LogPrintf("CDBEnv::Salvage: Rerun with aggressive mode to ignore " | LogPrintf("BerkeleyEnvironment::Salvage: Rerun with aggressive " | ||||
"errors and continue.\n"); | "mode to ignore errors and continue.\n"); | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
if (result != 0 && result != DB_VERIFY_BAD) { | if (result != 0 && result != DB_VERIFY_BAD) { | ||||
LogPrintf("CDBEnv::Salvage: Database salvage failed with result %d.\n", | LogPrintf("BerkeleyEnvironment::Salvage: Database salvage failed with " | ||||
"result %d.\n", | |||||
result); | result); | ||||
return false; | return false; | ||||
} | } | ||||
// Format of bdb dump is ascii lines: | // Format of bdb dump is ascii lines: | ||||
// header lines... | // header lines... | ||||
// HEADER=END | // HEADER=END | ||||
// hexadecimal key | // hexadecimal key | ||||
Show All 11 Lines | bool BerkeleyEnvironment::Salvage( | ||||
while (!strDump.eof() && keyHex != DATA_END) { | while (!strDump.eof() && keyHex != DATA_END) { | ||||
getline(strDump, keyHex); | getline(strDump, keyHex); | ||||
if (keyHex != DATA_END) { | if (keyHex != DATA_END) { | ||||
if (strDump.eof()) { | if (strDump.eof()) { | ||||
break; | break; | ||||
} | } | ||||
getline(strDump, valueHex); | getline(strDump, valueHex); | ||||
if (valueHex == DATA_END) { | if (valueHex == DATA_END) { | ||||
LogPrintf("CDBEnv::Salvage: WARNING: Number of keys in data " | LogPrintf("BerkeleyEnvironment::Salvage: WARNING: Number of " | ||||
"does not match number of values.\n"); | "keys in data does not match number of values.\n"); | ||||
break; | break; | ||||
} | } | ||||
vResult.push_back(make_pair(ParseHex(keyHex), ParseHex(valueHex))); | vResult.push_back(make_pair(ParseHex(keyHex), ParseHex(valueHex))); | ||||
} | } | ||||
} | } | ||||
if (keyHex != DATA_END) { | if (keyHex != DATA_END) { | ||||
LogPrintf("CDBEnv::Salvage: WARNING: Unexpected end of file while " | LogPrintf("BerkeleyEnvironment::Salvage: WARNING: Unexpected end of " | ||||
"reading salvage output.\n"); | "file while reading salvage output.\n"); | ||||
return false; | return false; | ||||
} | } | ||||
return (result == 0); | return (result == 0); | ||||
} | } | ||||
void CDBEnv::CheckpointLSN(const std::string &strFile) { | void BerkeleyEnvironment::CheckpointLSN(const std::string &strFile) { | ||||
dbenv->txn_checkpoint(0, 0, 0); | dbenv->txn_checkpoint(0, 0, 0); | ||||
if (fMockDb) { | if (fMockDb) { | ||||
return; | return; | ||||
} | } | ||||
dbenv->lsn_reset(strFile.c_str(), 0); | dbenv->lsn_reset(strFile.c_str(), 0); | ||||
} | } | ||||
CDB::CDB(CWalletDBWrapper &dbw, const char *pszMode, bool fFlushOnCloseIn) | BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase &database, const char *pszMode, | ||||
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 = dbw.env; | env = database.env; | ||||
if (dbw.IsDummy()) { | if (database.IsDummy()) { | ||||
return; | return; | ||||
} | } | ||||
const std::string &strFilename = dbw.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; | ||||
if (fCreate) { | if (fCreate) { | ||||
nFlags |= DB_CREATE; | nFlags |= DB_CREATE; | ||||
} | } | ||||
{ | { | ||||
LOCK(cs_db); | LOCK(cs_db); | ||||
if (!env->Open(false /* retry */)) { | if (!env->Open(false /* retry */)) { | ||||
throw std::runtime_error( | throw std::runtime_error( | ||||
"CDB: Failed to open database environment."); | "BerkeleyBatch: Failed to open database environment."); | ||||
} | } | ||||
pdb = env->mapDb[strFilename]; | pdb = env->mapDb[strFilename]; | ||||
if (pdb == nullptr) { | if (pdb == nullptr) { | ||||
int ret; | int ret; | ||||
std::unique_ptr<Db> pdb_temp = | std::unique_ptr<Db> pdb_temp = | ||||
std::make_unique<Db>(env->dbenv.get(), 0); | std::make_unique<Db>(env->dbenv.get(), 0); | ||||
bool fMockDb = env->IsMock(); | bool fMockDb = env->IsMock(); | ||||
if (fMockDb) { | if (fMockDb) { | ||||
DbMpoolFile *mpf = pdb_temp->get_mpf(); | DbMpoolFile *mpf = pdb_temp->get_mpf(); | ||||
ret = mpf->set_flags(DB_MPOOL_NOFILE, 1); | ret = mpf->set_flags(DB_MPOOL_NOFILE, 1); | ||||
if (ret != 0) { | if (ret != 0) { | ||||
throw std::runtime_error( | throw std::runtime_error( | ||||
strprintf("CDB: Failed to configure for no temp file " | strprintf("BerkeleyBatch: Failed to configure for no " | ||||
"backing for database %s", | "temp file backing for database %s", | ||||
strFilename)); | strFilename)); | ||||
} | } | ||||
} | } | ||||
ret = pdb_temp->open( | ret = pdb_temp->open( | ||||
nullptr, // Txn pointer | nullptr, // Txn pointer | ||||
fMockDb ? nullptr : strFilename.c_str(), // Filename | fMockDb ? nullptr : strFilename.c_str(), // Filename | ||||
fMockDb ? strFilename.c_str() : "main", // Logical db name | fMockDb ? strFilename.c_str() : "main", // Logical db name | ||||
DB_BTREE, // Database type | DB_BTREE, // Database type | ||||
nFlags, // Flags | nFlags, // Flags | ||||
0); | 0); | ||||
if (ret != 0) { | if (ret != 0) { | ||||
throw std::runtime_error(strprintf( | throw std::runtime_error( | ||||
"CDB: Error %d, can't open database %s", ret, strFilename)); | strprintf("BerkeleyBatch: Error %d, can't open database %s", | ||||
ret, strFilename)); | |||||
} | } | ||||
// Call CheckUniqueFileid on the containing BDB environment to | // Call CheckUniqueFileid on the containing BDB environment to | ||||
// avoid BDB data consistency bugs that happen when different data | // avoid BDB data consistency bugs that happen when different data | ||||
// files in the same environment have the same fileid. | // files in the same environment have the same fileid. | ||||
// | // | ||||
// Also call CheckUniqueFileid on all the other g_dbenvs to prevent | // Also call CheckUniqueFileid on all the other g_dbenvs to prevent | ||||
// bitcoin from opening the same data file through another | // bitcoin from opening the same data file through another | ||||
Show All 20 Lines | if (fCreate) { | ||||
fReadOnly = fTmp; | fReadOnly = fTmp; | ||||
} | } | ||||
} | } | ||||
++env->mapFileUseCount[strFilename]; | ++env->mapFileUseCount[strFilename]; | ||||
strFile = strFilename; | strFile = strFilename; | ||||
} | } | ||||
} | } | ||||
void CDB::Flush() { | void BerkeleyBatch::Flush() { | ||||
if (activeTxn) { | if (activeTxn) { | ||||
return; | return; | ||||
} | } | ||||
// Flush database activity from memory pool to disk log | // Flush database activity from memory pool to disk log | ||||
unsigned int nMinutes = 0; | unsigned int nMinutes = 0; | ||||
if (fReadOnly) { | if (fReadOnly) { | ||||
nMinutes = 1; | nMinutes = 1; | ||||
} | } | ||||
env->dbenv->txn_checkpoint( | env->dbenv->txn_checkpoint( | ||||
nMinutes ? gArgs.GetArg("-dblogsize", DEFAULT_WALLET_DBLOGSIZE) * 1024 | nMinutes ? gArgs.GetArg("-dblogsize", DEFAULT_WALLET_DBLOGSIZE) * 1024 | ||||
: 0, | : 0, | ||||
nMinutes, 0); | nMinutes, 0); | ||||
} | } | ||||
void CWalletDBWrapper::IncrementUpdateCounter() { | void BerkeleyDatabase::IncrementUpdateCounter() { | ||||
++nUpdateCounter; | ++nUpdateCounter; | ||||
} | } | ||||
void CDB::Close() { | void BerkeleyBatch::Close() { | ||||
if (!pdb) { | if (!pdb) { | ||||
return; | return; | ||||
} | } | ||||
if (activeTxn) { | if (activeTxn) { | ||||
activeTxn->abort(); | activeTxn->abort(); | ||||
} | } | ||||
activeTxn = nullptr; | activeTxn = nullptr; | ||||
pdb = nullptr; | pdb = nullptr; | ||||
if (fFlushOnClose) { | if (fFlushOnClose) { | ||||
Flush(); | Flush(); | ||||
} | } | ||||
LOCK(cs_db); | LOCK(cs_db); | ||||
--env->mapFileUseCount[strFile]; | --env->mapFileUseCount[strFile]; | ||||
} | } | ||||
void CDBEnv::CloseDb(const std::string &strFile) { | void BerkeleyEnvironment::CloseDb(const std::string &strFile) { | ||||
LOCK(cs_db); | LOCK(cs_db); | ||||
if (mapDb[strFile] != nullptr) { | if (mapDb[strFile] != nullptr) { | ||||
// Close the database handle | // Close the database handle | ||||
Db *pdb = mapDb[strFile]; | Db *pdb = mapDb[strFile]; | ||||
pdb->close(0); | pdb->close(0); | ||||
delete pdb; | delete pdb; | ||||
mapDb[strFile] = nullptr; | mapDb[strFile] = nullptr; | ||||
} | } | ||||
} | } | ||||
bool CDB::Rewrite(CWalletDBWrapper &dbw, const char *pszSkip) { | bool BerkeleyBatch::Rewrite(BerkeleyDatabase &database, const char *pszSkip) { | ||||
if (dbw.IsDummy()) { | if (database.IsDummy()) { | ||||
return true; | return true; | ||||
} | } | ||||
CDBEnv *env = dbw.env; | BerkeleyEnvironment *env = database.env; | ||||
const std::string &strFile = dbw.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); | ||||
env->CheckpointLSN(strFile); | env->CheckpointLSN(strFile); | ||||
env->mapFileUseCount.erase(strFile); | env->mapFileUseCount.erase(strFile); | ||||
bool fSuccess = true; | bool fSuccess = true; | ||||
LogPrintf("CDB::Rewrite: Rewriting %s...\n", strFile); | LogPrintf("BerkeleyBatch::Rewrite: Rewriting %s...\n", strFile); | ||||
std::string strFileRes = strFile + ".rewrite"; | std::string strFileRes = strFile + ".rewrite"; | ||||
{ | { | ||||
// surround usage of db with extra {} | // surround usage of db with extra {} | ||||
CDB db(dbw, "r"); | BerkeleyBatch db(database, "r"); | ||||
std::unique_ptr<Db> pdbCopy = | std::unique_ptr<Db> pdbCopy = | ||||
std::make_unique<Db>(env->dbenv.get(), 0); | std::make_unique<Db>(env->dbenv.get(), 0); | ||||
int ret = pdbCopy->open(nullptr, // Txn pointer | int ret = pdbCopy->open(nullptr, // Txn pointer | ||||
strFileRes.c_str(), // Filename | strFileRes.c_str(), // Filename | ||||
"main", // Logical db name | "main", // Logical db name | ||||
DB_BTREE, // Database type | DB_BTREE, // Database type | ||||
DB_CREATE, // Flags | DB_CREATE, // Flags | ||||
0); | 0); | ||||
if (ret > 0) { | if (ret > 0) { | ||||
LogPrintf( | LogPrintf("BerkeleyBatch::Rewrite: Can't create " | ||||
"CDB::Rewrite: Can't create database file %s\n", | "database file %s\n", | ||||
strFileRes); | strFileRes); | ||||
fSuccess = false; | fSuccess = false; | ||||
} | } | ||||
Dbc *pcursor = db.GetCursor(); | Dbc *pcursor = db.GetCursor(); | ||||
if (pcursor) | if (pcursor) | ||||
while (fSuccess) { | while (fSuccess) { | ||||
CDataStream ssKey(SER_DISK, CLIENT_VERSION); | CDataStream ssKey(SER_DISK, CLIENT_VERSION); | ||||
CDataStream ssValue(SER_DISK, CLIENT_VERSION); | CDataStream ssValue(SER_DISK, CLIENT_VERSION); | ||||
▲ Show 20 Lines • Show All 43 Lines • ▼ Show 20 Lines | while (true) { | ||||
} | } | ||||
Db dbB(env->dbenv.get(), 0); | Db dbB(env->dbenv.get(), 0); | ||||
if (dbB.rename(strFileRes.c_str(), nullptr, strFile.c_str(), | if (dbB.rename(strFileRes.c_str(), nullptr, strFile.c_str(), | ||||
0)) { | 0)) { | ||||
fSuccess = false; | fSuccess = false; | ||||
} | } | ||||
} | } | ||||
if (!fSuccess) { | if (!fSuccess) { | ||||
LogPrintf( | LogPrintf("BerkeleyBatch::Rewrite: Failed to rewrite " | ||||
"CDB::Rewrite: Failed to rewrite database file %s\n", | "database file %s\n", | ||||
strFileRes); | strFileRes); | ||||
} | } | ||||
return fSuccess; | return fSuccess; | ||||
} | } | ||||
} | } | ||||
MilliSleep(100); | MilliSleep(100); | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
void CDBEnv::Flush(bool fShutdown) { | void BerkeleyEnvironment::Flush(bool fShutdown) { | ||||
int64_t nStart = GetTimeMillis(); | int64_t nStart = GetTimeMillis(); | ||||
// Flush log data to the actual data file on all files that are not in use | // Flush log data to the actual data file on all files that are not in use | ||||
LogPrint(BCLog::DB, "CDBEnv::Flush: Flush(%s)%s\n", | LogPrint(BCLog::DB, "BerkeleyEnvironment::Flush: Flush(%s)%s\n", | ||||
fShutdown ? "true" : "false", | fShutdown ? "true" : "false", | ||||
fDbEnvInit ? "" : " database not started"); | fDbEnvInit ? "" : " database not started"); | ||||
if (!fDbEnvInit) { | if (!fDbEnvInit) { | ||||
return; | return; | ||||
} | } | ||||
{ | { | ||||
LOCK(cs_db); | LOCK(cs_db); | ||||
std::map<std::string, int>::iterator mi = mapFileUseCount.begin(); | std::map<std::string, int>::iterator mi = mapFileUseCount.begin(); | ||||
while (mi != mapFileUseCount.end()) { | while (mi != mapFileUseCount.end()) { | ||||
std::string strFile = (*mi).first; | std::string strFile = (*mi).first; | ||||
int nRefCount = (*mi).second; | int nRefCount = (*mi).second; | ||||
LogPrint(BCLog::DB, | LogPrint( | ||||
"CDBEnv::Flush: Flushing %s (refcount = %d)...\n", strFile, | BCLog::DB, | ||||
nRefCount); | "BerkeleyEnvironment::Flush: Flushing %s (refcount = %d)...\n", | ||||
strFile, nRefCount); | |||||
if (nRefCount == 0) { | if (nRefCount == 0) { | ||||
// Move log data to the dat file | // Move log data to the dat file | ||||
CloseDb(strFile); | CloseDb(strFile); | ||||
LogPrint(BCLog::DB, "CDBEnv::Flush: %s checkpoint\n", strFile); | LogPrint(BCLog::DB, | ||||
"BerkeleyEnvironment::Flush: %s checkpoint\n", | |||||
strFile); | |||||
dbenv->txn_checkpoint(0, 0, 0); | dbenv->txn_checkpoint(0, 0, 0); | ||||
LogPrint(BCLog::DB, "CDBEnv::Flush: %s detach\n", strFile); | LogPrint(BCLog::DB, "BerkeleyEnvironment::Flush: %s detach\n", | ||||
strFile); | |||||
if (!fMockDb) { | if (!fMockDb) { | ||||
dbenv->lsn_reset(strFile.c_str(), 0); | dbenv->lsn_reset(strFile.c_str(), 0); | ||||
} | } | ||||
LogPrint(BCLog::DB, "CDBEnv::Flush: %s closed\n", strFile); | LogPrint(BCLog::DB, "BerkeleyEnvironment::Flush: %s closed\n", | ||||
strFile); | |||||
mapFileUseCount.erase(mi++); | mapFileUseCount.erase(mi++); | ||||
} else { | } else { | ||||
mi++; | mi++; | ||||
} | } | ||||
} | } | ||||
LogPrint(BCLog::DB, "CDBEnv::Flush: Flush(%s)%s took %15dms\n", | LogPrint(BCLog::DB, | ||||
"BerkeleyEnvironment::Flush: Flush(%s)%s took %15dms\n", | |||||
fShutdown ? "true" : "false", | fShutdown ? "true" : "false", | ||||
fDbEnvInit ? "" : " database not started", | fDbEnvInit ? "" : " database not started", | ||||
GetTimeMillis() - nStart); | GetTimeMillis() - nStart); | ||||
if (fShutdown) { | if (fShutdown) { | ||||
char **listp; | char **listp; | ||||
if (mapFileUseCount.empty()) { | if (mapFileUseCount.empty()) { | ||||
dbenv->log_archive(&listp, DB_ARCH_REMOVE); | dbenv->log_archive(&listp, DB_ARCH_REMOVE); | ||||
Close(); | Close(); | ||||
if (!fMockDb) { | if (!fMockDb) { | ||||
fs::remove_all(fs::path(strPath) / "database"); | fs::remove_all(fs::path(strPath) / "database"); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
bool CDB::PeriodicFlush(CWalletDBWrapper &dbw) { | bool BerkeleyBatch::PeriodicFlush(BerkeleyDatabase &database) { | ||||
if (dbw.IsDummy()) { | if (database.IsDummy()) { | ||||
return true; | return true; | ||||
} | } | ||||
bool ret = false; | bool ret = false; | ||||
CDBEnv *env = dbw.env; | BerkeleyEnvironment *env = database.env; | ||||
const std::string &strFile = dbw.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; | ||||
mit++; | mit++; | ||||
Show All 17 Lines | if (lockDb) { | ||||
ret = true; | ret = true; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
return ret; | return ret; | ||||
} | } | ||||
bool CWalletDBWrapper::Rewrite(const char *pszSkip) { | bool BerkeleyDatabase::Rewrite(const char *pszSkip) { | ||||
return CDB::Rewrite(*this, pszSkip); | return BerkeleyBatch::Rewrite(*this, pszSkip); | ||||
} | } | ||||
bool CWalletDBWrapper::Backup(const std::string &strDest) { | bool BerkeleyDatabase::Backup(const std::string &strDest) { | ||||
if (IsDummy()) { | if (IsDummy()) { | ||||
return false; | return false; | ||||
} | } | ||||
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) { | ||||
Show All 27 Lines | while (true) { | ||||
} | } | ||||
} | } | ||||
} | } | ||||
MilliSleep(100); | MilliSleep(100); | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
void CWalletDBWrapper::Flush(bool shutdown) { | void BerkeleyDatabase::Flush(bool shutdown) { | ||||
if (!IsDummy()) { | if (!IsDummy()) { | ||||
env->Flush(shutdown); | env->Flush(shutdown); | ||||
} | } | ||||
} | } |