Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/db.cpp
Show All 25 Lines | |||||
//! 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 BerkeleyEnvironment &env, | void CheckUniqueFileid(const BerkeleyEnvironment &env, | ||||
const std::string &filename, Db &db) { | const std::string &filename, Db &db, | ||||
WalletDatabaseFileId &fileid) { | |||||
if (env.IsMock()) { | if (env.IsMock()) { | ||||
return; | return; | ||||
} | } | ||||
u_int8_t fileid[DB_FILE_ID_LEN]; | int ret = db.get_mpf()->get_fileid(fileid.value); | ||||
int ret = db.get_mpf()->get_fileid(fileid); | |||||
if (ret != 0) { | if (ret != 0) { | ||||
throw std::runtime_error(strprintf( | throw std::runtime_error(strprintf( | ||||
"BerkeleyBatch: 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.m_fileids) { | ||||
u_int8_t item_fileid[DB_FILE_ID_LEN]; | if (fileid == item.second && &fileid != &item.second) { | ||||
if (item.second && | |||||
item.second->get_mpf()->get_fileid(item_fileid) == 0 && | |||||
memcmp(fileid, item_fileid, sizeof(fileid)) == 0) { | |||||
const char *item_filename = nullptr; | |||||
item.second->get_dbname(&item_filename, nullptr); | |||||
throw std::runtime_error(strprintf( | throw std::runtime_error(strprintf( | ||||
"BerkeleyBatch: Can't open database %s (duplicates fileid %s " | "BerkeleyBatch: Can't open database %s (duplicates fileid %s " | ||||
"from %s)", | "from %s)", | ||||
filename, | filename, | ||||
HexStr(std::begin(item_fileid), std::end(item_fileid)), | HexStr(std::begin(item.second.value), | ||||
item_filename ? item_filename : "(unknown database)")); | std::end(item.second.value)), | ||||
item.first)); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
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, BerkeleyEnvironment> g_dbenvs; | std::map<std::string, BerkeleyEnvironment> g_dbenvs; | ||||
} // namespace | } // namespace | ||||
bool WalletDatabaseFileId::operator==(const WalletDatabaseFileId &rhs) const { | |||||
return memcmp(value, &rhs.value, sizeof(value)) == 0; | |||||
} | |||||
BerkeleyEnvironment *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(); | ||||
▲ Show 20 Lines • Show All 472 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, strFilename, *pdb_temp, | ||||
this->env->m_fileids[strFilename]); | |||||
} | } | ||||
pdb = pdb_temp.release(); | pdb = pdb_temp.release(); | ||||
env->mapDb[strFilename] = pdb; | env->mapDb[strFilename] = 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 337 Lines • ▼ Show 20 Lines | |||||
void BerkeleyDatabase::Flush(bool shutdown) { | void BerkeleyDatabase::Flush(bool shutdown) { | ||||
if (!IsDummy()) { | if (!IsDummy()) { | ||||
env->Flush(shutdown); | env->Flush(shutdown); | ||||
if (shutdown) { | if (shutdown) { | ||||
LOCK(cs_db); | LOCK(cs_db); | ||||
g_dbenvs.erase(env->Directory().string()); | g_dbenvs.erase(env->Directory().string()); | ||||
env = nullptr; | env = nullptr; | ||||
} else { | |||||
// TODO: To avoid g_dbenvs.erase erasing the environment prematurely | |||||
// after the first database shutdown when multiple databases are | |||||
// open in the same environment, should replace raw database `env` | |||||
// pointers with shared or weak pointers, or else separate the | |||||
// database and environment shutdowns so environments can be shut | |||||
// down after databases. | |||||
env->m_fileids.erase(strFile); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
void BerkeleyDatabase::ReloadDbEnv() { | void BerkeleyDatabase::ReloadDbEnv() { | ||||
if (!IsDummy()) { | if (!IsDummy()) { | ||||
env->ReloadDbEnv(); | env->ReloadDbEnv(); | ||||
} | } | ||||
} | } |