|
20 | 20 |
|
21 | 21 | #include <boost/thread.hpp>
|
22 | 22 |
|
| 23 | +namespace { |
| 24 | +//! Make sure database has a unique fileid within the environment. If it |
| 25 | +//! doesn't, throw an error. BDB caches do not work properly when more than one |
| 26 | +//! open database has the same fileid (values written to one database may show |
| 27 | +//! up in reads to other databases). |
| 28 | +//! |
| 29 | +//! BerkeleyDB generates unique fileids by default |
| 30 | +//! (https://docs.oracle.com/cd/E17275_01/html/programmer_reference/program_copy.html), |
| 31 | +//! so bitcoin should never create different databases with the same fileid, but |
| 32 | +//! this error can be triggered if users manually copy database files. |
| 33 | +void CheckUniqueFileid(const CDBEnv& env, const std::string& filename, Db& db) |
| 34 | +{ |
| 35 | + if (env.IsMock()) return; |
| 36 | + |
| 37 | + u_int8_t fileid[DB_FILE_ID_LEN]; |
| 38 | + int ret = db.get_mpf()->get_fileid(fileid); |
| 39 | + if (ret != 0) { |
| 40 | + throw std::runtime_error(strprintf("CDB: Can't open database %s (get_fileid failed with %d)", filename, ret)); |
| 41 | + } |
| 42 | + |
| 43 | + for (const auto& item : env.mapDb) { |
| 44 | + u_int8_t item_fileid[DB_FILE_ID_LEN]; |
| 45 | + if (item.second && item.second->get_mpf()->get_fileid(item_fileid) == 0 && |
| 46 | + memcmp(fileid, item_fileid, sizeof(fileid)) == 0) { |
| 47 | + const char* item_filename = nullptr; |
| 48 | + item.second->get_dbname(&item_filename, nullptr); |
| 49 | + throw std::runtime_error(strprintf("CDB: Can't open database %s (duplicates fileid %s from %s)", filename, |
| 50 | + HexStr(std::begin(item_fileid), std::end(item_fileid)), |
| 51 | + item_filename ? item_filename : "(unknown database)")); |
| 52 | + } |
| 53 | + } |
| 54 | +} |
| 55 | +} // namespace |
| 56 | + |
23 | 57 | //
|
24 | 58 | // CDB
|
25 | 59 | //
|
@@ -403,6 +437,7 @@ CDB::CDB(CWalletDBWrapper& dbw, const char* pszMode, bool fFlushOnCloseIn) : pdb
|
403 | 437 | if (ret != 0) {
|
404 | 438 | throw std::runtime_error(strprintf("CDB: Error %d, can't open database %s", ret, strFilename));
|
405 | 439 | }
|
| 440 | + CheckUniqueFileid(*env, strFilename, *pdb_temp); |
406 | 441 |
|
407 | 442 | pdb = pdb_temp.release();
|
408 | 443 | env->mapDb[strFilename] = pdb;
|
|
0 commit comments