mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-11-11 14:38:29 +01:00
Move RecoverDatabaseFile and RecoverKeysOnlyFilter into salvage.{cpp/h}
This commit is contained in:
@@ -317,134 +317,6 @@ BerkeleyBatch::SafeDbt::operator Dbt*()
|
||||
return &m_dbt;
|
||||
}
|
||||
|
||||
/* End of headers, beginning of key/value data */
|
||||
static const char *HEADER_END = "HEADER=END";
|
||||
/* End of key/value data */
|
||||
static const char *DATA_END = "DATA=END";
|
||||
typedef std::pair<std::vector<unsigned char>, std::vector<unsigned char> > KeyValPair;
|
||||
|
||||
bool RecoverDatabaseFile(const fs::path& file_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& newFilename)
|
||||
{
|
||||
std::string filename;
|
||||
std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(file_path, filename);
|
||||
|
||||
// Recovery procedure:
|
||||
// move wallet file to walletfilename.timestamp.bak
|
||||
// Call Salvage with fAggressive=true to
|
||||
// get as much data as possible.
|
||||
// Rewrite salvaged data to fresh wallet file
|
||||
// Set -rescan so any missing transactions will be
|
||||
// found.
|
||||
int64_t now = GetTime();
|
||||
newFilename = strprintf("%s.%d.bak", filename, now);
|
||||
|
||||
int result = env->dbenv->dbrename(nullptr, filename.c_str(), nullptr,
|
||||
newFilename.c_str(), DB_AUTO_COMMIT);
|
||||
if (result == 0)
|
||||
LogPrintf("Renamed %s to %s\n", filename, newFilename);
|
||||
else
|
||||
{
|
||||
LogPrintf("Failed to rename %s to %s\n", filename, newFilename);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Salvage data from a file. The DB_AGGRESSIVE flag is being used (see berkeley DB->verify() method documentation).
|
||||
* key/value pairs are appended to salvagedData which are then written out to a new wallet file.
|
||||
* NOTE: reads the entire database into memory, so cannot be used
|
||||
* for huge databases.
|
||||
*/
|
||||
std::vector<KeyValPair> salvagedData;
|
||||
|
||||
std::stringstream strDump;
|
||||
|
||||
Db db(env->dbenv.get(), 0);
|
||||
result = db.verify(newFilename.c_str(), nullptr, &strDump, DB_SALVAGE | DB_AGGRESSIVE);
|
||||
if (result == DB_VERIFY_BAD) {
|
||||
LogPrintf("Salvage: Database salvage found errors, all data may not be recoverable.\n");
|
||||
}
|
||||
if (result != 0 && result != DB_VERIFY_BAD) {
|
||||
LogPrintf("Salvage: Database salvage failed with result %d.\n", result);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Format of bdb dump is ascii lines:
|
||||
// header lines...
|
||||
// HEADER=END
|
||||
// hexadecimal key
|
||||
// hexadecimal value
|
||||
// ... repeated
|
||||
// DATA=END
|
||||
|
||||
std::string strLine;
|
||||
while (!strDump.eof() && strLine != HEADER_END)
|
||||
getline(strDump, strLine); // Skip past header
|
||||
|
||||
std::string keyHex, valueHex;
|
||||
while (!strDump.eof() && keyHex != DATA_END) {
|
||||
getline(strDump, keyHex);
|
||||
if (keyHex != DATA_END) {
|
||||
if (strDump.eof())
|
||||
break;
|
||||
getline(strDump, valueHex);
|
||||
if (valueHex == DATA_END) {
|
||||
LogPrintf("Salvage: WARNING: Number of keys in data does not match number of values.\n");
|
||||
break;
|
||||
}
|
||||
salvagedData.push_back(make_pair(ParseHex(keyHex), ParseHex(valueHex)));
|
||||
}
|
||||
}
|
||||
|
||||
bool fSuccess;
|
||||
if (keyHex != DATA_END) {
|
||||
LogPrintf("Salvage: WARNING: Unexpected end of file while reading salvage output.\n");
|
||||
fSuccess = false;
|
||||
} else {
|
||||
fSuccess = (result == 0);
|
||||
}
|
||||
|
||||
if (salvagedData.empty())
|
||||
{
|
||||
LogPrintf("Salvage(aggressive) found no records in %s.\n", newFilename);
|
||||
return false;
|
||||
}
|
||||
LogPrintf("Salvage(aggressive) found %u records\n", salvagedData.size());
|
||||
|
||||
std::unique_ptr<Db> pdbCopy = MakeUnique<Db>(env->dbenv.get(), 0);
|
||||
int ret = pdbCopy->open(nullptr, // Txn pointer
|
||||
filename.c_str(), // Filename
|
||||
"main", // Logical db name
|
||||
DB_BTREE, // Database type
|
||||
DB_CREATE, // Flags
|
||||
0);
|
||||
if (ret > 0) {
|
||||
LogPrintf("Cannot create database file %s\n", filename);
|
||||
pdbCopy->close(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
DbTxn* ptxn = env->TxnBegin();
|
||||
for (KeyValPair& row : salvagedData)
|
||||
{
|
||||
if (recoverKVcallback)
|
||||
{
|
||||
CDataStream ssKey(row.first, SER_DISK, CLIENT_VERSION);
|
||||
CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION);
|
||||
if (!(*recoverKVcallback)(callbackDataIn, ssKey, ssValue))
|
||||
continue;
|
||||
}
|
||||
Dbt datKey(&row.first[0], row.first.size());
|
||||
Dbt datValue(&row.second[0], row.second.size());
|
||||
int ret2 = pdbCopy->put(ptxn, &datKey, &datValue, DB_NOOVERWRITE);
|
||||
if (ret2 > 0)
|
||||
fSuccess = false;
|
||||
}
|
||||
ptxn->commit(0);
|
||||
pdbCopy->close(0);
|
||||
|
||||
return fSuccess;
|
||||
}
|
||||
|
||||
bool BerkeleyBatch::VerifyEnvironment(const fs::path& file_path, bilingual_str& errorStr)
|
||||
{
|
||||
std::string walletFile;
|
||||
|
||||
Reference in New Issue
Block a user