Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / diagnostics / sqlite_diagnostics.cc
1 // Copyright (c) 2012 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
5 #include "chrome/browser/diagnostics/sqlite_diagnostics.h"
6
7 #include "base/base_paths.h"
8 #include "base/files/file_util.h"
9 #include "base/logging.h"
10 #include "base/memory/ref_counted.h"
11 #include "base/memory/singleton.h"
12 #include "base/memory/weak_ptr.h"
13 #include "base/metrics/histogram.h"
14 #include "base/path_service.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "chrome/common/chrome_constants.h"
19 #include "chrome/common/chrome_paths.h"
20 #include "chromeos/chromeos_constants.h"
21 #include "components/webdata/common/webdata_constants.h"
22 #include "content/public/common/content_constants.h"
23 #include "sql/connection.h"
24 #include "sql/statement.h"
25 #include "storage/browser/database/database_tracker.h"
26 #include "third_party/sqlite/sqlite3.h"
27
28 namespace diagnostics {
29
30 namespace {
31
32 // Generic diagnostic test class for checking SQLite database integrity.
33 class SqliteIntegrityTest : public DiagnosticsTest {
34  public:
35   // These are bit flags, so each value should be a power of two.
36   enum Flags {
37     NO_FLAGS_SET = 0,
38     CRITICAL = 0x01,
39     REMOVE_IF_CORRUPT = 0x02,
40   };
41
42   SqliteIntegrityTest(uint32 flags,
43                       DiagnosticsTestId id,
44                       const base::FilePath& db_path)
45       : DiagnosticsTest(id), flags_(flags), db_path_(db_path) {}
46
47   virtual bool RecoveryImpl(DiagnosticsModel::Observer* observer) OVERRIDE {
48     int outcome_code = GetOutcomeCode();
49     if (flags_ & REMOVE_IF_CORRUPT) {
50       switch (outcome_code) {
51         case DIAG_SQLITE_ERROR_HANDLER_CALLED:
52         case DIAG_SQLITE_CANNOT_OPEN_DB:
53         case DIAG_SQLITE_DB_LOCKED:
54         case DIAG_SQLITE_PRAGMA_FAILED:
55         case DIAG_SQLITE_DB_CORRUPTED:
56           LOG(WARNING) << "Removing broken SQLite database: "
57                        << db_path_.value();
58           base::DeleteFile(db_path_, false);
59           break;
60         case DIAG_SQLITE_SUCCESS:
61         case DIAG_SQLITE_FILE_NOT_FOUND_OK:
62         case DIAG_SQLITE_FILE_NOT_FOUND:
63           break;
64         default:
65           DCHECK(false) << "Invalid outcome code: " << outcome_code;
66           break;
67       }
68     }
69     return true;
70   }
71
72   virtual bool ExecuteImpl(DiagnosticsModel::Observer* observer) OVERRIDE {
73     // If we're given an absolute path, use it. If not, then assume it's under
74     // the profile directory.
75     base::FilePath path;
76     if (!db_path_.IsAbsolute())
77       path = GetUserDefaultProfileDir().Append(db_path_);
78     else
79       path = db_path_;
80
81     if (!base::PathExists(path)) {
82       if (flags_ & CRITICAL) {
83         RecordOutcome(DIAG_SQLITE_FILE_NOT_FOUND,
84                       "File not found",
85                       DiagnosticsModel::TEST_FAIL_CONTINUE);
86       } else {
87         RecordOutcome(DIAG_SQLITE_FILE_NOT_FOUND_OK,
88                       "File not found (but that is OK)",
89                       DiagnosticsModel::TEST_OK);
90       }
91       return true;
92     }
93
94     int errors = 0;
95     {  // Scope the statement and database so they close properly.
96       sql::Connection database;
97       database.set_exclusive_locking();
98       scoped_refptr<ErrorRecorder> recorder(new ErrorRecorder);
99
100       // Set the error callback so that we can get useful results in a debug
101       // build for a corrupted database. Without setting the error callback,
102       // sql::Connection will just DCHECK.
103       database.set_error_callback(
104           base::Bind(&SqliteIntegrityTest::ErrorRecorder::RecordSqliteError,
105                      recorder->AsWeakPtr(),
106                      &database));
107       if (!database.Open(path)) {
108         RecordFailure(DIAG_SQLITE_CANNOT_OPEN_DB,
109                       "Cannot open DB. Possibly corrupted");
110         return true;
111       }
112       if (recorder->has_error()) {
113         RecordFailure(DIAG_SQLITE_ERROR_HANDLER_CALLED,
114                       recorder->FormatError());
115         return true;
116       }
117       sql::Statement statement(
118           database.GetUniqueStatement("PRAGMA integrity_check;"));
119       if (recorder->has_error()) {
120         RecordFailure(DIAG_SQLITE_ERROR_HANDLER_CALLED,
121                       recorder->FormatError());
122         return true;
123       }
124       if (!statement.is_valid()) {
125         int error = database.GetErrorCode();
126         if (SQLITE_BUSY == error) {
127           RecordFailure(DIAG_SQLITE_DB_LOCKED,
128                         "Database locked by another process");
129         } else {
130           std::string str("Pragma failed. Error: ");
131           str += base::IntToString(error);
132           RecordFailure(DIAG_SQLITE_PRAGMA_FAILED, str);
133         }
134         return false;
135       }
136
137       while (statement.Step()) {
138         std::string result(statement.ColumnString(0));
139         if ("ok" != result)
140           ++errors;
141       }
142       if (recorder->has_error()) {
143         RecordFailure(DIAG_SQLITE_ERROR_HANDLER_CALLED,
144                       recorder->FormatError());
145         return true;
146       }
147     }
148
149     // All done. Report to the user.
150     if (errors != 0) {
151       std::string str("Database corruption detected: ");
152       str += base::IntToString(errors) + " errors";
153       RecordFailure(DIAG_SQLITE_DB_CORRUPTED, str);
154       return true;
155     }
156     RecordSuccess("No corruption detected");
157     return true;
158   }
159
160  private:
161   class ErrorRecorder : public base::RefCounted<ErrorRecorder>,
162                         public base::SupportsWeakPtr<ErrorRecorder> {
163    public:
164     ErrorRecorder() : has_error_(false), sqlite_error_(0), last_errno_(0) {}
165
166     void RecordSqliteError(sql::Connection* connection,
167                            int sqlite_error,
168                            sql::Statement* statement) {
169       has_error_ = true;
170       sqlite_error_ = sqlite_error;
171       last_errno_ = connection->GetLastErrno();
172       message_ = connection->GetErrorMessage();
173     }
174
175     bool has_error() const { return has_error_; }
176
177     std::string FormatError() {
178       return base::StringPrintf("SQLite error: %d, Last Errno: %d: %s",
179                                 sqlite_error_,
180                                 last_errno_,
181                                 message_.c_str());
182     }
183
184    private:
185     friend class base::RefCounted<ErrorRecorder>;
186     ~ErrorRecorder() {}
187
188     bool has_error_;
189     int sqlite_error_;
190     int last_errno_;
191     std::string message_;
192
193     DISALLOW_COPY_AND_ASSIGN(ErrorRecorder);
194   };
195
196   uint32 flags_;
197   base::FilePath db_path_;
198   DISALLOW_COPY_AND_ASSIGN(SqliteIntegrityTest);
199 };
200
201 }  // namespace
202
203 DiagnosticsTest* MakeSqliteCookiesDbTest() {
204   return new SqliteIntegrityTest(SqliteIntegrityTest::CRITICAL,
205                                  DIAGNOSTICS_SQLITE_INTEGRITY_COOKIE_TEST,
206                                  base::FilePath(chrome::kCookieFilename));
207 }
208
209 DiagnosticsTest* MakeSqliteWebDatabaseTrackerDbTest() {
210   base::FilePath databases_dir(storage::kDatabaseDirectoryName);
211   base::FilePath tracker_db =
212       databases_dir.Append(storage::kTrackerDatabaseFileName);
213   return new SqliteIntegrityTest(
214       SqliteIntegrityTest::NO_FLAGS_SET,
215       DIAGNOSTICS_SQLITE_INTEGRITY_DATABASE_TRACKER_TEST,
216       tracker_db);
217 }
218
219 DiagnosticsTest* MakeSqliteHistoryDbTest() {
220   return new SqliteIntegrityTest(SqliteIntegrityTest::CRITICAL,
221                                  DIAGNOSTICS_SQLITE_INTEGRITY_HISTORY_TEST,
222                                  base::FilePath(chrome::kHistoryFilename));
223 }
224
225 #if defined(OS_CHROMEOS)
226 DiagnosticsTest* MakeSqliteNssCertDbTest() {
227   base::FilePath home_dir;
228   PathService::Get(base::DIR_HOME, &home_dir);
229   return new SqliteIntegrityTest(SqliteIntegrityTest::REMOVE_IF_CORRUPT,
230                                  DIAGNOSTICS_SQLITE_INTEGRITY_NSS_CERT_TEST,
231                                  home_dir.Append(chromeos::kNssCertDbPath));
232 }
233
234 DiagnosticsTest* MakeSqliteNssKeyDbTest() {
235   base::FilePath home_dir;
236   PathService::Get(base::DIR_HOME, &home_dir);
237   return new SqliteIntegrityTest(SqliteIntegrityTest::REMOVE_IF_CORRUPT,
238                                  DIAGNOSTICS_SQLITE_INTEGRITY_NSS_KEY_TEST,
239                                  home_dir.Append(chromeos::kNssKeyDbPath));
240 }
241 #endif  // defined(OS_CHROMEOS)
242
243 DiagnosticsTest* MakeSqliteThumbnailsDbTest() {
244   return new SqliteIntegrityTest(SqliteIntegrityTest::NO_FLAGS_SET,
245                                  DIAGNOSTICS_SQLITE_INTEGRITY_THUMBNAILS_TEST,
246                                  base::FilePath(chrome::kThumbnailsFilename));
247 }
248
249 DiagnosticsTest* MakeSqliteWebDataDbTest() {
250   return new SqliteIntegrityTest(SqliteIntegrityTest::CRITICAL,
251                                  DIAGNOSTICS_SQLITE_INTEGRITY_WEB_DATA_TEST,
252                                  base::FilePath(kWebDataFilename));
253 }
254
255 }  // namespace diagnostics