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 // This enum must match the numbering for Sqlite.RecoveryEvents in |
| 26 // histograms.xml. Do not reorder or remove items, only add new items before |
| 27 // RECOVERY_EVENT_MAX. |
25 enum RecoveryEventType { | 28 enum RecoveryEventType { |
26 // Init() completed successfully. | 29 // Init() completed successfully. |
27 RECOVERY_SUCCESS_INIT = 0, | 30 RECOVERY_SUCCESS_INIT = 0, |
28 | 31 |
29 // Failed to open temporary database to recover into. | 32 // Failed to open temporary database to recover into. |
30 RECOVERY_FAILED_OPEN_TEMPORARY, | 33 RECOVERY_FAILED_OPEN_TEMPORARY, |
31 | 34 |
32 // Failed to initialize recover vtable system. | 35 // Failed to initialize recover vtable system. |
33 RECOVERY_FAILED_VIRTUAL_TABLE_INIT, | 36 RECOVERY_FAILED_VIRTUAL_TABLE_INIT, |
34 | 37 |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
101 | 104 |
102 // Failed to recover an individual table. | 105 // Failed to recover an individual table. |
103 RECOVERY_FAILED_AUTORECOVERDB_TABLE, | 106 RECOVERY_FAILED_AUTORECOVERDB_TABLE, |
104 | 107 |
105 // Failed to recover [sqlite_sequence] table. | 108 // Failed to recover [sqlite_sequence] table. |
106 RECOVERY_FAILED_AUTORECOVERDB_SEQUENCE, | 109 RECOVERY_FAILED_AUTORECOVERDB_SEQUENCE, |
107 | 110 |
108 // Failed to recover triggers or views or virtual tables. | 111 // Failed to recover triggers or views or virtual tables. |
109 RECOVERY_FAILED_AUTORECOVERDB_AUX, | 112 RECOVERY_FAILED_AUTORECOVERDB_AUX, |
110 | 113 |
111 // Always keep this at the end. | 114 // After SQLITE_NOTADB failure setting up for recovery, Delete() failed. |
| 115 RECOVERY_FAILED_AUTORECOVERDB_NOTADB_DELETE, |
| 116 |
| 117 // After SQLITE_NOTADB failure setting up for recovery, Delete() succeeded |
| 118 // then Open() failed. |
| 119 RECOVERY_FAILED_AUTORECOVERDB_NOTADB_REOPEN, |
| 120 |
| 121 // After SQLITE_NOTADB failure setting up for recovery, Delete() and Open() |
| 122 // succeeded, then querying the database failed. |
| 123 RECOVERY_FAILED_AUTORECOVERDB_NOTADB_QUERY, |
| 124 |
| 125 // After SQLITE_NOTADB failure setting up for recovery, the database was |
| 126 // successfully deleted. |
| 127 RECOVERY_SUCCESS_AUTORECOVERDB_NOTADB_DELETE, |
| 128 |
| 129 // Add new items before this one, always keep this one at the end. |
112 RECOVERY_EVENT_MAX, | 130 RECOVERY_EVENT_MAX, |
113 }; | 131 }; |
114 | 132 |
115 void RecordRecoveryEvent(RecoveryEventType recovery_event) { | 133 void RecordRecoveryEvent(RecoveryEventType recovery_event) { |
116 UMA_HISTOGRAM_ENUMERATION("Sqlite.RecoveryEvents", | 134 UMA_HISTOGRAM_ENUMERATION("Sqlite.RecoveryEvents", |
117 recovery_event, RECOVERY_EVENT_MAX); | 135 recovery_event, RECOVERY_EVENT_MAX); |
118 } | 136 } |
119 | 137 |
120 } // namespace | 138 } // namespace |
121 | 139 |
(...skipping 459 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
581 // TODO(shess): This conservatively uses Rollback() rather than Unrecoverable(). | 599 // TODO(shess): This conservatively uses Rollback() rather than Unrecoverable(). |
582 // 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 |
583 // errors. Change the failure cases to Unrecoverable() if/when histogram | 601 // errors. Change the failure cases to Unrecoverable() if/when histogram |
584 // results indicate that everything is working reasonably. | 602 // results indicate that everything is working reasonably. |
585 // | 603 // |
586 // static | 604 // static |
587 void Recovery::RecoverDatabase(Connection* db, | 605 void Recovery::RecoverDatabase(Connection* db, |
588 const base::FilePath& db_path) { | 606 const base::FilePath& db_path) { |
589 std::unique_ptr<sql::Recovery> recovery = sql::Recovery::Begin(db, db_path); | 607 std::unique_ptr<sql::Recovery> recovery = sql::Recovery::Begin(db, db_path); |
590 if (!recovery) { | 608 if (!recovery) { |
591 // TODO(shess): If recovery can't even get started, Raze() or Delete(). | 609 // Close the underlying sqlite* handle. Windows does not allow deleting |
592 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_BEGIN); | 610 // open files, and all platforms block opening a second sqlite3* handle |
| 611 // against a database when exclusive locking is set. |
593 db->Poison(); | 612 db->Poison(); |
| 613 |
| 614 // Histograms from Recovery::Begin() show all current failures are in |
| 615 // attaching the corrupt database, with 2/3 being SQLITE_NOTADB. Don't |
| 616 // delete the database except for that specific failure case. |
| 617 { |
| 618 Connection probe_db; |
| 619 if (!probe_db.OpenInMemory() || |
| 620 probe_db.AttachDatabase(db_path, "corrupt") || |
| 621 probe_db.GetErrorCode() != SQLITE_NOTADB) { |
| 622 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_BEGIN); |
| 623 return; |
| 624 } |
| 625 } |
| 626 |
| 627 // The database has invalid data in the SQLite header, so it is almost |
| 628 // certainly not recoverable without manual intervention (and likely not |
| 629 // recoverable _with_ manual intervention). Clear away the broken database. |
| 630 if (!sql::Connection::Delete(db_path)) { |
| 631 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_NOTADB_DELETE); |
| 632 return; |
| 633 } |
| 634 |
| 635 // Windows deletion is complicated by file scanners and malware - sometimes |
| 636 // Delete() appears to succeed, even though the file remains. The following |
| 637 // attempts to track if this happens often enough to cause concern. |
| 638 { |
| 639 Connection probe_db; |
| 640 if (!probe_db.Open(db_path)) { |
| 641 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_NOTADB_REOPEN); |
| 642 return; |
| 643 } |
| 644 if (!probe_db.Execute("PRAGMA auto_vacuum")) { |
| 645 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_NOTADB_QUERY); |
| 646 return; |
| 647 } |
| 648 } |
| 649 |
| 650 // The rest of the recovery code could be run on the re-opened database, but |
| 651 // the database is empty, so there would be no point. |
| 652 RecordRecoveryEvent(RECOVERY_SUCCESS_AUTORECOVERDB_NOTADB_DELETE); |
594 return; | 653 return; |
595 } | 654 } |
596 | 655 |
597 #if DCHECK_IS_ON() | 656 #if DCHECK_IS_ON() |
598 // This code silently fails to recover fts3 virtual tables. At this time no | 657 // 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 | 658 // browser database contain fts3 tables. Just to be safe, complain loudly if |
600 // the database contains virtual tables. | 659 // the database contains virtual tables. |
601 // | 660 // |
602 // fts3 has an [x_segdir] table containing a column [end_block INTEGER]. But | 661 // 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 | 662 // 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. | 765 // - SQLITE_READONLY - permissions could be fixed. |
707 // - SQLITE_IOERR - rewrite using new blocks. | 766 // - SQLITE_IOERR - rewrite using new blocks. |
708 // - SQLITE_FULL - recover in memory and rewrite subset of data. | 767 // - SQLITE_FULL - recover in memory and rewrite subset of data. |
709 | 768 |
710 default: | 769 default: |
711 return false; | 770 return false; |
712 } | 771 } |
713 } | 772 } |
714 | 773 |
715 } // namespace sql | 774 } // namespace sql |
OLD | NEW |