recovery.cc 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641
  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
  3. // found in the LICENSE file.
  4. #include "sql/recovery.h"
  5. #include <stddef.h>
  6. #include <memory>
  7. #include <string>
  8. #include <tuple>
  9. #include <utility>
  10. #include <vector>
  11. #include "base/bind.h"
  12. #include "base/check_op.h"
  13. #include "base/dcheck_is_on.h"
  14. #include "base/files/file_path.h"
  15. #include "base/format_macros.h"
  16. #include "base/logging.h"
  17. #include "base/notreached.h"
  18. #include "base/strings/string_util.h"
  19. #include "base/strings/stringprintf.h"
  20. #include "base/types/pass_key.h"
  21. #include "sql/database.h"
  22. #include "sql/recover_module/module.h"
  23. #include "sql/statement.h"
  24. #include "third_party/sqlite/sqlite3.h"
  25. namespace sql {
  26. // static
  27. std::unique_ptr<Recovery> Recovery::Begin(Database* database,
  28. const base::FilePath& db_path) {
  29. // Recovery is likely to be used in error handling. Since recovery changes
  30. // the state of the handle, protect against multiple layers attempting the
  31. // same recovery.
  32. if (!database->is_open()) {
  33. // Warn about API mis-use.
  34. DCHECK(database->poisoned(InternalApiToken()))
  35. << "Illegal to recover with closed Database";
  36. return nullptr;
  37. }
  38. // Using `new` to access a non-public constructor
  39. std::unique_ptr<Recovery> recovery(new Recovery(database));
  40. if (!recovery->Init(db_path)) {
  41. // TODO(shess): Should Init() failure result in Raze()?
  42. recovery->Shutdown(POISON);
  43. return nullptr;
  44. }
  45. return recovery;
  46. }
  47. // static
  48. bool Recovery::Recovered(std::unique_ptr<Recovery> r) {
  49. return r->Backup();
  50. }
  51. // static
  52. void Recovery::Unrecoverable(std::unique_ptr<Recovery> r) {
  53. CHECK(r->db_);
  54. // ~Recovery() will RAZE_AND_POISON.
  55. }
  56. // static
  57. void Recovery::Rollback(std::unique_ptr<Recovery> r) {
  58. // TODO(shess): Crash / crash and dump?
  59. r->Shutdown(POISON);
  60. }
  61. Recovery::Recovery(Database* connection)
  62. : db_(connection),
  63. recover_db_({
  64. .exclusive_locking = false,
  65. .page_size = db_->page_size(),
  66. // The interface to the recovery module is a virtual table.
  67. .enable_virtual_tables_discouraged = true,
  68. }) {
  69. // Files with I/O errors cannot be safely memory-mapped.
  70. recover_db_.set_mmap_disabled();
  71. // TODO(shess): This may not handle cases where the default page
  72. // size is used, but the default has changed. I do not think this
  73. // has ever happened. This could be handled by using "PRAGMA
  74. // page_size", at the cost of potential additional failure cases.
  75. }
  76. Recovery::~Recovery() {
  77. Shutdown(RAZE_AND_POISON);
  78. }
  79. bool Recovery::Init(const base::FilePath& db_path) {
  80. #if DCHECK_IS_ON()
  81. // set_error_callback() will DCHECK if the database already has an error
  82. // callback. The recovery process is likely to result in SQLite errors, and
  83. // those shouldn't get surfaced to any callback.
  84. db_->set_error_callback(base::BindRepeating(
  85. [](int sqlite_error_code, sql::Statement* statement) {}));
  86. // Undo the set_error_callback() above. We only used it for its DCHECK
  87. // behavior.
  88. db_->reset_error_callback();
  89. #endif // DCHECK_IS_ON()
  90. // Break any outstanding transactions on the original database to
  91. // prevent deadlocks reading through the attached version.
  92. // TODO(shess): A client may legitimately wish to recover from
  93. // within the transaction context, because it would potentially
  94. // preserve any in-flight changes. Unfortunately, any attach-based
  95. // system could not handle that. A system which manually queried
  96. // one database and stored to the other possibly could, but would be
  97. // more complicated.
  98. db_->RollbackAllTransactions();
  99. // Disable exclusive locking mode so that the attached database can
  100. // access things. The locking_mode change is not active until the
  101. // next database access, so immediately force an access. Enabling
  102. // writable_schema allows processing through certain kinds of
  103. // corruption.
  104. // TODO(shess): It would be better to just close the handle, but it
  105. // is necessary for the final backup which rewrites things. It
  106. // might be reasonable to close then re-open the handle.
  107. std::ignore = db_->Execute("PRAGMA writable_schema=1");
  108. std::ignore = db_->Execute("PRAGMA locking_mode=NORMAL");
  109. std::ignore = db_->Execute("SELECT COUNT(*) FROM sqlite_schema");
  110. // TODO(shess): If this is a common failure case, it might be
  111. // possible to fall back to a memory database. But it probably
  112. // implies that the SQLite tmpdir logic is busted, which could cause
  113. // a variety of other random issues in our code.
  114. if (!recover_db_.OpenTemporary(base::PassKey<Recovery>()))
  115. return false;
  116. // Enable the recover virtual table for this connection.
  117. int rc = EnableRecoveryExtension(&recover_db_, InternalApiToken());
  118. if (rc != SQLITE_OK) {
  119. LOG(ERROR) << "Failed to initialize recover module: "
  120. << recover_db_.GetErrorMessage();
  121. return false;
  122. }
  123. // Turn on |SQLITE_RecoveryMode| for the handle, which allows
  124. // reading certain broken databases.
  125. if (!recover_db_.Execute("PRAGMA writable_schema=1"))
  126. return false;
  127. if (!recover_db_.AttachDatabase(db_path, "corrupt", InternalApiToken()))
  128. return false;
  129. return true;
  130. }
  131. bool Recovery::Backup() {
  132. CHECK(db_);
  133. CHECK(recover_db_.is_open());
  134. // TODO(shess): Some of the failure cases here may need further
  135. // exploration. Just as elsewhere, persistent problems probably
  136. // need to be razed, while anything which might succeed on a future
  137. // run probably should be allowed to try. But since Raze() uses the
  138. // same approach, even that wouldn't work when this code fails.
  139. //
  140. // The documentation for the backup system indicate a relatively
  141. // small number of errors are expected:
  142. // SQLITE_BUSY - cannot lock the destination database. This should
  143. // only happen if someone has another handle to the
  144. // database, Chromium generally doesn't do that.
  145. // SQLITE_LOCKED - someone locked the source database. Should be
  146. // impossible (perhaps anti-virus could?).
  147. // SQLITE_READONLY - destination is read-only.
  148. // SQLITE_IOERR - since source database is temporary, probably
  149. // indicates that the destination contains blocks
  150. // throwing errors, or gross filesystem errors.
  151. // SQLITE_NOMEM - out of memory, should be transient.
  152. //
  153. // AFAICT, SQLITE_BUSY and SQLITE_NOMEM could perhaps be considered
  154. // transient, with SQLITE_LOCKED being unclear.
  155. //
  156. // SQLITE_READONLY and SQLITE_IOERR are probably persistent, with a
  157. // strong chance that Raze() would not resolve them. If Delete()
  158. // deletes the database file, the code could then re-open the file
  159. // and attempt the backup again.
  160. //
  161. // For now, this code attempts a best effort.
  162. // Backup the original db from the recovered db.
  163. const char* kMain = "main";
  164. sqlite3_backup* backup =
  165. sqlite3_backup_init(db_->db(InternalApiToken()), kMain,
  166. recover_db_.db(InternalApiToken()), kMain);
  167. if (!backup) {
  168. // Error code is in the destination database handle.
  169. LOG(ERROR) << "sqlite3_backup_init() failed: "
  170. << sqlite3_errmsg(db_->db(InternalApiToken()));
  171. return false;
  172. }
  173. // -1 backs up the entire database.
  174. int rc = sqlite3_backup_step(backup, -1);
  175. int pages = sqlite3_backup_pagecount(backup);
  176. // TODO(shess): sqlite3_backup_finish() appears to allow returning a
  177. // different value from sqlite3_backup_step(). Circle back and
  178. // figure out if that can usefully inform the decision of whether to
  179. // retry or not.
  180. sqlite3_backup_finish(backup);
  181. DCHECK_GT(pages, 0);
  182. if (rc != SQLITE_DONE) {
  183. LOG(ERROR) << "sqlite3_backup_step() failed: "
  184. << sqlite3_errmsg(db_->db(InternalApiToken()));
  185. }
  186. // The destination database was locked. Give up, but leave the data
  187. // in place. Maybe it won't be locked next time.
  188. if (rc == SQLITE_BUSY || rc == SQLITE_LOCKED) {
  189. Shutdown(POISON);
  190. return false;
  191. }
  192. // Running out of memory should be transient, retry later.
  193. if (rc == SQLITE_NOMEM) {
  194. Shutdown(POISON);
  195. return false;
  196. }
  197. // TODO(shess): For now, leave the original database alone. Some errors should
  198. // probably route to RAZE_AND_POISON.
  199. if (rc != SQLITE_DONE) {
  200. Shutdown(POISON);
  201. return false;
  202. }
  203. // Clean up the recovery db, and terminate the main database
  204. // connection.
  205. Shutdown(POISON);
  206. return true;
  207. }
  208. void Recovery::Shutdown(Recovery::Disposition raze) {
  209. if (!db_)
  210. return;
  211. recover_db_.Close();
  212. if (raze == RAZE_AND_POISON) {
  213. db_->RazeAndClose();
  214. } else if (raze == POISON) {
  215. db_->Poison();
  216. }
  217. db_ = nullptr;
  218. }
  219. bool Recovery::AutoRecoverTable(const char* table_name,
  220. size_t* rows_recovered) {
  221. // Query the info for the recovered table in database [main].
  222. std::string query(
  223. base::StringPrintf("PRAGMA main.table_info(%s)", table_name));
  224. Statement s(db()->GetUniqueStatement(query.c_str()));
  225. // The columns of the recover virtual table.
  226. std::vector<std::string> create_column_decls;
  227. // The columns to select from the recover virtual table when copying
  228. // to the recovered table.
  229. std::vector<std::string> insert_columns;
  230. // If PRIMARY KEY is a single INTEGER column, then it is an alias
  231. // for ROWID. The primary key can be compound, so this can only be
  232. // determined after processing all column data and tracking what is
  233. // seen. |pk_column_count| counts the columns in the primary key.
  234. // |rowid_decl| stores the ROWID version of the last INTEGER column
  235. // seen, which is at |rowid_ofs| in |create_column_decls|.
  236. size_t pk_column_count = 0;
  237. size_t rowid_ofs = 0; // Only valid if rowid_decl is set.
  238. std::string rowid_decl; // ROWID version of column |rowid_ofs|.
  239. while (s.Step()) {
  240. const std::string column_name(s.ColumnString(1));
  241. const std::string column_type(s.ColumnString(2));
  242. const ColumnType default_type = s.GetColumnType(4);
  243. const bool default_is_null = (default_type == ColumnType::kNull);
  244. const int pk_column = s.ColumnInt(5);
  245. // http://www.sqlite.org/pragma.html#pragma_table_info documents column 5 as
  246. // the 1-based index of the column in the primary key, otherwise 0.
  247. if (pk_column > 0)
  248. ++pk_column_count;
  249. // Construct column declaration as "name type [optional constraint]".
  250. std::string column_decl = column_name;
  251. // SQLite's affinity detection is documented at:
  252. // http://www.sqlite.org/datatype3.html#affname
  253. // The gist of it is that CHAR, TEXT, and INT use substring matches.
  254. // TODO(shess): It would be nice to unit test the type handling,
  255. // but it is not obvious to me how to write a test which would
  256. // fail appropriately when something was broken. It would have to
  257. // somehow use data which would allow detecting the various type
  258. // coercions which happen. If STRICT could be enabled, type
  259. // mismatches could be detected by which rows are filtered.
  260. if (column_type.find("INT") != std::string::npos) {
  261. if (pk_column == 1) {
  262. rowid_ofs = create_column_decls.size();
  263. rowid_decl = column_name + " ROWID";
  264. }
  265. column_decl += " INTEGER";
  266. } else if (column_type.find("CHAR") != std::string::npos ||
  267. column_type.find("TEXT") != std::string::npos) {
  268. column_decl += " TEXT";
  269. } else if (column_type == "BLOB") {
  270. column_decl += " BLOB";
  271. } else if (column_type.find("DOUB") != std::string::npos) {
  272. column_decl += " FLOAT";
  273. } else {
  274. // TODO(shess): AFAICT, there remain:
  275. // - contains("CLOB") -> TEXT
  276. // - contains("REAL") -> FLOAT
  277. // - contains("FLOA") -> FLOAT
  278. // - other -> "NUMERIC"
  279. // Just code those in as they come up.
  280. NOTREACHED() << " Unsupported type " << column_type;
  281. return false;
  282. }
  283. create_column_decls.push_back(column_decl);
  284. // Per the NOTE in the header file, convert NULL values to the
  285. // DEFAULT. All columns could be IFNULL(column_name,default), but
  286. // the NULL case would require special handling either way.
  287. if (default_is_null) {
  288. insert_columns.push_back(column_name);
  289. } else {
  290. // The default value appears to be pre-quoted, as if it is
  291. // literally from the sqlite_schema CREATE statement.
  292. std::string default_value = s.ColumnString(4);
  293. insert_columns.push_back(base::StringPrintf(
  294. "IFNULL(%s,%s)", column_name.c_str(), default_value.c_str()));
  295. }
  296. }
  297. // Receiving no column information implies that the table doesn't exist.
  298. if (create_column_decls.empty()) {
  299. return false;
  300. }
  301. // If the PRIMARY KEY was a single INTEGER column, convert it to ROWID.
  302. if (pk_column_count == 1 && !rowid_decl.empty())
  303. create_column_decls[rowid_ofs] = rowid_decl;
  304. std::string recover_create(base::StringPrintf(
  305. "CREATE VIRTUAL TABLE temp.recover_%s USING recover(corrupt.%s, %s)",
  306. table_name,
  307. table_name,
  308. base::JoinString(create_column_decls, ",").c_str()));
  309. // INSERT OR IGNORE means that it will drop rows resulting from constraint
  310. // violations. INSERT OR REPLACE only handles UNIQUE constraint violations.
  311. std::string recover_insert(base::StringPrintf(
  312. "INSERT OR IGNORE INTO main.%s SELECT %s FROM temp.recover_%s",
  313. table_name,
  314. base::JoinString(insert_columns, ",").c_str(),
  315. table_name));
  316. std::string recover_drop(base::StringPrintf(
  317. "DROP TABLE temp.recover_%s", table_name));
  318. if (!db()->Execute(recover_create.c_str()))
  319. return false;
  320. if (!db()->Execute(recover_insert.c_str())) {
  321. std::ignore = db()->Execute(recover_drop.c_str());
  322. return false;
  323. }
  324. *rows_recovered = db()->GetLastChangeCount();
  325. // TODO(shess): Is leaving the recover table around a breaker?
  326. return db()->Execute(recover_drop.c_str());
  327. }
  328. bool Recovery::SetupMeta() {
  329. // clang-format off
  330. static const char kCreateSql[] =
  331. "CREATE VIRTUAL TABLE temp.recover_meta USING recover("
  332. "corrupt.meta,"
  333. "key TEXT NOT NULL,"
  334. "value ANY" // Whatever is stored.
  335. ")";
  336. // clang-format on
  337. return db()->Execute(kCreateSql);
  338. }
  339. bool Recovery::GetMetaVersionNumber(int* version) {
  340. DCHECK(version);
  341. // TODO(shess): DCHECK(db()->DoesTableExist("temp.recover_meta"));
  342. // Unfortunately, DoesTableExist() queries sqlite_schema, not
  343. // sqlite_temp_master.
  344. static const char kVersionSql[] =
  345. "SELECT value FROM temp.recover_meta WHERE key = 'version'";
  346. sql::Statement recovery_version(db()->GetUniqueStatement(kVersionSql));
  347. if (!recovery_version.Step())
  348. return false;
  349. *version = recovery_version.ColumnInt(0);
  350. return true;
  351. }
  352. namespace {
  353. // Collect statements from [corrupt.sqlite_schema.sql] which start with |prefix|
  354. // (which should be a valid SQL string ending with the space before a table
  355. // name), then apply the statements to [main]. Skip any table named
  356. // 'sqlite_sequence', as that table is created on demand by SQLite if any tables
  357. // use AUTOINCREMENT.
  358. //
  359. // Returns |true| if all of the matching items were created in the main
  360. // database. Returns |false| if an item fails on creation, or if the corrupt
  361. // database schema cannot be queried.
  362. bool SchemaCopyHelper(Database* db, const char* prefix) {
  363. const size_t prefix_len = strlen(prefix);
  364. DCHECK_EQ(' ', prefix[prefix_len-1]);
  365. sql::Statement s(db->GetUniqueStatement(
  366. "SELECT DISTINCT sql FROM corrupt.sqlite_schema "
  367. "WHERE name<>'sqlite_sequence'"));
  368. while (s.Step()) {
  369. std::string sql = s.ColumnString(0);
  370. // Skip statements that don't start with |prefix|.
  371. if (sql.compare(0, prefix_len, prefix) != 0)
  372. continue;
  373. sql.insert(prefix_len, "main.");
  374. if (!db->Execute(sql.c_str()))
  375. return false;
  376. }
  377. return s.Succeeded();
  378. }
  379. } // namespace
  380. // This method is derived from SQLite's vacuum.c. VACUUM operates very
  381. // similarily, creating a new database, populating the schema, then copying the
  382. // data.
  383. //
  384. // TODO(shess): This conservatively uses Rollback() rather than Unrecoverable().
  385. // With Rollback(), it is expected that the database will continue to generate
  386. // errors. Change the failure cases to Unrecoverable().
  387. //
  388. // static
  389. std::unique_ptr<Recovery> Recovery::BeginRecoverDatabase(
  390. Database* db,
  391. const base::FilePath& db_path) {
  392. std::unique_ptr<sql::Recovery> recovery = sql::Recovery::Begin(db, db_path);
  393. if (!recovery) {
  394. // Close the underlying sqlite* handle. Windows does not allow deleting
  395. // open files, and all platforms block opening a second sqlite3* handle
  396. // against a database when exclusive locking is set.
  397. db->Poison();
  398. // When this code was written, histograms showed that most failures happened
  399. // while attaching a corrupt database. In this case, a large proportion of
  400. // attachment failures were SQLITE_NOTADB.
  401. //
  402. // We currently only delete the database in that specific failure case.
  403. {
  404. Database probe_db;
  405. if (!probe_db.OpenInMemory() ||
  406. probe_db.AttachDatabase(db_path, "corrupt", InternalApiToken()) ||
  407. probe_db.GetErrorCode() != SQLITE_NOTADB) {
  408. return nullptr;
  409. }
  410. }
  411. // The database has invalid data in the SQLite header, so it is almost
  412. // certainly not recoverable without manual intervention (and likely not
  413. // recoverable _with_ manual intervention). Clear away the broken database.
  414. if (!sql::Database::Delete(db_path))
  415. return nullptr;
  416. // Windows deletion is complicated by file scanners and malware - sometimes
  417. // Delete() appears to succeed, even though the file remains. The following
  418. // attempts to track if this happens often enough to cause concern.
  419. {
  420. Database probe_db;
  421. if (!probe_db.Open(db_path))
  422. return nullptr;
  423. if (!probe_db.Execute("PRAGMA auto_vacuum"))
  424. return nullptr;
  425. }
  426. // The rest of the recovery code could be run on the re-opened database, but
  427. // the database is empty, so there would be no point.
  428. return nullptr;
  429. }
  430. #if DCHECK_IS_ON()
  431. // This code silently fails to recover fts3 virtual tables. At this time no
  432. // browser database contain fts3 tables. Just to be safe, complain loudly if
  433. // the database contains virtual tables.
  434. //
  435. // fts3 has an [x_segdir] table containing a column [end_block INTEGER]. But
  436. // it actually stores either an integer or a text containing a pair of
  437. // integers separated by a space. AutoRecoverTable() trusts the INTEGER tag
  438. // when setting up the recover vtable, so those rows get dropped. Setting
  439. // that column to ANY may work.
  440. if (db->is_open()) {
  441. sql::Statement s(db->GetUniqueStatement(
  442. "SELECT 1 FROM sqlite_schema WHERE sql LIKE 'CREATE VIRTUAL TABLE %'"));
  443. DCHECK(!s.Step()) << "Recovery of virtual tables not supported";
  444. }
  445. #endif
  446. // TODO(shess): vacuum.c turns off checks and foreign keys.
  447. // TODO(shess): vacuum.c turns synchronous=OFF for the target. I do not fully
  448. // understand this, as the temporary db should not have a journal file at all.
  449. // Perhaps it does in case of cache spill?
  450. // Copy table schema from [corrupt] to [main].
  451. if (!SchemaCopyHelper(recovery->db(), "CREATE TABLE ") ||
  452. !SchemaCopyHelper(recovery->db(), "CREATE INDEX ") ||
  453. !SchemaCopyHelper(recovery->db(), "CREATE UNIQUE INDEX ")) {
  454. // No RecordRecoveryEvent() here because SchemaCopyHelper() already did.
  455. Recovery::Rollback(std::move(recovery));
  456. return nullptr;
  457. }
  458. // Run auto-recover against each table, skipping the sequence table. This is
  459. // necessary because table recovery can create the sequence table as a side
  460. // effect, so recovering that table inline could lead to duplicate data.
  461. {
  462. sql::Statement s(recovery->db()->GetUniqueStatement(
  463. "SELECT name FROM sqlite_schema WHERE sql LIKE 'CREATE TABLE %' "
  464. "AND name!='sqlite_sequence'"));
  465. while (s.Step()) {
  466. const std::string name = s.ColumnString(0);
  467. size_t rows_recovered;
  468. if (!recovery->AutoRecoverTable(name.c_str(), &rows_recovered)) {
  469. Recovery::Rollback(std::move(recovery));
  470. return nullptr;
  471. }
  472. }
  473. if (!s.Succeeded()) {
  474. Recovery::Rollback(std::move(recovery));
  475. return nullptr;
  476. }
  477. }
  478. // Overwrite any sequences created.
  479. if (recovery->db()->DoesTableExist("corrupt.sqlite_sequence")) {
  480. std::ignore = recovery->db()->Execute("DELETE FROM main.sqlite_sequence");
  481. size_t rows_recovered;
  482. if (!recovery->AutoRecoverTable("sqlite_sequence", &rows_recovered)) {
  483. Recovery::Rollback(std::move(recovery));
  484. return nullptr;
  485. }
  486. }
  487. // Copy triggers and views directly to sqlite_schema. Any tables they refer
  488. // to should already exist.
  489. static const char kCreateMetaItemsSql[] =
  490. "INSERT INTO main.sqlite_schema "
  491. "SELECT type, name, tbl_name, rootpage, sql "
  492. "FROM corrupt.sqlite_schema WHERE type='view' OR type='trigger'";
  493. if (!recovery->db()->Execute(kCreateMetaItemsSql)) {
  494. Recovery::Rollback(std::move(recovery));
  495. return nullptr;
  496. }
  497. return recovery;
  498. }
  499. void Recovery::RecoverDatabase(Database* db, const base::FilePath& db_path) {
  500. std::unique_ptr<sql::Recovery> recovery = BeginRecoverDatabase(db, db_path);
  501. if (recovery)
  502. std::ignore = Recovery::Recovered(std::move(recovery));
  503. }
  504. void Recovery::RecoverDatabaseWithMetaVersion(Database* db,
  505. const base::FilePath& db_path) {
  506. std::unique_ptr<sql::Recovery> recovery = BeginRecoverDatabase(db, db_path);
  507. if (!recovery)
  508. return;
  509. int version = 0;
  510. if (!recovery->SetupMeta() || !recovery->GetMetaVersionNumber(&version)) {
  511. sql::Recovery::Unrecoverable(std::move(recovery));
  512. return;
  513. }
  514. std::ignore = Recovery::Recovered(std::move(recovery));
  515. }
  516. // static
  517. bool Recovery::ShouldRecover(int extended_error) {
  518. // Trim extended error codes.
  519. int error = extended_error & 0xFF;
  520. switch (error) {
  521. case SQLITE_NOTADB:
  522. // SQLITE_NOTADB happens if the SQLite header is broken. Some earlier
  523. // versions of SQLite return this where other versions return
  524. // SQLITE_CORRUPT, which is a recoverable case. Later versions only
  525. // return this error only in unrecoverable cases, in which case recovery
  526. // will fail with no changes to the database, so there's no harm in
  527. // attempting recovery in this case.
  528. return true;
  529. case SQLITE_CORRUPT:
  530. // SQLITE_CORRUPT generally means that the database is readable as a
  531. // SQLite database, but some inconsistency has been detected by SQLite.
  532. // In many cases the inconsistency is relatively trivial, such as if an
  533. // index refers to a row which was deleted, in which case most or even all
  534. // of the data can be recovered. This can also be reported if parts of
  535. // the file have been overwritten with garbage data, in which recovery
  536. // should be able to recover partial data.
  537. return true;
  538. // TODO(shess): Possible future options for automated fixing:
  539. // - SQLITE_CANTOPEN - delete the broken symlink or directory.
  540. // - SQLITE_PERM - permissions could be fixed.
  541. // - SQLITE_READONLY - permissions could be fixed.
  542. // - SQLITE_IOERR - rewrite using new blocks.
  543. // - SQLITE_FULL - recover in memory and rewrite subset of data.
  544. default:
  545. return false;
  546. }
  547. }
  548. // static
  549. int Recovery::EnableRecoveryExtension(Database* db, InternalApiToken) {
  550. return sql::recover::RegisterRecoverExtension(db->db(InternalApiToken()));
  551. }
  552. } // namespace sql