Auto-backup wallet.dat before flush/rewrite operations
- Add AutoBackupWallet() that copies wallet.dat to wallet.dat.auto.bak before any DB flush or rewrite - Call AutoBackupWallet() in ThreadFlushWalletDB() before flushing - Call AutoBackupWallet() in AppInit2() after loading wallet - Add suspicious-size check in AppInit2() (warns if wallet.dat < 1KB) - Declare AutoBackupWallet() in db.h This protects against wallet corruption during crash by maintaining an auto-backup that is always at least as recent as the last flush.
This commit is contained in:
@@ -28,6 +28,7 @@ extern unsigned int nWalletDBUpdated;
|
||||
|
||||
void ThreadFlushWalletDB(void* parg);
|
||||
bool BackupWallet(const CWallet& wallet, const std::string& strDest);
|
||||
bool AutoBackupWallet(const boost::filesystem::path& walletPath);
|
||||
|
||||
|
||||
class CDBEnv
|
||||
|
||||
@@ -1041,6 +1041,21 @@ bool AppInit2()
|
||||
nStart = GetTimeMillis();
|
||||
bool fFirstRun = true;
|
||||
pwalletMain = new CWallet(strWalletFileName);
|
||||
|
||||
// Auto-backup wallet.dat before loading (protects against corruption during load/flush)
|
||||
{
|
||||
fs::path walletPath = GetDataDir() / strWalletFileName;
|
||||
if (fs::exists(walletPath)) {
|
||||
uintmax_t wsize = fs::file_size(walletPath);
|
||||
printf("Wallet file size: %llu bytes\n", (unsigned long long)wsize);
|
||||
if (wsize < 1024) {
|
||||
strErrors << _("WARNING: wallet.dat is suspiciously small (") << wsize << _(" bytes). It may be corrupt.\n");
|
||||
printf("WARNING: wallet.dat is only %llu bytes - possibly corrupt!\n", (unsigned long long)wsize);
|
||||
}
|
||||
AutoBackupWallet(walletPath);
|
||||
}
|
||||
}
|
||||
|
||||
DBErrors nLoadWalletRet = pwalletMain->LoadWallet(fFirstRun);
|
||||
if (nLoadWalletRet != DB_LOAD_OK)
|
||||
{
|
||||
|
||||
@@ -16,6 +16,40 @@ namespace fs = boost::filesystem;
|
||||
static uint64_t nAccountingEntryNumber = 0;
|
||||
extern bool fWalletUnlockStakingOnly;
|
||||
|
||||
//
|
||||
// Auto-backup wallet before flush/rewrite operations.
|
||||
// Copies wallet.dat to wallet.dat.auto.bak if the backup is older than the wallet.
|
||||
// Returns true if backup was created or already up to date.
|
||||
//
|
||||
bool AutoBackupWallet(const fs::path& walletPath)
|
||||
{
|
||||
fs::path backupPath = walletPath.string() + ".auto.bak";
|
||||
try {
|
||||
// Only back up if wallet exists and is non-trivial (>1KB)
|
||||
if (!fs::exists(walletPath))
|
||||
return true;
|
||||
uintmax_t walletSize = fs::file_size(walletPath);
|
||||
if (walletSize < 1024) {
|
||||
printf("AutoBackupWallet: wallet.dat is only %llu bytes (possibly corrupt), skipping auto-backup\n",
|
||||
(unsigned long long)walletSize);
|
||||
return false;
|
||||
}
|
||||
// Skip if backup exists and is same size (already backed up this version)
|
||||
if (fs::exists(backupPath)) {
|
||||
uintmax_t backupSize = fs::file_size(backupPath);
|
||||
if (backupSize == walletSize)
|
||||
return true;
|
||||
}
|
||||
fs::copy_file(walletPath, backupPath, fs::copy_options::overwrite_existing);
|
||||
printf("AutoBackupWallet: backed up wallet.dat (%llu bytes) to wallet.dat.auto.bak\n",
|
||||
(unsigned long long)walletSize);
|
||||
return true;
|
||||
} catch (const fs::filesystem_error& e) {
|
||||
printf("AutoBackupWallet: failed - %s\n", e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// CWalletDB
|
||||
//
|
||||
@@ -580,6 +614,10 @@ void ThreadFlushWalletDB(void* parg)
|
||||
nLastFlushed = nWalletDBUpdated;
|
||||
int64_t nStart = GetTimeMillis();
|
||||
|
||||
// Auto-backup before flush (protects against corruption)
|
||||
fs::path walletPath = GetDataDir() / strFile;
|
||||
AutoBackupWallet(walletPath);
|
||||
|
||||
// Flush wallet.dat so it's self contained
|
||||
bitdb.CloseDb(strFile);
|
||||
bitdb.CheckpointLSN(strFile);
|
||||
|
||||
Reference in New Issue
Block a user