wallet: Fix duplicate fileid
This commit is contained in:
parent
5c25409d68
commit
2d796faf62
2 changed files with 26 additions and 12 deletions
|
@ -20,6 +20,7 @@
|
||||||
#include <boost/thread.hpp>
|
#include <boost/thread.hpp>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
//! Make sure database has a unique fileid within the environment. If it
|
//! 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
|
//! 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
|
||||||
|
@ -29,25 +30,19 @@ namespace {
|
||||||
//! (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, const std::string& filename, Db& db)
|
void CheckUniqueFileid(const BerkeleyEnvironment& env, const std::string& filename, Db& db, WalletDatabaseFileId& fileid)
|
||||||
{
|
{
|
||||||
if (env.IsMock()) return;
|
if (env.IsMock()) 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("BerkeleyBatch: Can't open database %s (get_fileid failed with %d)", filename, ret));
|
throw std::runtime_error(strprintf("BerkeleyBatch: Can't open database %s (get_fileid failed with %d)", 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("BerkeleyBatch: Can't open database %s (duplicates fileid %s from %s)", filename,
|
throw std::runtime_error(strprintf("BerkeleyBatch: Can't open database %s (duplicates fileid %s from %s)", filename,
|
||||||
HexStr(std::begin(item_fileid), std::end(item_fileid)),
|
HexStr(std::begin(item.second.value), std::end(item.second.value)), item.first));
|
||||||
item_filename ? item_filename : "(unknown database)"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,6 +51,11 @@ CCriticalSection cs_db;
|
||||||
std::map<std::string, BerkeleyEnvironment> g_dbenvs GUARDED_BY(cs_db); //!< Map from directory name to open db environment.
|
std::map<std::string, BerkeleyEnvironment> g_dbenvs GUARDED_BY(cs_db); //!< Map from directory name to open db environment.
|
||||||
} // namespace
|
} // 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)
|
BerkeleyEnvironment* GetWalletEnv(const fs::path& wallet_path, std::string& database_filename)
|
||||||
{
|
{
|
||||||
fs::path env_directory;
|
fs::path env_directory;
|
||||||
|
@ -504,7 +504,7 @@ BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode, bo
|
||||||
// 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& env : g_dbenvs) {
|
for (const auto& env : g_dbenvs) {
|
||||||
CheckUniqueFileid(env.second, strFilename, *pdb_temp);
|
CheckUniqueFileid(env.second, strFilename, *pdb_temp, this->env->m_fileids[strFilename]);
|
||||||
}
|
}
|
||||||
|
|
||||||
pdb = pdb_temp.release();
|
pdb = pdb_temp.release();
|
||||||
|
@ -826,6 +826,13 @@ void BerkeleyDatabase::Flush(bool 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <db_cxx.h>
|
#include <db_cxx.h>
|
||||||
|
@ -25,6 +26,11 @@
|
||||||
static const unsigned int DEFAULT_WALLET_DBLOGSIZE = 100;
|
static const unsigned int DEFAULT_WALLET_DBLOGSIZE = 100;
|
||||||
static const bool DEFAULT_WALLET_PRIVDB = true;
|
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
|
class BerkeleyEnvironment
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
@ -38,6 +44,7 @@ public:
|
||||||
std::unique_ptr<DbEnv> dbenv;
|
std::unique_ptr<DbEnv> dbenv;
|
||||||
std::map<std::string, int> mapFileUseCount;
|
std::map<std::string, int> mapFileUseCount;
|
||||||
std::map<std::string, Db*> mapDb;
|
std::map<std::string, Db*> mapDb;
|
||||||
|
std::unordered_map<std::string, WalletDatabaseFileId> m_fileids;
|
||||||
std::condition_variable_any m_db_in_use;
|
std::condition_variable_any m_db_in_use;
|
||||||
|
|
||||||
BerkeleyEnvironment(const fs::path& env_directory);
|
BerkeleyEnvironment(const fs::path& env_directory);
|
||||||
|
|
Loading…
Reference in a new issue