77#include " db.h"
88
99#include " addrman.h"
10+ #include " guiinterfaceutil.h"
1011#include " hash.h"
1112#include " protocol.h"
1213#include " util.h"
@@ -144,7 +145,7 @@ void CDBEnv::MakeMock()
144145 fMockDb = true ;
145146}
146147
147- CDBEnv::VerifyResult CDBEnv::Verify (const std::string& strFile, bool (*recoverFunc)(CDBEnv& dbenv, const std::string& strFile))
148+ CDBEnv::VerifyResult CDBEnv::Verify (const std::string& strFile, bool (*recoverFunc)(const std::string& strFile))
148149{
149150 LOCK (cs_db);
150151 assert (mapFileUseCount.count (strFile) == 0 );
@@ -157,10 +158,123 @@ CDBEnv::VerifyResult CDBEnv::Verify(const std::string& strFile, bool (*recoverFu
157158 return RECOVER_FAIL;
158159
159160 // Try to recover:
160- bool fRecovered = (*recoverFunc)(* this , strFile);
161+ bool fRecovered = (*recoverFunc)(strFile);
161162 return (fRecovered ? RECOVER_OK : RECOVER_FAIL);
162163}
163164
165+ bool CDB::Recover (const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void * callbackData, CDataStream ssKey, CDataStream ssValue))
166+ {
167+ // Recovery procedure:
168+ // move wallet file to wallet.timestamp.bak
169+ // Call Salvage with fAggressive=true to
170+ // get as much data as possible.
171+ // Rewrite salvaged data to fresh wallet file
172+ // Set -rescan so any missing transactions will be
173+ // found.
174+ int64_t now = GetTime ();
175+ std::string newFilename = strprintf (" wallet.%d.bak" , now);
176+
177+ int result = bitdb.dbenv ->dbrename (NULL , filename.c_str (), NULL ,
178+ newFilename.c_str (), DB_AUTO_COMMIT);
179+ if (result == 0 ) {
180+ LogPrintf (" Renamed %s to %s\n " , filename, newFilename);
181+ } else {
182+ LogPrintf (" Failed to rename %s to %s\n " , filename, newFilename);
183+ return false ;
184+ }
185+
186+ std::vector<CDBEnv::KeyValPair> salvagedData;
187+ bool fSuccess = bitdb.Salvage (newFilename, true , salvagedData);
188+ if (salvagedData.empty ()) {
189+ LogPrintf (" Salvage(aggressive) found no records in %s.\n " , newFilename);
190+ return false ;
191+ }
192+ LogPrintf (" Salvage(aggressive) found %u records\n " , salvagedData.size ());
193+
194+ std::unique_ptr<Db> pdbCopy (new Db (bitdb.dbenv , 0 ));
195+ int ret = pdbCopy->open (NULL , // Txn pointer
196+ filename.c_str (), // Filename
197+ " main" , // Logical db name
198+ DB_BTREE, // Database type
199+ DB_CREATE, // Flags
200+ 0 );
201+ if (ret > 0 ) {
202+ LogPrintf (" Cannot create database file %s\n " , filename);
203+ return false ;
204+ }
205+
206+ DbTxn* ptxn = bitdb.TxnBegin ();
207+ for (CDBEnv::KeyValPair& row : salvagedData) {
208+ if (recoverKVcallback) {
209+ CDataStream ssKey (row.first , SER_DISK, CLIENT_VERSION);
210+ CDataStream ssValue (row.second , SER_DISK, CLIENT_VERSION);
211+ std::string strType, strErr;
212+ if (!(*recoverKVcallback)(callbackDataIn, ssKey, ssValue))
213+ continue ;
214+ }
215+ Dbt datKey (&row.first [0 ], row.first .size ());
216+ Dbt datValue (&row.second [0 ], row.second .size ());
217+ int ret2 = pdbCopy->put (ptxn, &datKey, &datValue, DB_NOOVERWRITE);
218+ if (ret2 > 0 )
219+ fSuccess = false ;
220+ }
221+ ptxn->commit (0 );
222+ pdbCopy->close (0 );
223+
224+ return fSuccess ;
225+ }
226+
227+ bool CDB::VerifyEnvironment (const std::string& walletFile, const fs::path& dataDir, std::string& errorStr)
228+ {
229+ LogPrintf (" Using BerkeleyDB version %s\n " , DbEnv::version (0 , 0 , 0 ));
230+ LogPrintf (" Using wallet %s\n " , walletFile);
231+
232+ // Wallet file must be a plain filename without a directory
233+ fs::path wallet_file_path (walletFile);
234+ if (walletFile != wallet_file_path.filename ().string ())
235+ return UIError (strprintf ((" Wallet %s resides outside data directory %s" ), walletFile, dataDir.string ()));
236+
237+ if (!bitdb.Open (dataDir)) {
238+ // try moving the database env out of the way
239+ fs::path pathDatabase = dataDir / " database" ;
240+ fs::path pathDatabaseBak = dataDir / strprintf (" database.%d.bak" , GetTime ());
241+ try {
242+ fs::rename (pathDatabase, pathDatabaseBak);
243+ LogPrintf (" Moved old %s to %s. Retrying.\n " , pathDatabase.string (), pathDatabaseBak.string ());
244+ } catch (const fs::filesystem_error&) {
245+ // failure is ok (well, not really, but it's not worse than what we started with)
246+ }
247+
248+ // try again
249+ if (!bitdb.Open (dataDir)) {
250+ // if it still fails, it probably means we can't even create the database env
251+ errorStr = strprintf ((" Error initializing wallet database environment %s!" ), GetDataDir ());
252+ return false ;
253+ }
254+ }
255+ return true ;
256+ }
257+
258+ bool CDB::VerifyDatabaseFile (const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr, bool (*recoverFunc)(const std::string& strFile))
259+ {
260+ if (fs::exists (dataDir / walletFile)) {
261+ CDBEnv::VerifyResult r = bitdb.Verify (walletFile, recoverFunc);
262+ if (r == CDBEnv::RECOVER_OK) {
263+ warningStr = strprintf ((" Warning: Wallet file corrupt, data salvaged!"
264+ " Original %s saved as %s in %s; if"
265+ " your balance or transactions are incorrect you should"
266+ " restore from a backup." ),
267+ walletFile, " wallet.{timestamp}.bak" , dataDir);
268+ }
269+ if (r == CDBEnv::RECOVER_FAIL) {
270+ errorStr = strprintf ((" %s corrupt, salvage failed" ), walletFile);
271+ return false ;
272+ }
273+ }
274+ // also return true if files does not exists
275+ return true ;
276+ }
277+
164278/* End of headers, beginning of key/value data */
165279static const char *HEADER_END = " HEADER=END" ;
166280/* End of key/value data */
0 commit comments