Chromium Code Reviews| 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" |
| 11 #include "base/logging.h" | 11 #include "base/logging.h" |
| 12 #include "base/metrics/histogram_macros.h" | 12 #include "base/metrics/histogram_macros.h" |
| 13 #include "base/metrics/sparse_histogram.h" | 13 #include "base/metrics/sparse_histogram.h" |
| 14 #include "base/strings/string_util.h" | 14 #include "base/strings/string_util.h" |
| 15 #include "base/strings/stringprintf.h" | 15 #include "base/strings/stringprintf.h" |
| 16 #include "sql/connection.h" | 16 #include "sql/connection.h" |
| 17 #include "sql/statement.h" | 17 #include "sql/statement.h" |
| 18 #include "third_party/sqlite/sqlite3.h" | 18 #include "third_party/sqlite/sqlite3.h" |
| 19 #include "third_party/sqlite/src/src/recover.h" | 19 #include "third_party/sqlite/src/src/recover.h" |
| 20 | 20 |
| 21 namespace sql { | 21 namespace sql { |
| 22 | 22 |
| 23 namespace { | 23 namespace { |
| 24 | 24 |
| 25 enum RecoveryEventType { | 25 enum RecoveryEventType { |
|
jwd
2017/03/02 18:45:29
Can you add a comment saying this enum is used for
Scott Hess - ex-Googler
2017/03/02 18:55:52
Done.
| |
| 26 // Init() completed successfully. | 26 // Init() completed successfully. |
| 27 RECOVERY_SUCCESS_INIT = 0, | 27 RECOVERY_SUCCESS_INIT = 0, |
| 28 | 28 |
| 29 // Failed to open temporary database to recover into. | 29 // Failed to open temporary database to recover into. |
| 30 RECOVERY_FAILED_OPEN_TEMPORARY, | 30 RECOVERY_FAILED_OPEN_TEMPORARY, |
| 31 | 31 |
| 32 // Failed to initialize recover vtable system. | 32 // Failed to initialize recover vtable system. |
| 33 RECOVERY_FAILED_VIRTUAL_TABLE_INIT, | 33 RECOVERY_FAILED_VIRTUAL_TABLE_INIT, |
| 34 | 34 |
| 35 // System SQLite doesn't support vtable. | 35 // System SQLite doesn't support vtable. |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 101 | 101 |
| 102 // Failed to recover an individual table. | 102 // Failed to recover an individual table. |
| 103 RECOVERY_FAILED_AUTORECOVERDB_TABLE, | 103 RECOVERY_FAILED_AUTORECOVERDB_TABLE, |
| 104 | 104 |
| 105 // Failed to recover [sqlite_sequence] table. | 105 // Failed to recover [sqlite_sequence] table. |
| 106 RECOVERY_FAILED_AUTORECOVERDB_SEQUENCE, | 106 RECOVERY_FAILED_AUTORECOVERDB_SEQUENCE, |
| 107 | 107 |
| 108 // Failed to recover triggers or views or virtual tables. | 108 // Failed to recover triggers or views or virtual tables. |
| 109 RECOVERY_FAILED_AUTORECOVERDB_AUX, | 109 RECOVERY_FAILED_AUTORECOVERDB_AUX, |
| 110 | 110 |
| 111 // After SQLITE_NOTADB failure setting up for recovery, Delete() failed. | |
| 112 RECOVERY_FAILED_AUTORECOVERDB_NOTADB_DELETE, | |
| 113 | |
| 114 // After SQLITE_NOTADB failure setting up for recovery, Delete() succeeded | |
| 115 // then Open() failed. | |
| 116 RECOVERY_FAILED_AUTORECOVERDB_NOTADB_REOPEN, | |
| 117 | |
| 118 // After SQLITE_NOTADB failure setting up for recovery, Delete() and Open() | |
| 119 // succeeded, then querying the database failed. | |
| 120 RECOVERY_FAILED_AUTORECOVERDB_NOTADB_QUERY, | |
| 121 | |
| 122 // After SQLITE_NOTADB failure setting up for recovery, the database was | |
| 123 // successfully deleted. | |
| 124 RECOVERY_SUCCESS_AUTORECOVERDB_NOTADB_DELETE, | |
| 125 | |
| 111 // Always keep this at the end. | 126 // Always keep this at the end. |
| 112 RECOVERY_EVENT_MAX, | 127 RECOVERY_EVENT_MAX, |
| 113 }; | 128 }; |
| 114 | 129 |
| 115 void RecordRecoveryEvent(RecoveryEventType recovery_event) { | 130 void RecordRecoveryEvent(RecoveryEventType recovery_event) { |
| 116 UMA_HISTOGRAM_ENUMERATION("Sqlite.RecoveryEvents", | 131 UMA_HISTOGRAM_ENUMERATION("Sqlite.RecoveryEvents", |
| 117 recovery_event, RECOVERY_EVENT_MAX); | 132 recovery_event, RECOVERY_EVENT_MAX); |
| 118 } | 133 } |
| 119 | 134 |
| 120 } // namespace | 135 } // namespace |
| (...skipping 460 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 581 // TODO(shess): This conservatively uses Rollback() rather than Unrecoverable(). | 596 // TODO(shess): This conservatively uses Rollback() rather than Unrecoverable(). |
| 582 // With Rollback(), it is expected that the database will continue to generate | 597 // With Rollback(), it is expected that the database will continue to generate |
| 583 // errors. Change the failure cases to Unrecoverable() if/when histogram | 598 // errors. Change the failure cases to Unrecoverable() if/when histogram |
| 584 // results indicate that everything is working reasonably. | 599 // results indicate that everything is working reasonably. |
| 585 // | 600 // |
| 586 // static | 601 // static |
| 587 void Recovery::RecoverDatabase(Connection* db, | 602 void Recovery::RecoverDatabase(Connection* db, |
| 588 const base::FilePath& db_path) { | 603 const base::FilePath& db_path) { |
| 589 std::unique_ptr<sql::Recovery> recovery = sql::Recovery::Begin(db, db_path); | 604 std::unique_ptr<sql::Recovery> recovery = sql::Recovery::Begin(db, db_path); |
| 590 if (!recovery) { | 605 if (!recovery) { |
| 591 // TODO(shess): If recovery can't even get started, Raze() or Delete(). | 606 // Close the underlying sqlite* handle. Windows does not allow deleting |
| 592 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_BEGIN); | 607 // open files, and all platforms block opening a second sqlite3* handle |
| 608 // against a database when exclusive locking is set. | |
| 593 db->Poison(); | 609 db->Poison(); |
| 610 | |
| 611 // Histograms from Recovery::Begin() show all current failures are in | |
| 612 // attaching the corrupt database, with 2/3 being SQLITE_NOTADB. Don't | |
| 613 // delete the database except for that specific failure case. | |
| 614 { | |
| 615 Connection probe_db; | |
| 616 if (!probe_db.OpenInMemory() || | |
| 617 probe_db.AttachDatabase(db_path, "corrupt") || | |
| 618 probe_db.GetErrorCode() != SQLITE_NOTADB) { | |
| 619 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_BEGIN); | |
| 620 return; | |
| 621 } | |
| 622 } | |
| 623 | |
| 624 // The database has invalid data in the SQLite header, so it is almost | |
| 625 // certainly not recoverable without manual intervention (and likely not | |
| 626 // recoverable _with_ manual intervention). Clear away the broken database. | |
| 627 if (!sql::Connection::Delete(db_path)) { | |
| 628 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_NOTADB_DELETE); | |
| 629 return; | |
| 630 } | |
| 631 | |
| 632 // Windows deletion is complicated by file scanners and malware - sometimes | |
| 633 // Delete() appears to succeed, even though the file remains. The following | |
| 634 // attempts to track if this happens often enough to cause concern. | |
| 635 { | |
| 636 Connection probe_db; | |
| 637 if (!probe_db.Open(db_path)) { | |
| 638 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_NOTADB_REOPEN); | |
| 639 return; | |
| 640 } | |
| 641 if (!probe_db.Execute("PRAGMA auto_vacuum")) { | |
| 642 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_NOTADB_QUERY); | |
| 643 return; | |
| 644 } | |
| 645 } | |
| 646 | |
| 647 // 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. | |
| 649 RecordRecoveryEvent(RECOVERY_SUCCESS_AUTORECOVERDB_NOTADB_DELETE); | |
| 594 return; | 650 return; |
| 595 } | 651 } |
| 596 | 652 |
| 597 #if DCHECK_IS_ON() | 653 #if DCHECK_IS_ON() |
| 598 // This code silently fails to recover fts3 virtual tables. At this time no | 654 // This code silently fails to recover fts3 virtual tables. At this time no |
| 599 // browser database contain fts3 tables. Just to be safe, complain loudly if | 655 // browser database contain fts3 tables. Just to be safe, complain loudly if |
| 600 // the database contains virtual tables. | 656 // the database contains virtual tables. |
| 601 // | 657 // |
| 602 // fts3 has an [x_segdir] table containing a column [end_block INTEGER]. But | 658 // fts3 has an [x_segdir] table containing a column [end_block INTEGER]. But |
| 603 // it actually stores either an integer or a text containing a pair of | 659 // it actually stores either an integer or a text containing a pair of |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 706 // - SQLITE_READONLY - permissions could be fixed. | 762 // - SQLITE_READONLY - permissions could be fixed. |
| 707 // - SQLITE_IOERR - rewrite using new blocks. | 763 // - SQLITE_IOERR - rewrite using new blocks. |
| 708 // - SQLITE_FULL - recover in memory and rewrite subset of data. | 764 // - SQLITE_FULL - recover in memory and rewrite subset of data. |
| 709 | 765 |
| 710 default: | 766 default: |
| 711 return false; | 767 return false; |
| 712 } | 768 } |
| 713 } | 769 } |
| 714 | 770 |
| 715 } // namespace sql | 771 } // namespace sql |
| OLD | NEW |