Skip to content

Commit 2d796fa

Browse files
committed
wallet: Fix duplicate fileid
1 parent 5c25409 commit 2d796fa

File tree

2 files changed

+26
-12
lines changed

2 files changed

+26
-12
lines changed

src/wallet/db.cpp

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <boost/thread.hpp>
2121

2222
namespace {
23+
2324
//! Make sure database has a unique fileid within the environment. If it
2425
//! doesn't, throw an error. BDB caches do not work properly when more than one
2526
//! open database has the same fileid (values written to one database may show
@@ -29,25 +30,19 @@ namespace {
2930
//! (https://docs.oracle.com/cd/E17275_01/html/programmer_reference/program_copy.html),
3031
//! so bitcoin should never create different databases with the same fileid, but
3132
//! this error can be triggered if users manually copy database files.
32-
void CheckUniqueFileid(const BerkeleyEnvironment& env, const std::string& filename, Db& db)
33+
void CheckUniqueFileid(const BerkeleyEnvironment& env, const std::string& filename, Db& db, WalletDatabaseFileId& fileid)
3334
{
3435
if (env.IsMock()) return;
3536

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

42-
for (const auto& item : env.mapDb) {
43-
u_int8_t item_fileid[DB_FILE_ID_LEN];
44-
if (item.second && item.second->get_mpf()->get_fileid(item_fileid) == 0 &&
45-
memcmp(fileid, item_fileid, sizeof(fileid)) == 0) {
46-
const char* item_filename = nullptr;
47-
item.second->get_dbname(&item_filename, nullptr);
42+
for (const auto& item : env.m_fileids) {
43+
if (fileid == item.second && &fileid != &item.second) {
4844
throw std::runtime_error(strprintf("BerkeleyBatch: Can't open database %s (duplicates fileid %s from %s)", filename,
49-
HexStr(std::begin(item_fileid), std::end(item_fileid)),
50-
item_filename ? item_filename : "(unknown database)"));
45+
HexStr(std::begin(item.second.value), std::end(item.second.value)), item.first));
5146
}
5247
}
5348
}
@@ -56,6 +51,11 @@ CCriticalSection cs_db;
5651
std::map<std::string, BerkeleyEnvironment> g_dbenvs GUARDED_BY(cs_db); //!< Map from directory name to open db environment.
5752
} // namespace
5853

54+
bool WalletDatabaseFileId::operator==(const WalletDatabaseFileId& rhs) const
55+
{
56+
return memcmp(value, &rhs.value, sizeof(value)) == 0;
57+
}
58+
5959
BerkeleyEnvironment* GetWalletEnv(const fs::path& wallet_path, std::string& database_filename)
6060
{
6161
fs::path env_directory;
@@ -504,7 +504,7 @@ BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode, bo
504504
// versions of BDB have an set_lk_exclusive method for this
505505
// purpose, but the older version we use does not.)
506506
for (const auto& env : g_dbenvs) {
507-
CheckUniqueFileid(env.second, strFilename, *pdb_temp);
507+
CheckUniqueFileid(env.second, strFilename, *pdb_temp, this->env->m_fileids[strFilename]);
508508
}
509509

510510
pdb = pdb_temp.release();
@@ -826,6 +826,13 @@ void BerkeleyDatabase::Flush(bool shutdown)
826826
LOCK(cs_db);
827827
g_dbenvs.erase(env->Directory().string());
828828
env = nullptr;
829+
} else {
830+
// TODO: To avoid g_dbenvs.erase erasing the environment prematurely after the
831+
// first database shutdown when multiple databases are open in the same
832+
// environment, should replace raw database `env` pointers with shared or weak
833+
// pointers, or else separate the database and environment shutdowns so
834+
// environments can be shut down after databases.
835+
env->m_fileids.erase(strFile);
829836
}
830837
}
831838
}

src/wallet/db.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,19 @@
1818
#include <map>
1919
#include <memory>
2020
#include <string>
21+
#include <unordered_map>
2122
#include <vector>
2223

2324
#include <db_cxx.h>
2425

2526
static const unsigned int DEFAULT_WALLET_DBLOGSIZE = 100;
2627
static const bool DEFAULT_WALLET_PRIVDB = true;
2728

29+
struct WalletDatabaseFileId {
30+
u_int8_t value[DB_FILE_ID_LEN];
31+
bool operator==(const WalletDatabaseFileId& rhs) const;
32+
};
33+
2834
class BerkeleyEnvironment
2935
{
3036
private:
@@ -38,6 +44,7 @@ class BerkeleyEnvironment
3844
std::unique_ptr<DbEnv> dbenv;
3945
std::map<std::string, int> mapFileUseCount;
4046
std::map<std::string, Db*> mapDb;
47+
std::unordered_map<std::string, WalletDatabaseFileId> m_fileids;
4148
std::condition_variable_any m_db_in_use;
4249

4350
BerkeleyEnvironment(const fs::path& env_directory);

0 commit comments

Comments
 (0)