18
18
#endif
19
19
20
20
#include < boost/filesystem.hpp>
21
+ #include < boost/foreach.hpp>
21
22
#include < boost/thread.hpp>
22
23
#include < boost/version.hpp>
23
24
@@ -145,7 +146,7 @@ void CDBEnv::MakeMock()
145
146
fMockDb = true ;
146
147
}
147
148
148
- CDBEnv::VerifyResult CDBEnv::Verify (const std::string& strFile, bool (*recoverFunc)(CDBEnv& dbenv, const std::string& strFile))
149
+ CDBEnv::VerifyResult CDBEnv::Verify (const std::string& strFile, bool (*recoverFunc)(const std::string& strFile))
149
150
{
150
151
LOCK (cs_db);
151
152
assert (mapFileUseCount.count (strFile) == 0 );
@@ -158,10 +159,134 @@ CDBEnv::VerifyResult CDBEnv::Verify(const std::string& strFile, bool (*recoverFu
158
159
return RECOVER_FAIL;
159
160
160
161
// Try to recover:
161
- bool fRecovered = (*recoverFunc)(* this , strFile);
162
+ bool fRecovered = (*recoverFunc)(strFile);
162
163
return (fRecovered ? RECOVER_OK : RECOVER_FAIL);
163
164
}
164
165
166
+ bool CDB::Recover (const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void * callbackData, CDataStream ssKey, CDataStream ssValue))
167
+ {
168
+ // Recovery procedure:
169
+ // move wallet file to wallet.timestamp.bak
170
+ // Call Salvage with fAggressive=true to
171
+ // get as much data as possible.
172
+ // Rewrite salvaged data to fresh wallet file
173
+ // Set -rescan so any missing transactions will be
174
+ // found.
175
+ int64_t now = GetTime ();
176
+ std::string newFilename = strprintf (" wallet.%d.bak" , now);
177
+
178
+ int result = bitdb.dbenv ->dbrename (NULL , filename.c_str (), NULL ,
179
+ newFilename.c_str (), DB_AUTO_COMMIT);
180
+ if (result == 0 )
181
+ LogPrintf (" Renamed %s to %s\n " , filename, newFilename);
182
+ else
183
+ {
184
+ LogPrintf (" Failed to rename %s to %s\n " , filename, newFilename);
185
+ return false ;
186
+ }
187
+
188
+ std::vector<CDBEnv::KeyValPair> salvagedData;
189
+ bool fSuccess = bitdb.Salvage (newFilename, true , salvagedData);
190
+ if (salvagedData.empty ())
191
+ {
192
+ LogPrintf (" Salvage(aggressive) found no records in %s.\n " , newFilename);
193
+ return false ;
194
+ }
195
+ LogPrintf (" Salvage(aggressive) found %u records\n " , salvagedData.size ());
196
+
197
+ std::unique_ptr<Db> pdbCopy (new Db (bitdb.dbenv , 0 ));
198
+ int ret = pdbCopy->open (NULL , // Txn pointer
199
+ filename.c_str (), // Filename
200
+ " main" , // Logical db name
201
+ DB_BTREE, // Database type
202
+ DB_CREATE, // Flags
203
+ 0 );
204
+ if (ret > 0 )
205
+ {
206
+ LogPrintf (" Cannot create database file %s\n " , filename);
207
+ return false ;
208
+ }
209
+
210
+ DbTxn* ptxn = bitdb.TxnBegin ();
211
+ BOOST_FOREACH (CDBEnv::KeyValPair& row, salvagedData)
212
+ {
213
+ if (recoverKVcallback)
214
+ {
215
+ CDataStream ssKey (row.first , SER_DISK, CLIENT_VERSION);
216
+ CDataStream ssValue (row.second , SER_DISK, CLIENT_VERSION);
217
+ string strType, strErr;
218
+ if (!(*recoverKVcallback)(callbackDataIn, ssKey, ssValue))
219
+ continue ;
220
+ }
221
+ Dbt datKey (&row.first [0 ], row.first .size ());
222
+ Dbt datValue (&row.second [0 ], row.second .size ());
223
+ int ret2 = pdbCopy->put (ptxn, &datKey, &datValue, DB_NOOVERWRITE);
224
+ if (ret2 > 0 )
225
+ fSuccess = false ;
226
+ }
227
+ ptxn->commit (0 );
228
+ pdbCopy->close (0 );
229
+
230
+ return fSuccess ;
231
+ }
232
+
233
+ bool CDB::VerifyEnvironment (const std::string& walletFile, const boost::filesystem::path& dataDir, std::string& errorStr)
234
+ {
235
+ LogPrintf (" Using BerkeleyDB version %s\n " , DbEnv::version (0 , 0 , 0 ));
236
+ LogPrintf (" Using wallet %s\n " , walletFile);
237
+
238
+ // Wallet file must be a plain filename without a directory
239
+ if (walletFile != boost::filesystem::basename (walletFile) + boost::filesystem::extension (walletFile))
240
+ {
241
+ errorStr = strprintf (_ (" Wallet %s resides outside data directory %s" ), walletFile, dataDir.string ());
242
+ return false ;
243
+ }
244
+
245
+ if (!bitdb.Open (dataDir))
246
+ {
247
+ // try moving the database env out of the way
248
+ boost::filesystem::path pathDatabase = dataDir / " database" ;
249
+ boost::filesystem::path pathDatabaseBak = dataDir / strprintf (" database.%d.bak" , GetTime ());
250
+ try {
251
+ boost::filesystem::rename (pathDatabase, pathDatabaseBak);
252
+ LogPrintf (" Moved old %s to %s. Retrying.\n " , pathDatabase.string (), pathDatabaseBak.string ());
253
+ } catch (const boost::filesystem::filesystem_error&) {
254
+ // failure is ok (well, not really, but it's not worse than what we started with)
255
+ }
256
+
257
+ // try again
258
+ if (!bitdb.Open (dataDir)) {
259
+ // if it still fails, it probably means we can't even create the database env
260
+ errorStr = strprintf (_ (" Error initializing wallet database environment %s!" ), GetDataDir ());
261
+ return false ;
262
+ }
263
+ }
264
+ return true ;
265
+ }
266
+
267
+ bool CDB::VerifyDatabaseFile (const std::string& walletFile, const boost::filesystem::path& dataDir, std::string& warningStr, std::string& errorStr, bool (*recoverFunc)(const std::string& strFile))
268
+ {
269
+ if (boost::filesystem::exists (dataDir / walletFile))
270
+ {
271
+ CDBEnv::VerifyResult r = bitdb.Verify (walletFile, recoverFunc);
272
+ if (r == CDBEnv::RECOVER_OK)
273
+ {
274
+ warningStr = strprintf (_ (" Warning: Wallet file corrupt, data salvaged!"
275
+ " Original %s saved as %s in %s; if"
276
+ " your balance or transactions are incorrect you should"
277
+ " restore from a backup." ),
278
+ walletFile, " wallet.{timestamp}.bak" , dataDir);
279
+ }
280
+ if (r == CDBEnv::RECOVER_FAIL)
281
+ {
282
+ errorStr = strprintf (_ (" %s corrupt, salvage failed" ), walletFile);
283
+ return false ;
284
+ }
285
+ }
286
+ // also return true if files does not exists
287
+ return true ;
288
+ }
289
+
165
290
/* End of headers, beginning of key/value data */
166
291
static const char *HEADER_END = " HEADER=END" ;
167
292
/* End of key/value data */
@@ -473,3 +598,41 @@ void CDBEnv::Flush(bool fShutdown)
473
598
}
474
599
}
475
600
}
601
+
602
+ bool CDB::PeriodicFlush (std::string strFile)
603
+ {
604
+ bool ret = false ;
605
+ TRY_LOCK (bitdb.cs_db ,lockDb);
606
+ if (lockDb)
607
+ {
608
+ // Don't do this if any databases are in use
609
+ int nRefCount = 0 ;
610
+ map<string, int >::iterator mi = bitdb.mapFileUseCount .begin ();
611
+ while (mi != bitdb.mapFileUseCount .end ())
612
+ {
613
+ nRefCount += (*mi).second ;
614
+ mi++;
615
+ }
616
+
617
+ if (nRefCount == 0 )
618
+ {
619
+ boost::this_thread::interruption_point ();
620
+ map<string, int >::iterator mi = bitdb.mapFileUseCount .find (strFile);
621
+ if (mi != bitdb.mapFileUseCount .end ())
622
+ {
623
+ LogPrint (" db" , " Flushing %s\n " , strFile);
624
+ int64_t nStart = GetTimeMillis ();
625
+
626
+ // Flush wallet file so it's self contained
627
+ bitdb.CloseDb (strFile);
628
+ bitdb.CheckpointLSN (strFile);
629
+
630
+ bitdb.mapFileUseCount .erase (mi++);
631
+ LogPrint (" db" , " Flushed %s %dms\n " , strFile, GetTimeMillis () - nStart);
632
+ ret = true ;
633
+ }
634
+ }
635
+ }
636
+
637
+ return ret;
638
+ }
0 commit comments