Skip to content

Commit

Permalink
wallet: Fix duplicate fileid
Browse files Browse the repository at this point in the history
  • Loading branch information
ken2812221 authored and furszy committed Nov 25, 2021
1 parent 6e03bf2 commit 28d17a1
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 13 deletions.
33 changes: 20 additions & 13 deletions src/wallet/db.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@


namespace {

//! Make sure database has a unique fileid within the environment. If it
//! 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
Expand All @@ -33,25 +34,19 @@ namespace {
//! (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
//! this error can be triggered if users manually copy database files.
void CheckUniqueFileid(const BerkeleyEnvironment& env, const std::string& filename, Db& db)
void CheckUniqueFileid(const BerkeleyEnvironment& env, const std::string& filename, Db& db, WalletDatabaseFileId& fileid)
{
if (env.IsMock()) return;

u_int8_t fileid[DB_FILE_ID_LEN];
int ret = db.get_mpf()->get_fileid(fileid);
int ret = db.get_mpf()->get_fileid(fileid.value);
if (ret != 0) {
throw std::runtime_error(strprintf("BerkeleyBatch: Can't open database %s (get_fileid failed with %d)", filename, ret));
}

for (const auto& item : env.mapDb) {
u_int8_t item_fileid[DB_FILE_ID_LEN];
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);
for (const auto& item : env.m_fileids) {
if (fileid == item.second && &fileid != &item.second) {
throw std::runtime_error(strprintf("BerkeleyBatch: Can't open database %s (duplicates fileid %s from %s)", filename,
HexStr(item_fileid),
item_filename ? item_filename : "(unknown database)"));
HexStr(item.second.value), item.first));
}
}
}
Expand All @@ -60,6 +55,11 @@ RecursiveMutex cs_db;
std::map<std::string, BerkeleyEnvironment> g_dbenvs; //!< Map from directory name to open db environment.
} // namespace

bool WalletDatabaseFileId::operator==(const WalletDatabaseFileId& rhs) const
{
return memcmp(value, &rhs.value, sizeof(value)) == 0;
}

BerkeleyEnvironment* GetWalletEnv(const fs::path& wallet_path, std::string& database_filename)
{
fs::path env_directory;
Expand Down Expand Up @@ -503,8 +503,8 @@ BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode, bo
// be implemented, so no equality checks are needed at all. (Newer
// versions of BDB have an set_lk_exclusive method for this
// purpose, but the older version we use does not.)
for (auto& env : g_dbenvs) {
CheckUniqueFileid(env.second, strFilename, *pdb_temp);
for (const auto& env : g_dbenvs) {
CheckUniqueFileid(env.second, strFilename, *pdb_temp, this->env->m_fileids[strFilename]);
}

pdb = pdb_temp.release();
Expand Down Expand Up @@ -827,6 +827,13 @@ void BerkeleyDatabase::Flush(bool shutdown)
LOCK(cs_db);
g_dbenvs.erase(env->Directory().string());
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);
}
}
}
Expand Down
7 changes: 7 additions & 0 deletions src/wallet/db.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,19 @@
#include <atomic>
#include <map>
#include <string>
#include <unordered_map>
#include <vector>

#include <db_cxx.h>

static const unsigned int DEFAULT_WALLET_DBLOGSIZE = 100;
static const bool DEFAULT_WALLET_PRIVDB = true;

struct WalletDatabaseFileId {
u_int8_t value[DB_FILE_ID_LEN];
bool operator==(const WalletDatabaseFileId& rhs) const;
};

class BerkeleyEnvironment
{
private:
Expand All @@ -37,6 +43,7 @@ class BerkeleyEnvironment
std::unique_ptr<DbEnv> dbenv;
std::map<std::string, int> mapFileUseCount;
std::map<std::string, Db*> mapDb;
std::unordered_map<std::string, WalletDatabaseFileId> m_fileids;
std::condition_variable_any m_db_in_use;

BerkeleyEnvironment(const fs::path& env_directory);
Expand Down

0 comments on commit 28d17a1

Please sign in to comment.