diff options
Diffstat (limited to 'src/db.cpp')
| -rw-r--r-- | src/db.cpp | 112 |
1 files changed, 112 insertions, 0 deletions
| @@ -198,6 +198,38 @@ void WalletSQLiteRuntimeRollbackTxn() | |||
| 198 | pWalletSQLiteRuntime->RollbackTransaction(strError); | 198 | pWalletSQLiteRuntime->RollbackTransaction(strError); |
| 199 | } | 199 | } |
| 200 | 200 | ||
| 201 | // Open the SQLite wallet read-only as the active runtime. Used by the one-shot | ||
| 202 | // storage diagnostics, which run before the normal wallet load and must be able | ||
| 203 | // to inspect a wallet that the loader would refuse -- so this opens the file for | ||
| 204 | // scanning without loading or validating its records. | ||
| 205 | bool WalletSQLiteRuntimeOpenReadOnly(const string& strPath, string& strErrorRet) | ||
| 206 | { | ||
| 207 | strErrorRet.clear(); | ||
| 208 | if (!FileExists(strPath.c_str())) | ||
| 209 | { | ||
| 210 | strErrorRet = strprintf("SQLite wallet does not exist: %s", strPath.c_str()); | ||
| 211 | return false; | ||
| 212 | } | ||
| 213 | |||
| 214 | WalletSQLiteRuntimeClose(); | ||
| 215 | std::auto_ptr<CWalletDBSQLite> db(new CWalletDBSQLite()); | ||
| 216 | if (!db->OpenReadOnly(strPath, strErrorRet)) | ||
| 217 | return false; | ||
| 218 | |||
| 219 | pWalletSQLiteRuntime = db.release(); | ||
| 220 | return true; | ||
| 221 | } | ||
| 222 | |||
| 223 | bool WalletSQLiteRuntimeCheckpoint(string& strErrorRet) | ||
| 224 | { | ||
| 225 | if (!pWalletSQLiteRuntime) | ||
| 226 | { | ||
| 227 | strErrorRet = "SQLite wallet backend is not active"; | ||
| 228 | return false; | ||
| 229 | } | ||
| 230 | return pWalletSQLiteRuntime->Checkpoint(strErrorRet); | ||
| 231 | } | ||
| 232 | |||
| 201 | 233 | ||
| 202 | CDB::CDB(const char* pszFile, const char* pszMode, bool fTxn) : pdb(NULL) | 234 | CDB::CDB(const char* pszFile, const char* pszMode, bool fTxn) : pdb(NULL) |
| 203 | { | 235 | { |
| @@ -1354,6 +1386,16 @@ bool CWalletDB::LoadWallet(vector<unsigned char>& vchDefaultKeyRet) | |||
| 1354 | 1386 | ||
| 1355 | bool ScanWalletRecords(CWalletRecordVisitor& visitor, string& strErrorRet) | 1387 | bool ScanWalletRecords(CWalletRecordVisitor& visitor, string& strErrorRet) |
| 1356 | { | 1388 | { |
| 1389 | // Under the SQLite backend there is no Berkeley DB cursor to walk; scan the | ||
| 1390 | // active SQLite store instead. This is the single choke point every "read | ||
| 1391 | // every wallet record" caller goes through (storage audit and check among | ||
| 1392 | // them), so routing it here makes those honour -walletbackend=sqlite. | ||
| 1393 | if (WalletSQLiteRuntimeActive()) | ||
| 1394 | { | ||
| 1395 | strErrorRet.clear(); | ||
| 1396 | return pWalletSQLiteRuntime->ScanRecords(visitor, strErrorRet); | ||
| 1397 | } | ||
| 1398 | |||
| 1357 | class CWalletScanDB : public CWalletDB | 1399 | class CWalletScanDB : public CWalletDB |
| 1358 | { | 1400 | { |
| 1359 | public: | 1401 | public: |
| @@ -1584,11 +1626,81 @@ bool LoadWalletFromSQLiteRuntime(const string& strPath) | |||
| 1584 | // this file was written are not spendable from it. Backing up once is not | 1626 | // this file was written are not spendable from it. Backing up once is not |
| 1585 | // enough, and that is a property of the wallet format rather than of this | 1627 | // enough, and that is a property of the wallet format rather than of this |
| 1586 | // function. | 1628 | // function. |
| 1629 | // Back up the active SQLite wallet. The store is checkpointed so the WAL is | ||
| 1630 | // folded into the main file, then the single self-contained wallet.sqlite is | ||
| 1631 | // copied through a temp file and renamed into place (same temp/old/rename dance | ||
| 1632 | // as the Berkeley DB path, for Windows' non-overwriting rename). No lsn_reset: | ||
| 1633 | // a checkpointed SQLite file needs nothing beside it to open. | ||
| 1634 | static bool BackupWalletSQLite(const string& strDest) | ||
| 1635 | { | ||
| 1636 | string strSrc = GetAppDir() + "/wallet.sqlite"; | ||
| 1637 | if (!FileExists(strSrc.c_str())) | ||
| 1638 | return error("BackupWallet() : %s does not exist\n", strSrc.c_str()); | ||
| 1639 | |||
| 1640 | string strCheckpointError; | ||
| 1641 | if (!WalletSQLiteRuntimeCheckpoint(strCheckpointError)) | ||
| 1642 | return error("BackupWallet() : checkpoint failed: %s\n", strCheckpointError.c_str()); | ||
| 1643 | |||
| 1644 | string strTemp = strprintf("%s.tmp.%lld", strDest.c_str(), GetTime()); | ||
| 1645 | for (int i = 1; FileExists(strTemp.c_str()); i++) | ||
| 1646 | strTemp = strprintf("%s.tmp.%lld.%d", strDest.c_str(), GetTime(), i); | ||
| 1647 | string strOld = strprintf("%s.old.%lld", strDest.c_str(), GetTime()); | ||
| 1648 | for (int i = 1; FileExists(strOld.c_str()); i++) | ||
| 1649 | strOld = strprintf("%s.old.%lld.%d", strDest.c_str(), GetTime(), i); | ||
| 1650 | |||
| 1651 | FILE* pfIn = fopen(strSrc.c_str(), "rb"); | ||
| 1652 | if (!pfIn) | ||
| 1653 | return error("BackupWallet() : cannot read %s\n", strSrc.c_str()); | ||
| 1654 | FILE* pfOut = fopen(strTemp.c_str(), "wb"); | ||
| 1655 | if (!pfOut) | ||
| 1656 | { | ||
| 1657 | fclose(pfIn); | ||
| 1658 | return error("BackupWallet() : cannot write %s\n", strTemp.c_str()); | ||
| 1659 | } | ||
| 1660 | |||
| 1661 | char buf[65536]; | ||
| 1662 | size_t n; | ||
| 1663 | bool fOk = true; | ||
| 1664 | while ((n = fread(buf, 1, sizeof(buf), pfIn)) > 0) | ||
| 1665 | if (fwrite(buf, 1, n, pfOut) != n) { fOk = false; break; } | ||
| 1666 | if (ferror(pfIn)) | ||
| 1667 | fOk = false; | ||
| 1668 | fclose(pfIn); | ||
| 1669 | if (fclose(pfOut) != 0) | ||
| 1670 | fOk = false; | ||
| 1671 | if (!fOk) | ||
| 1672 | { | ||
| 1673 | remove(strTemp.c_str()); // half a wallet is worse than none | ||
| 1674 | return error("BackupWallet() : copy to %s failed\n", strTemp.c_str()); | ||
| 1675 | } | ||
| 1676 | |||
| 1677 | bool fHadOldDest = FileExists(strDest.c_str()); | ||
| 1678 | if (fHadOldDest && rename(strDest.c_str(), strOld.c_str()) != 0) | ||
| 1679 | { | ||
| 1680 | remove(strTemp.c_str()); | ||
| 1681 | return error("BackupWallet() : could not move old %s aside\n", strDest.c_str()); | ||
| 1682 | } | ||
| 1683 | if (rename(strTemp.c_str(), strDest.c_str()) != 0) | ||
| 1684 | { | ||
| 1685 | remove(strTemp.c_str()); | ||
| 1686 | if (fHadOldDest) | ||
| 1687 | rename(strOld.c_str(), strDest.c_str()); | ||
| 1688 | return error("BackupWallet() : could not install %s\n", strDest.c_str()); | ||
| 1689 | } | ||
| 1690 | if (fHadOldDest) | ||
| 1691 | remove(strOld.c_str()); | ||
| 1692 | return true; | ||
| 1693 | } | ||
| 1694 | |||
| 1587 | bool BackupWallet(const string& strDest) | 1695 | bool BackupWallet(const string& strDest) |
| 1588 | { | 1696 | { |
| 1589 | if (strDest.empty()) | 1697 | if (strDest.empty()) |
| 1590 | return error("BackupWallet() : no destination given\n"); | 1698 | return error("BackupWallet() : no destination given\n"); |
| 1591 | 1699 | ||
| 1700 | // Under the SQLite backend the live wallet is wallet.sqlite, not wallet.dat. | ||
| 1701 | if (WalletSQLiteRuntimeActive()) | ||
| 1702 | return BackupWalletSQLite(strDest); | ||
| 1703 | |||
| 1592 | string strSrc = GetAppDir() + "/wallet.dat"; | 1704 | string strSrc = GetAppDir() + "/wallet.dat"; |
| 1593 | if (!FileExists(strSrc.c_str())) | 1705 | if (!FileExists(strSrc.c_str())) |
| 1594 | return error("BackupWallet() : %s does not exist\n", strSrc.c_str()); | 1706 | return error("BackupWallet() : %s does not exist\n", strSrc.c_str()); |