| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "sql/recovery.h" | 5 #include "sql/recovery.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 | 8 |
| 9 #include "base/files/file_path.h" | 9 #include "base/files/file_path.h" |
| 10 #include "base/format_macros.h" | 10 #include "base/format_macros.h" |
| (...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 116 RECOVERY_FAILED_AUTORECOVERDB_NOTADB_REOPEN, | 116 RECOVERY_FAILED_AUTORECOVERDB_NOTADB_REOPEN, |
| 117 | 117 |
| 118 // After SQLITE_NOTADB failure setting up for recovery, Delete() and Open() | 118 // After SQLITE_NOTADB failure setting up for recovery, Delete() and Open() |
| 119 // succeeded, then querying the database failed. | 119 // succeeded, then querying the database failed. |
| 120 RECOVERY_FAILED_AUTORECOVERDB_NOTADB_QUERY, | 120 RECOVERY_FAILED_AUTORECOVERDB_NOTADB_QUERY, |
| 121 | 121 |
| 122 // After SQLITE_NOTADB failure setting up for recovery, the database was | 122 // After SQLITE_NOTADB failure setting up for recovery, the database was |
| 123 // successfully deleted. | 123 // successfully deleted. |
| 124 RECOVERY_SUCCESS_AUTORECOVERDB_NOTADB_DELETE, | 124 RECOVERY_SUCCESS_AUTORECOVERDB_NOTADB_DELETE, |
| 125 | 125 |
| 126 // Failed to find required [meta.version] information. |
| 127 RECOVERY_FAILED_AUTORECOVERDB_META_VERSION, |
| 128 |
| 126 // Always keep this at the end. | 129 // Always keep this at the end. |
| 127 RECOVERY_EVENT_MAX, | 130 RECOVERY_EVENT_MAX, |
| 128 }; | 131 }; |
| 129 | 132 |
| 130 void RecordRecoveryEvent(RecoveryEventType recovery_event) { | 133 void RecordRecoveryEvent(RecoveryEventType recovery_event) { |
| 131 UMA_HISTOGRAM_ENUMERATION("Sqlite.RecoveryEvents", | 134 UMA_HISTOGRAM_ENUMERATION("Sqlite.RecoveryEvents", |
| 132 recovery_event, RECOVERY_EVENT_MAX); | 135 recovery_event, RECOVERY_EVENT_MAX); |
| 133 } | 136 } |
| 134 | 137 |
| 135 } // namespace | 138 } // namespace |
| (...skipping 456 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 592 // This method is derived from SQLite's vacuum.c. VACUUM operates very | 595 // This method is derived from SQLite's vacuum.c. VACUUM operates very |
| 593 // similarily, creating a new database, populating the schema, then copying the | 596 // similarily, creating a new database, populating the schema, then copying the |
| 594 // data. | 597 // data. |
| 595 // | 598 // |
| 596 // TODO(shess): This conservatively uses Rollback() rather than Unrecoverable(). | 599 // TODO(shess): This conservatively uses Rollback() rather than Unrecoverable(). |
| 597 // With Rollback(), it is expected that the database will continue to generate | 600 // With Rollback(), it is expected that the database will continue to generate |
| 598 // errors. Change the failure cases to Unrecoverable() if/when histogram | 601 // errors. Change the failure cases to Unrecoverable() if/when histogram |
| 599 // results indicate that everything is working reasonably. | 602 // results indicate that everything is working reasonably. |
| 600 // | 603 // |
| 601 // static | 604 // static |
| 602 void Recovery::RecoverDatabase(Connection* db, | 605 std::unique_ptr<Recovery> Recovery::BeginRecoverDatabase( |
| 603 const base::FilePath& db_path) { | 606 Connection* db, |
| 607 const base::FilePath& db_path) { |
| 604 std::unique_ptr<sql::Recovery> recovery = sql::Recovery::Begin(db, db_path); | 608 std::unique_ptr<sql::Recovery> recovery = sql::Recovery::Begin(db, db_path); |
| 605 if (!recovery) { | 609 if (!recovery) { |
| 606 // Close the underlying sqlite* handle. Windows does not allow deleting | 610 // Close the underlying sqlite* handle. Windows does not allow deleting |
| 607 // open files, and all platforms block opening a second sqlite3* handle | 611 // open files, and all platforms block opening a second sqlite3* handle |
| 608 // against a database when exclusive locking is set. | 612 // against a database when exclusive locking is set. |
| 609 db->Poison(); | 613 db->Poison(); |
| 610 | 614 |
| 611 // Histograms from Recovery::Begin() show all current failures are in | 615 // Histograms from Recovery::Begin() show all current failures are in |
| 612 // attaching the corrupt database, with 2/3 being SQLITE_NOTADB. Don't | 616 // attaching the corrupt database, with 2/3 being SQLITE_NOTADB. Don't |
| 613 // delete the database except for that specific failure case. | 617 // delete the database except for that specific failure case. |
| 614 { | 618 { |
| 615 Connection probe_db; | 619 Connection probe_db; |
| 616 if (!probe_db.OpenInMemory() || | 620 if (!probe_db.OpenInMemory() || |
| 617 probe_db.AttachDatabase(db_path, "corrupt") || | 621 probe_db.AttachDatabase(db_path, "corrupt") || |
| 618 probe_db.GetErrorCode() != SQLITE_NOTADB) { | 622 probe_db.GetErrorCode() != SQLITE_NOTADB) { |
| 619 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_BEGIN); | 623 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_BEGIN); |
| 620 return; | 624 return nullptr; |
| 621 } | 625 } |
| 622 } | 626 } |
| 623 | 627 |
| 624 // The database has invalid data in the SQLite header, so it is almost | 628 // The database has invalid data in the SQLite header, so it is almost |
| 625 // certainly not recoverable without manual intervention (and likely not | 629 // certainly not recoverable without manual intervention (and likely not |
| 626 // recoverable _with_ manual intervention). Clear away the broken database. | 630 // recoverable _with_ manual intervention). Clear away the broken database. |
| 627 if (!sql::Connection::Delete(db_path)) { | 631 if (!sql::Connection::Delete(db_path)) { |
| 628 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_NOTADB_DELETE); | 632 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_NOTADB_DELETE); |
| 629 return; | 633 return nullptr; |
| 630 } | 634 } |
| 631 | 635 |
| 632 // Windows deletion is complicated by file scanners and malware - sometimes | 636 // Windows deletion is complicated by file scanners and malware - sometimes |
| 633 // Delete() appears to succeed, even though the file remains. The following | 637 // Delete() appears to succeed, even though the file remains. The following |
| 634 // attempts to track if this happens often enough to cause concern. | 638 // attempts to track if this happens often enough to cause concern. |
| 635 { | 639 { |
| 636 Connection probe_db; | 640 Connection probe_db; |
| 637 if (!probe_db.Open(db_path)) { | 641 if (!probe_db.Open(db_path)) { |
| 638 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_NOTADB_REOPEN); | 642 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_NOTADB_REOPEN); |
| 639 return; | 643 return nullptr; |
| 640 } | 644 } |
| 641 if (!probe_db.Execute("PRAGMA auto_vacuum")) { | 645 if (!probe_db.Execute("PRAGMA auto_vacuum")) { |
| 642 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_NOTADB_QUERY); | 646 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_NOTADB_QUERY); |
| 643 return; | 647 return nullptr; |
| 644 } | 648 } |
| 645 } | 649 } |
| 646 | 650 |
| 647 // The rest of the recovery code could be run on the re-opened database, but | 651 // The rest of the recovery code could be run on the re-opened database, but |
| 648 // the database is empty, so there would be no point. | 652 // the database is empty, so there would be no point. |
| 649 RecordRecoveryEvent(RECOVERY_SUCCESS_AUTORECOVERDB_NOTADB_DELETE); | 653 RecordRecoveryEvent(RECOVERY_SUCCESS_AUTORECOVERDB_NOTADB_DELETE); |
| 650 return; | 654 return nullptr; |
| 651 } | 655 } |
| 652 | 656 |
| 653 #if DCHECK_IS_ON() | 657 #if DCHECK_IS_ON() |
| 654 // This code silently fails to recover fts3 virtual tables. At this time no | 658 // This code silently fails to recover fts3 virtual tables. At this time no |
| 655 // browser database contain fts3 tables. Just to be safe, complain loudly if | 659 // browser database contain fts3 tables. Just to be safe, complain loudly if |
| 656 // the database contains virtual tables. | 660 // the database contains virtual tables. |
| 657 // | 661 // |
| 658 // fts3 has an [x_segdir] table containing a column [end_block INTEGER]. But | 662 // fts3 has an [x_segdir] table containing a column [end_block INTEGER]. But |
| 659 // it actually stores either an integer or a text containing a pair of | 663 // it actually stores either an integer or a text containing a pair of |
| 660 // integers separated by a space. AutoRecoverTable() trusts the INTEGER tag | 664 // integers separated by a space. AutoRecoverTable() trusts the INTEGER tag |
| (...skipping 11 matching lines...) Expand all Loading... |
| 672 // TODO(shess): vacuum.c turns synchronous=OFF for the target. I do not fully | 676 // TODO(shess): vacuum.c turns synchronous=OFF for the target. I do not fully |
| 673 // understand this, as the temporary db should not have a journal file at all. | 677 // understand this, as the temporary db should not have a journal file at all. |
| 674 // Perhaps it does in case of cache spill? | 678 // Perhaps it does in case of cache spill? |
| 675 | 679 |
| 676 // Copy table schema from [corrupt] to [main]. | 680 // Copy table schema from [corrupt] to [main]. |
| 677 if (!SchemaCopyHelper(recovery->db(), "CREATE TABLE ") || | 681 if (!SchemaCopyHelper(recovery->db(), "CREATE TABLE ") || |
| 678 !SchemaCopyHelper(recovery->db(), "CREATE INDEX ") || | 682 !SchemaCopyHelper(recovery->db(), "CREATE INDEX ") || |
| 679 !SchemaCopyHelper(recovery->db(), "CREATE UNIQUE INDEX ")) { | 683 !SchemaCopyHelper(recovery->db(), "CREATE UNIQUE INDEX ")) { |
| 680 // No RecordRecoveryEvent() here because SchemaCopyHelper() already did. | 684 // No RecordRecoveryEvent() here because SchemaCopyHelper() already did. |
| 681 Recovery::Rollback(std::move(recovery)); | 685 Recovery::Rollback(std::move(recovery)); |
| 682 return; | 686 return nullptr; |
| 683 } | 687 } |
| 684 | 688 |
| 685 // Run auto-recover against each table, skipping the sequence table. This is | 689 // Run auto-recover against each table, skipping the sequence table. This is |
| 686 // necessary because table recovery can create the sequence table as a side | 690 // necessary because table recovery can create the sequence table as a side |
| 687 // effect, so recovering that table inline could lead to duplicate data. | 691 // effect, so recovering that table inline could lead to duplicate data. |
| 688 { | 692 { |
| 689 sql::Statement s(recovery->db()->GetUniqueStatement( | 693 sql::Statement s(recovery->db()->GetUniqueStatement( |
| 690 "SELECT name FROM sqlite_master WHERE sql LIKE 'CREATE TABLE %' " | 694 "SELECT name FROM sqlite_master WHERE sql LIKE 'CREATE TABLE %' " |
| 691 "AND name!='sqlite_sequence'")); | 695 "AND name!='sqlite_sequence'")); |
| 692 while (s.Step()) { | 696 while (s.Step()) { |
| 693 const std::string name = s.ColumnString(0); | 697 const std::string name = s.ColumnString(0); |
| 694 size_t rows_recovered; | 698 size_t rows_recovered; |
| 695 if (!recovery->AutoRecoverTable(name.c_str(), &rows_recovered)) { | 699 if (!recovery->AutoRecoverTable(name.c_str(), &rows_recovered)) { |
| 696 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_TABLE); | 700 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_TABLE); |
| 697 Recovery::Rollback(std::move(recovery)); | 701 Recovery::Rollback(std::move(recovery)); |
| 698 return; | 702 return nullptr; |
| 699 } | 703 } |
| 700 } | 704 } |
| 701 if (!s.Succeeded()) { | 705 if (!s.Succeeded()) { |
| 702 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_NAMESELECT); | 706 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_NAMESELECT); |
| 703 Recovery::Rollback(std::move(recovery)); | 707 Recovery::Rollback(std::move(recovery)); |
| 704 return; | 708 return nullptr; |
| 705 } | 709 } |
| 706 } | 710 } |
| 707 | 711 |
| 708 // Overwrite any sequences created. | 712 // Overwrite any sequences created. |
| 709 if (recovery->db()->DoesTableExist("corrupt.sqlite_sequence")) { | 713 if (recovery->db()->DoesTableExist("corrupt.sqlite_sequence")) { |
| 710 ignore_result(recovery->db()->Execute("DELETE FROM main.sqlite_sequence")); | 714 ignore_result(recovery->db()->Execute("DELETE FROM main.sqlite_sequence")); |
| 711 size_t rows_recovered; | 715 size_t rows_recovered; |
| 712 if (!recovery->AutoRecoverTable("sqlite_sequence", &rows_recovered)) { | 716 if (!recovery->AutoRecoverTable("sqlite_sequence", &rows_recovered)) { |
| 713 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_SEQUENCE); | 717 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_SEQUENCE); |
| 714 Recovery::Rollback(std::move(recovery)); | 718 Recovery::Rollback(std::move(recovery)); |
| 715 return; | 719 return nullptr; |
| 716 } | 720 } |
| 717 } | 721 } |
| 718 | 722 |
| 719 // Copy triggers and views directly to sqlite_master. Any tables they refer | 723 // Copy triggers and views directly to sqlite_master. Any tables they refer |
| 720 // to should already exist. | 724 // to should already exist. |
| 721 char kCreateMetaItems[] = | 725 char kCreateMetaItems[] = |
| 722 "INSERT INTO main.sqlite_master " | 726 "INSERT INTO main.sqlite_master " |
| 723 "SELECT type, name, tbl_name, rootpage, sql " | 727 "SELECT type, name, tbl_name, rootpage, sql " |
| 724 "FROM corrupt.sqlite_master WHERE type='view' OR type='trigger'"; | 728 "FROM corrupt.sqlite_master WHERE type='view' OR type='trigger'"; |
| 725 if (!recovery->db()->Execute(kCreateMetaItems)) { | 729 if (!recovery->db()->Execute(kCreateMetaItems)) { |
| 726 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_AUX); | 730 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_AUX); |
| 727 Recovery::Rollback(std::move(recovery)); | 731 Recovery::Rollback(std::move(recovery)); |
| 732 return nullptr; |
| 733 } |
| 734 |
| 735 RecordRecoveryEvent(RECOVERY_SUCCESS_AUTORECOVERDB); |
| 736 return recovery; |
| 737 } |
| 738 |
| 739 void Recovery::RecoverDatabase(Connection* db, const base::FilePath& db_path) { |
| 740 std::unique_ptr<sql::Recovery> recovery = BeginRecoverDatabase(db, db_path); |
| 741 |
| 742 // ignore_result() because BeginRecoverDatabase() and Recovered() already |
| 743 // provide suitable histogram coverage. |
| 744 if (recovery) |
| 745 ignore_result(Recovery::Recovered(std::move(recovery))); |
| 746 } |
| 747 |
| 748 void Recovery::RecoverDatabaseWithMetaVersion(Connection* db, |
| 749 const base::FilePath& db_path) { |
| 750 std::unique_ptr<sql::Recovery> recovery = BeginRecoverDatabase(db, db_path); |
| 751 if (!recovery) |
| 752 return; |
| 753 |
| 754 int version = 0; |
| 755 if (!recovery->SetupMeta() || !recovery->GetMetaVersionNumber(&version)) { |
| 756 sql::Recovery::Unrecoverable(std::move(recovery)); |
| 757 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_META_VERSION); |
| 728 return; | 758 return; |
| 729 } | 759 } |
| 730 | 760 |
| 731 RecordRecoveryEvent(RECOVERY_SUCCESS_AUTORECOVERDB); | 761 // ignore_result() because BeginRecoverDatabase() and Recovered() already |
| 762 // provide suitable histogram coverage. |
| 732 ignore_result(Recovery::Recovered(std::move(recovery))); | 763 ignore_result(Recovery::Recovered(std::move(recovery))); |
| 733 } | 764 } |
| 734 | 765 |
| 735 // static | 766 // static |
| 736 bool Recovery::ShouldRecover(int extended_error) { | 767 bool Recovery::ShouldRecover(int extended_error) { |
| 737 // Trim extended error codes. | 768 // Trim extended error codes. |
| 738 int error = extended_error & 0xFF; | 769 int error = extended_error & 0xFF; |
| 739 switch (error) { | 770 switch (error) { |
| 740 case SQLITE_NOTADB: | 771 case SQLITE_NOTADB: |
| 741 // SQLITE_NOTADB happens if the SQLite header is broken. Some earlier | 772 // SQLITE_NOTADB happens if the SQLite header is broken. Some earlier |
| (...skipping 20 matching lines...) Expand all Loading... |
| 762 // - SQLITE_READONLY - permissions could be fixed. | 793 // - SQLITE_READONLY - permissions could be fixed. |
| 763 // - SQLITE_IOERR - rewrite using new blocks. | 794 // - SQLITE_IOERR - rewrite using new blocks. |
| 764 // - SQLITE_FULL - recover in memory and rewrite subset of data. | 795 // - SQLITE_FULL - recover in memory and rewrite subset of data. |
| 765 | 796 |
| 766 default: | 797 default: |
| 767 return false; | 798 return false; |
| 768 } | 799 } |
| 769 } | 800 } |
| 770 | 801 |
| 771 } // namespace sql | 802 } // namespace sql |
| OLD | NEW |