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 "content/browser/appcache/appcache_database.h"
6
7 #include "base/auto_reset.h"
8 #include "base/bind.h"
9 #include "base/command_line.h"
10 #include "base/files/file_util.h"
11 #include "base/logging.h"
12 #include "base/stl_util.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "content/browser/appcache/appcache_backfillers.h"
15 #include "content/browser/appcache/appcache_entry.h"
16 #include "content/browser/appcache/appcache_histograms.h"
17 #include "sql/database.h"
18 #include "sql/error_delegate_util.h"
19 #include "sql/meta_table.h"
20 #include "sql/statement.h"
21 #include "sql/transaction.h"
22 #include "storage/browser/quota/padding_key.h"
23 #include "third_party/blink/public/common/features.h"
24
25 namespace content {
26
27 // Schema -------------------------------------------------------------------
28 namespace {
29
30 // Version number of the database.
31 //
32 // We support migrating the database schema from versions that are at most 2
33 // years old. Older versions are unsupported, and will cause the database to get
34 // nuked.
35 //
36 // Version 0 - 2009-12-28 - https://crrev.com/501033 (unsupported)
37 // Version 1 - 2010-01-20 - https://crrev.com/554008 (unsupported)
38 // Version 2 - 2010-02-23 - https://crrev.com/630009 (unsupported)
39 // Version 3 - 2010-03-17 - https://crrev.com/886003 (unsupported)
40 // Version 4 - 2011-12-12 - https://crrev.com/8396013 (unsupported)
41 // Version 5 - 2013-03-29 - https://crrev.com/12628006 (unsupported)
42 // Version 6 - 2013-09-20 - https://crrev.com/23503069 (unsupported)
43 // Version 7 - 2015-07-09 - https://crrev.com/879393002
44 // Version 8 - 2019-03-18 - https://crrev.com/c/1488059
45 // Version 9 - 2019-11-25 - https://crrev.com/c/1935034
46 // Version 10 - 2020-03-09 - https://crrev.com/c/2099463
47 const int kCurrentVersion = 10;
48 const int kCompatibleVersion = 10;
49 const bool kCreateIfNeeded = true;
50 const bool kDontCreate = false;
51
52 // A mechanism to run experiments that may affect in data being persisted
53 // in different ways such that when the experiment is toggled on/off via
54 // cmd line flags, the database gets reset. The active flags are stored at
55 // the time of database creation and compared when reopening. If different
56 // the database is reset.
57 const char kExperimentFlagsKey[] = "ExperimentFlags";
58
59 const char kGroupsTable[] = "Groups";
60 const char kCachesTable[] = "Caches";
61 const char kEntriesTable[] = "Entries";
62 const char kNamespacesTable[] = "Namespaces";
63 // TODO(crbug.com/1108479): Update table name, add migration.
64 const char kOnlineSafeListsTable[] = "OnlineWhiteLists";
65 const char kDeletableResponseIdsTable[] = "DeletableResponseIds";
66
67 struct TableInfo {
68 const char* table_name;
69 const char* columns;
70 };
71
72 struct IndexInfo {
73 const char* index_name;
74 const char* table_name;
75 const char* columns;
76 bool unique;
77 };
78
79 const TableInfo kTables[] = {
80 {kGroupsTable,
81 "(group_id INTEGER PRIMARY KEY,"
82 " origin TEXT,"
83 " manifest_url TEXT,"
84 " creation_time INTEGER,"
85 " last_access_time INTEGER,"
86 " last_full_update_check_time INTEGER,"
87 " first_evictable_error_time INTEGER,"
88 " token_expires INTEGER)"},
89
90 {kCachesTable,
91 "(cache_id INTEGER PRIMARY KEY,"
92 " group_id INTEGER,"
93 " online_wildcard INTEGER CHECK(online_wildcard IN (0, 1)),"
94 " update_time INTEGER,"
95 " cache_size INTEGER," // intentionally not normalized
96 " padding_size INTEGER," // intentionally not normalized
97 " manifest_parser_version INTEGER,"
98 " manifest_scope TEXT,"
99 " token_expires INTEGER)"},
100
101 {kEntriesTable,
102 "(cache_id INTEGER,"
103 " url TEXT,"
104 " flags INTEGER,"
105 " response_id INTEGER,"
106 " response_size INTEGER,"
107 " padding_size INTEGER,"
108 " token_expires INTEGER)"},
109
110 // The |is_pattern| field is obsolete.
111 {kNamespacesTable,
112 "(cache_id INTEGER,"
113 " origin TEXT," // intentionally not normalized
114 " type INTEGER,"
115 " namespace_url TEXT,"
116 " target_url TEXT,"
117 " is_pattern INTEGER CHECK(is_pattern IN (0, 1)),"
118 " token_expires INTEGER)"},
119
120 // The |is_pattern| field is obsolete.
121 {kOnlineSafeListsTable,
122 "(cache_id INTEGER,"
123 " namespace_url TEXT,"
124 " is_pattern INTEGER CHECK(is_pattern IN (0, 1)))"},
125
126 {kDeletableResponseIdsTable, "(response_id INTEGER NOT NULL)"},
127 };
128
129 const IndexInfo kIndexes[] = {
130 {"GroupsOriginIndex", kGroupsTable, "(origin)", false},
131
132 {"GroupsManifestIndex", kGroupsTable, "(manifest_url)", true},
133
134 {"CachesGroupIndex", kCachesTable, "(group_id)", false},
135
136 {"EntriesCacheIndex", kEntriesTable, "(cache_id)", false},
137
138 {"EntriesCacheAndUrlIndex", kEntriesTable, "(cache_id, url)", true},
139
140 {"EntriesResponseIdIndex", kEntriesTable, "(response_id)", true},
141
142 {"NamespacesCacheIndex", kNamespacesTable, "(cache_id)", false},
143
144 {"NamespacesOriginIndex", kNamespacesTable, "(origin)", false},
145
146 {"NamespacesCacheAndUrlIndex", kNamespacesTable,
147 "(cache_id, namespace_url)", true},
148
149 // TODO(crbug.com/1108479): Update table name, add migration.
150 {"OnlineWhiteListCacheIndex", kOnlineSafeListsTable, "(cache_id)", false},
151
152 {"DeletableResponsesIdIndex", kDeletableResponseIdsTable, "(response_id)",
153 true},
154 };
155
CreateTable(sql::Database * db,const TableInfo & info)156 bool CreateTable(sql::Database* db, const TableInfo& info) {
157 std::string sql("CREATE TABLE ");
158 sql += info.table_name;
159 sql += info.columns;
160 return db->Execute(sql.c_str());
161 }
162
CreateIndex(sql::Database * db,const IndexInfo & info)163 bool CreateIndex(sql::Database* db, const IndexInfo& info) {
164 std::string sql;
165 if (info.unique)
166 sql += "CREATE UNIQUE INDEX ";
167 else
168 sql += "CREATE INDEX ";
169 sql += info.index_name;
170 sql += " ON ";
171 sql += info.table_name;
172 sql += info.columns;
173 return db->Execute(sql.c_str());
174 }
175
GetActiveExperimentFlags()176 std::string GetActiveExperimentFlags() {
177 return std::string();
178 }
179
180 // GetURL().spec() is used instead of Serialize() to ensure
181 // backwards compatibility with older data.
SerializeOrigin(const url::Origin & origin)182 std::string SerializeOrigin(const url::Origin& origin) {
183 return origin.GetURL().spec();
184 }
185
186 } // anon namespace
187
188 // AppCacheDatabase ----------------------------------------------------------
189
GroupRecord()190 AppCacheDatabase::GroupRecord::GroupRecord()
191 : group_id(0) {
192 }
193
194 AppCacheDatabase::GroupRecord::GroupRecord(const GroupRecord& other) = default;
195
196 AppCacheDatabase::GroupRecord::~GroupRecord() = default;
197
198 AppCacheDatabase::CacheRecord::CacheRecord() = default;
199
200 AppCacheDatabase::CacheRecord::CacheRecord(const CacheRecord& other) = default;
201
202 AppCacheDatabase::CacheRecord::~CacheRecord() = default;
203
NamespaceRecord()204 AppCacheDatabase::NamespaceRecord::NamespaceRecord()
205 : cache_id(0) {
206 }
207
208 AppCacheDatabase::NamespaceRecord::~NamespaceRecord() = default;
209
AppCacheDatabase(const base::FilePath & path)210 AppCacheDatabase::AppCacheDatabase(const base::FilePath& path)
211 : db_file_path_(path),
212 is_disabled_(false),
213 is_recreating_(false),
214 was_corruption_detected_(false) {
215 }
216
~AppCacheDatabase()217 AppCacheDatabase::~AppCacheDatabase() {
218 CommitLazyLastAccessTimes();
219 }
220
Disable()221 void AppCacheDatabase::Disable() {
222 VLOG(1) << "Disabling appcache database.";
223 is_disabled_ = true;
224 ResetConnectionAndTables();
225 }
226
GetOriginUsage(const url::Origin & origin)227 int64_t AppCacheDatabase::GetOriginUsage(const url::Origin& origin) {
228 std::vector<CacheRecord> caches;
229 if (!FindCachesForOrigin(origin, &caches))
230 return 0;
231
232 int64_t origin_usage = 0;
233 for (const auto& cache : caches)
234 origin_usage += cache.cache_size + cache.padding_size;
235 return origin_usage;
236 }
237
GetAllOriginUsage(std::map<url::Origin,int64_t> * usage_map)238 bool AppCacheDatabase::GetAllOriginUsage(
239 std::map<url::Origin, int64_t>* usage_map) {
240 std::set<url::Origin> origins;
241 if (!FindOriginsWithGroups(&origins))
242 return false;
243 for (const auto& origin : origins)
244 (*usage_map)[origin] = GetOriginUsage(origin);
245 return true;
246 }
247
FindOriginsWithGroups(std::set<url::Origin> * origins)248 bool AppCacheDatabase::FindOriginsWithGroups(std::set<url::Origin>* origins) {
249 DCHECK(origins && origins->empty());
250 if (!LazyOpen(kDontCreate))
251 return false;
252
253 static const char kSql[] = "SELECT DISTINCT(origin) FROM Groups";
254
255 sql::Statement statement(db_->GetUniqueStatement(kSql));
256
257 while (statement.Step())
258 origins->insert(url::Origin::Create(GURL(statement.ColumnString(0))));
259
260 return statement.Succeeded();
261 }
262
FindLastStorageIds(int64_t * last_group_id,int64_t * last_cache_id,int64_t * last_response_id,int64_t * last_deletable_response_rowid)263 bool AppCacheDatabase::FindLastStorageIds(
264 int64_t* last_group_id,
265 int64_t* last_cache_id,
266 int64_t* last_response_id,
267 int64_t* last_deletable_response_rowid) {
268 DCHECK(last_group_id && last_cache_id && last_response_id &&
269 last_deletable_response_rowid);
270
271 *last_group_id = 0;
272 *last_cache_id = 0;
273 *last_response_id = 0;
274 *last_deletable_response_rowid = 0;
275
276 if (!LazyOpen(kDontCreate))
277 return false;
278
279 static const char kMaxGroupIdSql[] = "SELECT MAX(group_id) FROM Groups";
280 static const char kMaxCacheIdSql[] = "SELECT MAX(cache_id) FROM Caches";
281 static const char kMaxResponseIdFromEntriesSql[] =
282 "SELECT MAX(response_id) FROM Entries";
283 static const char kMaxResponseIdFromDeletablesSql[] =
284 "SELECT MAX(response_id) FROM DeletableResponseIds";
285 static const char kMaxDeletableResponseRowIdSql[] =
286 "SELECT MAX(rowid) FROM DeletableResponseIds";
287 int64_t max_group_id;
288 int64_t max_cache_id;
289 int64_t max_response_id_from_entries;
290 int64_t max_response_id_from_deletables;
291 int64_t max_deletable_response_rowid;
292 if (!RunUniqueStatementWithInt64Result(kMaxGroupIdSql, &max_group_id) ||
293 !RunUniqueStatementWithInt64Result(kMaxCacheIdSql, &max_cache_id) ||
294 !RunUniqueStatementWithInt64Result(kMaxResponseIdFromEntriesSql,
295 &max_response_id_from_entries) ||
296 !RunUniqueStatementWithInt64Result(kMaxResponseIdFromDeletablesSql,
297 &max_response_id_from_deletables) ||
298 !RunUniqueStatementWithInt64Result(kMaxDeletableResponseRowIdSql,
299 &max_deletable_response_rowid)) {
300 return false;
301 }
302
303 *last_group_id = max_group_id;
304 *last_cache_id = max_cache_id;
305 *last_response_id = std::max(max_response_id_from_entries,
306 max_response_id_from_deletables);
307 *last_deletable_response_rowid = max_deletable_response_rowid;
308 return true;
309 }
310
FindGroup(int64_t group_id,GroupRecord * record)311 bool AppCacheDatabase::FindGroup(int64_t group_id, GroupRecord* record) {
312 DCHECK(record);
313 if (!LazyOpen(kDontCreate))
314 return false;
315
316 static const char kSql[] =
317 "SELECT group_id, origin, manifest_url,"
318 " creation_time, last_access_time,"
319 " last_full_update_check_time,"
320 " first_evictable_error_time,"
321 " token_expires"
322 " FROM Groups WHERE group_id = ?";
323
324 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
325
326 statement.BindInt64(0, group_id);
327 if (!statement.Step())
328 return false;
329
330 ReadGroupRecord(statement, record);
331 DCHECK(record->group_id == group_id);
332 return true;
333 }
334
FindGroupForManifestUrl(const GURL & manifest_url,GroupRecord * record)335 bool AppCacheDatabase::FindGroupForManifestUrl(
336 const GURL& manifest_url, GroupRecord* record) {
337 DCHECK(record);
338 if (!LazyOpen(kDontCreate))
339 return false;
340
341 static const char kSql[] =
342 "SELECT group_id, origin, manifest_url,"
343 " creation_time, last_access_time,"
344 " last_full_update_check_time,"
345 " first_evictable_error_time,"
346 " token_expires"
347 " FROM Groups WHERE manifest_url = ?";
348
349 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
350 statement.BindString(0, manifest_url.spec());
351
352 if (!statement.Step())
353 return false;
354
355 ReadGroupRecord(statement, record);
356 DCHECK(record->manifest_url == manifest_url);
357 return true;
358 }
359
FindGroupsForOrigin(const url::Origin & origin,std::vector<GroupRecord> * records)360 bool AppCacheDatabase::FindGroupsForOrigin(const url::Origin& origin,
361 std::vector<GroupRecord>* records) {
362 DCHECK(records && records->empty());
363 if (!LazyOpen(kDontCreate))
364 return false;
365
366 static const char kSql[] =
367 "SELECT group_id, origin, manifest_url,"
368 " creation_time, last_access_time,"
369 " last_full_update_check_time,"
370 " first_evictable_error_time,"
371 " token_expires"
372 " FROM Groups WHERE origin = ?";
373
374 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
375 statement.BindString(0, SerializeOrigin(origin));
376
377 while (statement.Step()) {
378 records->push_back(GroupRecord());
379 ReadGroupRecord(statement, &records->back());
380 DCHECK(records->back().origin == origin);
381 }
382
383 return statement.Succeeded();
384 }
385
FindGroupForCache(int64_t cache_id,GroupRecord * record)386 bool AppCacheDatabase::FindGroupForCache(int64_t cache_id,
387 GroupRecord* record) {
388 DCHECK(record);
389 if (!LazyOpen(kDontCreate))
390 return false;
391
392 static const char kSql[] =
393 "SELECT g.group_id, g.origin, g.manifest_url,"
394 " g.creation_time, g.last_access_time,"
395 " g.last_full_update_check_time,"
396 " g.first_evictable_error_time,"
397 " g.token_expires"
398 " FROM Groups g, Caches c"
399 " WHERE c.cache_id = ? AND c.group_id = g.group_id";
400
401 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
402 statement.BindInt64(0, cache_id);
403
404 if (!statement.Step())
405 return false;
406
407 ReadGroupRecord(statement, record);
408 return true;
409 }
410
InsertGroup(const GroupRecord * record)411 bool AppCacheDatabase::InsertGroup(const GroupRecord* record) {
412 if (!LazyOpen(kCreateIfNeeded))
413 return false;
414
415 static const char kSql[] =
416 "INSERT INTO Groups"
417 " (group_id, origin, manifest_url, creation_time, last_access_time,"
418 " last_full_update_check_time, first_evictable_error_time,"
419 " token_expires)"
420 " VALUES(?, ?, ?, ?, ?, ?, ?, ?)";
421 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
422 statement.BindInt64(0, record->group_id);
423 statement.BindString(1, SerializeOrigin(record->origin));
424 statement.BindString(2, record->manifest_url.spec());
425 statement.BindInt64(3, record->creation_time.ToInternalValue());
426 statement.BindInt64(4, record->last_access_time.ToInternalValue());
427 statement.BindInt64(5, record->last_full_update_check_time.ToInternalValue());
428 statement.BindInt64(6, record->first_evictable_error_time.ToInternalValue());
429 statement.BindInt64(7, record->token_expires.ToInternalValue());
430 return statement.Run();
431 }
432
DeleteGroup(int64_t group_id)433 bool AppCacheDatabase::DeleteGroup(int64_t group_id) {
434 if (!LazyOpen(kDontCreate))
435 return false;
436
437 static const char kSql[] = "DELETE FROM Groups WHERE group_id = ?";
438 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
439 statement.BindInt64(0, group_id);
440 return statement.Run();
441 }
442
UpdateLastAccessTime(int64_t group_id,base::Time time)443 bool AppCacheDatabase::UpdateLastAccessTime(int64_t group_id, base::Time time) {
444 if (!LazyUpdateLastAccessTime(group_id, time))
445 return false;
446 return CommitLazyLastAccessTimes();
447 }
448
LazyUpdateLastAccessTime(int64_t group_id,base::Time time)449 bool AppCacheDatabase::LazyUpdateLastAccessTime(int64_t group_id,
450 base::Time time) {
451 if (!LazyOpen(kCreateIfNeeded))
452 return false;
453 lazy_last_access_times_[group_id] = time;
454 return true;
455 }
456
CommitLazyLastAccessTimes()457 bool AppCacheDatabase::CommitLazyLastAccessTimes() {
458 if (lazy_last_access_times_.empty())
459 return true;
460 if (!LazyOpen(kDontCreate))
461 return false;
462
463 sql::Transaction transaction(db_.get());
464 if (!transaction.Begin())
465 return false;
466 for (const auto& pair : lazy_last_access_times_) {
467 static const char kSql[] =
468 "UPDATE Groups SET last_access_time = ? WHERE group_id = ?";
469 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
470 statement.BindInt64(0, pair.second.ToInternalValue()); // time
471 statement.BindInt64(1, pair.first); // group_id
472 statement.Run();
473 }
474 lazy_last_access_times_.clear();
475 return transaction.Commit();
476 }
477
UpdateEvictionTimes(int64_t group_id,base::Time last_full_update_check_time,base::Time first_evictable_error_time)478 bool AppCacheDatabase::UpdateEvictionTimes(
479 int64_t group_id,
480 base::Time last_full_update_check_time,
481 base::Time first_evictable_error_time) {
482 if (!LazyOpen(kCreateIfNeeded))
483 return false;
484
485 static const char kSql[] =
486 "UPDATE Groups"
487 " SET last_full_update_check_time = ?,"
488 " first_evictable_error_time = ?"
489 " WHERE group_id = ?";
490 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
491 statement.BindInt64(0, last_full_update_check_time.ToInternalValue());
492 statement.BindInt64(1, first_evictable_error_time.ToInternalValue());
493 statement.BindInt64(2, group_id);
494 return statement.Run(); // Will succeed even if group_id is invalid.
495 }
496
FindCache(int64_t cache_id,CacheRecord * record)497 bool AppCacheDatabase::FindCache(int64_t cache_id, CacheRecord* record) {
498 DCHECK(record);
499 if (!LazyOpen(kDontCreate))
500 return false;
501
502 static const char kSql[] =
503 "SELECT cache_id, group_id, online_wildcard, update_time, cache_size, "
504 "padding_size, manifest_parser_version, manifest_scope, token_expires"
505 " FROM Caches WHERE cache_id = ?";
506
507 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
508 statement.BindInt64(0, cache_id);
509
510 if (!statement.Step())
511 return false;
512
513 ReadCacheRecord(statement, record);
514 return true;
515 }
516
FindCacheForGroup(int64_t group_id,CacheRecord * record)517 bool AppCacheDatabase::FindCacheForGroup(int64_t group_id,
518 CacheRecord* record) {
519 DCHECK(record);
520 if (!LazyOpen(kDontCreate))
521 return false;
522
523 static const char kSql[] =
524 "SELECT cache_id, group_id, online_wildcard, update_time, cache_size, "
525 "padding_size, manifest_parser_version, manifest_scope, token_expires"
526 " FROM Caches WHERE group_id = ?";
527
528 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
529 statement.BindInt64(0, group_id);
530
531 if (!statement.Step())
532 return false;
533
534 ReadCacheRecord(statement, record);
535 return true;
536 }
537
FindCachesForOrigin(const url::Origin & origin,std::vector<CacheRecord> * records)538 bool AppCacheDatabase::FindCachesForOrigin(const url::Origin& origin,
539 std::vector<CacheRecord>* records) {
540 DCHECK(records);
541 std::vector<GroupRecord> group_records;
542 if (!FindGroupsForOrigin(origin, &group_records))
543 return false;
544
545 for (const auto& record : group_records) {
546 CacheRecord cache_record;
547 if (!FindCacheForGroup(record.group_id, &cache_record))
548 continue;
549 if (HasValidOriginTrialToken(&cache_record))
550 records->push_back(cache_record);
551 }
552 return true;
553 }
554
InsertCache(const CacheRecord * record)555 bool AppCacheDatabase::InsertCache(const CacheRecord* record) {
556 if (!LazyOpen(kCreateIfNeeded))
557 return false;
558
559 static const char kSql[] =
560 "INSERT INTO Caches (cache_id, group_id, online_wildcard,"
561 " update_time, cache_size, padding_size,"
562 " manifest_parser_version, manifest_scope,"
563 " token_expires)"
564 " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)";
565
566 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
567 statement.BindInt64(0, record->cache_id);
568 statement.BindInt64(1, record->group_id);
569 statement.BindBool(2, record->online_wildcard);
570 statement.BindInt64(3, record->update_time.ToInternalValue());
571 DCHECK_GE(record->cache_size, 0);
572 statement.BindInt64(4, record->cache_size);
573 DCHECK_GE(record->padding_size, 0);
574 statement.BindInt64(5, record->padding_size);
575 statement.BindInt64(6, record->manifest_parser_version);
576 DCHECK_NE(record->manifest_parser_version, -1);
577 statement.BindString(7, record->manifest_scope);
578 DCHECK_NE(record->manifest_scope, "");
579 statement.BindInt64(8, record->token_expires.ToInternalValue());
580
581 return statement.Run();
582 }
583
DeleteCache(int64_t cache_id)584 bool AppCacheDatabase::DeleteCache(int64_t cache_id) {
585 if (!LazyOpen(kDontCreate))
586 return false;
587
588 static const char kSql[] = "DELETE FROM Caches WHERE cache_id = ?";
589
590 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
591 statement.BindInt64(0, cache_id);
592
593 return statement.Run();
594 }
595
FindEntriesForCache(int64_t cache_id,std::vector<EntryRecord> * records)596 bool AppCacheDatabase::FindEntriesForCache(int64_t cache_id,
597 std::vector<EntryRecord>* records) {
598 DCHECK(records && records->empty());
599 if (!LazyOpen(kDontCreate))
600 return false;
601
602 static const char kSql[] =
603 "SELECT cache_id, url, flags, response_id, response_size, padding_size, "
604 " token_expires "
605 "FROM Entries"
606 " WHERE cache_id = ?";
607
608 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
609 statement.BindInt64(0, cache_id);
610
611 while (statement.Step()) {
612 records->push_back(EntryRecord());
613 ReadEntryRecord(statement, &records->back());
614 DCHECK(records->back().cache_id == cache_id);
615 }
616
617 return statement.Succeeded();
618 }
619
FindEntriesForUrl(const GURL & url,std::vector<EntryRecord> * records)620 bool AppCacheDatabase::FindEntriesForUrl(
621 const GURL& url, std::vector<EntryRecord>* records) {
622 DCHECK(records && records->empty());
623 if (!LazyOpen(kDontCreate))
624 return false;
625
626 static const char kSql[] =
627 "SELECT cache_id, url, flags, response_id, response_size, padding_size, "
628 " token_expires "
629 "FROM Entries"
630 " WHERE url = ?";
631
632 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
633 statement.BindString(0, url.spec());
634
635 while (statement.Step()) {
636 records->push_back(EntryRecord());
637 ReadEntryRecord(statement, &records->back());
638 DCHECK(records->back().url == url);
639 }
640
641 return statement.Succeeded();
642 }
643
FindEntry(int64_t cache_id,const GURL & url,EntryRecord * record)644 bool AppCacheDatabase::FindEntry(int64_t cache_id,
645 const GURL& url,
646 EntryRecord* record) {
647 DCHECK(record);
648 if (!LazyOpen(kDontCreate))
649 return false;
650
651 static const char kSql[] =
652 "SELECT cache_id, url, flags, response_id, response_size, padding_size, "
653 " token_expires "
654 "FROM Entries"
655 " WHERE cache_id = ? AND url = ?";
656
657 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
658 statement.BindInt64(0, cache_id);
659 statement.BindString(1, url.spec());
660
661 if (!statement.Step())
662 return false;
663
664 ReadEntryRecord(statement, record);
665 DCHECK(record->cache_id == cache_id);
666 DCHECK(record->url == url);
667 return true;
668 }
669
InsertEntry(const EntryRecord * record)670 bool AppCacheDatabase::InsertEntry(const EntryRecord* record) {
671 if (!LazyOpen(kCreateIfNeeded))
672 return false;
673
674 static const char kSql[] =
675 "INSERT INTO Entries (cache_id, url, flags, response_id, response_size, "
676 "padding_size, token_expires)"
677 " VALUES(?, ?, ?, ?, ?, ?, ?)";
678
679 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
680 statement.BindInt64(0, record->cache_id);
681 statement.BindString(1, record->url.spec());
682 statement.BindInt(2, record->flags);
683 statement.BindInt64(3, record->response_id);
684 DCHECK_GE(record->response_size, 0);
685 statement.BindInt64(4, record->response_size);
686 DCHECK_GE(record->padding_size, 0);
687 statement.BindInt64(5, record->padding_size);
688 statement.BindInt64(6, record->token_expires.ToInternalValue());
689
690 return statement.Run();
691 }
692
InsertEntryRecords(const std::vector<EntryRecord> & records)693 bool AppCacheDatabase::InsertEntryRecords(
694 const std::vector<EntryRecord>& records) {
695 if (records.empty())
696 return true;
697 sql::Transaction transaction(db_.get());
698 if (!transaction.Begin())
699 return false;
700 for (const auto& record : records) {
701 if (!InsertEntry(&record))
702 return false;
703 }
704 return transaction.Commit();
705 }
706
DeleteEntriesForCache(int64_t cache_id)707 bool AppCacheDatabase::DeleteEntriesForCache(int64_t cache_id) {
708 if (!LazyOpen(kDontCreate))
709 return false;
710
711 static const char kSql[] = "DELETE FROM Entries WHERE cache_id = ?";
712
713 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
714 statement.BindInt64(0, cache_id);
715
716 return statement.Run();
717 }
718
AddEntryFlags(const GURL & entry_url,int64_t cache_id,int additional_flags)719 bool AppCacheDatabase::AddEntryFlags(const GURL& entry_url,
720 int64_t cache_id,
721 int additional_flags) {
722 if (!LazyOpen(kDontCreate))
723 return false;
724
725 static const char kSql[] =
726 "UPDATE Entries SET flags = flags | ? WHERE cache_id = ? AND url = ?";
727
728 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
729 statement.BindInt(0, additional_flags);
730 statement.BindInt64(1, cache_id);
731 statement.BindString(2, entry_url.spec());
732
733 return statement.Run() && db_->GetLastChangeCount();
734 }
735
FindNamespacesForOrigin(const url::Origin & origin,std::vector<NamespaceRecord> * intercepts,std::vector<NamespaceRecord> * fallbacks)736 bool AppCacheDatabase::FindNamespacesForOrigin(
737 const url::Origin& origin,
738 std::vector<NamespaceRecord>* intercepts,
739 std::vector<NamespaceRecord>* fallbacks) {
740 DCHECK(intercepts && intercepts->empty());
741 DCHECK(fallbacks && fallbacks->empty());
742 if (!LazyOpen(kDontCreate))
743 return false;
744
745 static const char kSql[] =
746 "SELECT cache_id, origin, type, namespace_url, target_url, token_expires"
747 " FROM Namespaces WHERE origin = ?";
748
749 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
750 statement.BindString(0, SerializeOrigin(origin));
751
752 ReadNamespaceRecords(&statement, intercepts, fallbacks);
753
754 return statement.Succeeded();
755 }
756
FindNamespacesForCache(int64_t cache_id,std::vector<NamespaceRecord> * intercepts,std::vector<NamespaceRecord> * fallbacks)757 bool AppCacheDatabase::FindNamespacesForCache(
758 int64_t cache_id,
759 std::vector<NamespaceRecord>* intercepts,
760 std::vector<NamespaceRecord>* fallbacks) {
761 DCHECK(intercepts && intercepts->empty());
762 DCHECK(fallbacks && fallbacks->empty());
763 if (!LazyOpen(kDontCreate))
764 return false;
765
766 static const char kSql[] =
767 "SELECT cache_id, origin, type, namespace_url, target_url, token_expires"
768 " FROM Namespaces WHERE cache_id = ?";
769
770 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
771 statement.BindInt64(0, cache_id);
772
773 ReadNamespaceRecords(&statement, intercepts, fallbacks);
774
775 return statement.Succeeded();
776 }
777
InsertNamespace(const NamespaceRecord * record)778 bool AppCacheDatabase::InsertNamespace(
779 const NamespaceRecord* record) {
780 if (!LazyOpen(kCreateIfNeeded))
781 return false;
782
783 static const char kSql[] =
784 "INSERT INTO Namespaces"
785 " (cache_id, origin, type, namespace_url, target_url, is_pattern,"
786 " token_expires)"
787 " VALUES (?, ?, ?, ?, ?, ?, ?)";
788
789 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
790 statement.BindInt64(0, record->cache_id);
791 statement.BindString(1, SerializeOrigin(record->origin));
792 statement.BindInt(2, record->namespace_.type);
793 statement.BindString(3, record->namespace_.namespace_url.spec());
794 statement.BindString(4, record->namespace_.target_url.spec());
795 statement.BindBool(5, /*is_pattern=*/false);
796 statement.BindInt64(6, record->token_expires.ToInternalValue());
797 return statement.Run();
798 }
799
InsertNamespaceRecords(const std::vector<NamespaceRecord> & records)800 bool AppCacheDatabase::InsertNamespaceRecords(
801 const std::vector<NamespaceRecord>& records) {
802 if (records.empty())
803 return true;
804 sql::Transaction transaction(db_.get());
805 if (!transaction.Begin())
806 return false;
807 for (const auto& record : records) {
808 if (!InsertNamespace(&record))
809 return false;
810 }
811 return transaction.Commit();
812 }
813
DeleteNamespacesForCache(int64_t cache_id)814 bool AppCacheDatabase::DeleteNamespacesForCache(int64_t cache_id) {
815 if (!LazyOpen(kDontCreate))
816 return false;
817
818 static const char kSql[] = "DELETE FROM Namespaces WHERE cache_id = ?";
819
820 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
821 statement.BindInt64(0, cache_id);
822
823 return statement.Run();
824 }
825
FindOnlineSafeListForCache(int64_t cache_id,std::vector<OnlineSafeListRecord> * records)826 bool AppCacheDatabase::FindOnlineSafeListForCache(
827 int64_t cache_id,
828 std::vector<OnlineSafeListRecord>* records) {
829 DCHECK(records && records->empty());
830 if (!LazyOpen(kDontCreate))
831 return false;
832
833 // TODO(crbug.com/1108479): Update table name.
834 static const char kSql[] =
835 "SELECT cache_id, namespace_url, is_pattern FROM OnlineWhiteLists"
836 " WHERE cache_id = ?";
837
838 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
839 statement.BindInt64(0, cache_id);
840
841 while (statement.Step()) {
842 records->push_back(OnlineSafeListRecord());
843 this->ReadOnlineSafeListRecord(statement, &records->back());
844 DCHECK(records->back().cache_id == cache_id);
845 }
846 return statement.Succeeded();
847 }
848
InsertOnlineSafeList(const OnlineSafeListRecord * record)849 bool AppCacheDatabase::InsertOnlineSafeList(
850 const OnlineSafeListRecord* record) {
851 if (!LazyOpen(kCreateIfNeeded))
852 return false;
853
854 // TODO(crbug.com/1108479): Update table name.
855 static const char kSql[] =
856 "INSERT INTO OnlineWhiteLists (cache_id, namespace_url, is_pattern)"
857 " VALUES (?, ?, ?)";
858
859 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
860 statement.BindInt64(0, record->cache_id);
861 statement.BindString(1, record->namespace_url.spec());
862 statement.BindBool(2, /*is_pattern=*/false);
863
864 return statement.Run();
865 }
866
InsertOnlineSafeListRecords(const std::vector<OnlineSafeListRecord> & records)867 bool AppCacheDatabase::InsertOnlineSafeListRecords(
868 const std::vector<OnlineSafeListRecord>& records) {
869 if (records.empty())
870 return true;
871 sql::Transaction transaction(db_.get());
872 if (!transaction.Begin())
873 return false;
874 for (const auto& record : records) {
875 if (!InsertOnlineSafeList(&record))
876 return false;
877 }
878 return transaction.Commit();
879 }
880
DeleteOnlineSafeListForCache(int64_t cache_id)881 bool AppCacheDatabase::DeleteOnlineSafeListForCache(int64_t cache_id) {
882 if (!LazyOpen(kDontCreate))
883 return false;
884
885 // TODO(crbug.com/1108479): Update table name.
886 static const char kSql[] = "DELETE FROM OnlineWhiteLists WHERE cache_id = ?";
887
888 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
889 statement.BindInt64(0, cache_id);
890
891 return statement.Run();
892 }
893
GetDeletableResponseIds(std::vector<int64_t> * response_ids,int64_t max_rowid,int limit)894 bool AppCacheDatabase::GetDeletableResponseIds(
895 std::vector<int64_t>* response_ids,
896 int64_t max_rowid,
897 int limit) {
898 if (!LazyOpen(kDontCreate))
899 return false;
900
901 static const char kSql[] =
902 "SELECT response_id FROM DeletableResponseIds "
903 " WHERE rowid <= ?"
904 " LIMIT ?";
905
906 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
907 statement.BindInt64(0, max_rowid);
908 statement.BindInt64(1, limit);
909
910 while (statement.Step())
911 response_ids->push_back(statement.ColumnInt64(0));
912 return statement.Succeeded();
913 }
914
InsertDeletableResponseIds(const std::vector<int64_t> & response_ids)915 bool AppCacheDatabase::InsertDeletableResponseIds(
916 const std::vector<int64_t>& response_ids) {
917 static const char kSql[] =
918 "INSERT INTO DeletableResponseIds (response_id) VALUES (?)";
919 return RunCachedStatementWithIds(SQL_FROM_HERE, kSql, response_ids);
920 }
921
DeleteDeletableResponseIds(const std::vector<int64_t> & response_ids)922 bool AppCacheDatabase::DeleteDeletableResponseIds(
923 const std::vector<int64_t>& response_ids) {
924 static const char kSql[] =
925 "DELETE FROM DeletableResponseIds WHERE response_id = ?";
926 return RunCachedStatementWithIds(SQL_FROM_HERE, kSql, response_ids);
927 }
928
HasValidOriginTrialToken(CacheRecord * cache_record)929 bool AppCacheDatabase::HasValidOriginTrialToken(CacheRecord* cache_record) {
930 if (!is_origin_trial_required_)
931 return true;
932 return cache_record->token_expires > base::Time::Now();
933 }
934
RunCachedStatementWithIds(sql::StatementID statement_id,const char * sql,const std::vector<int64_t> & ids)935 bool AppCacheDatabase::RunCachedStatementWithIds(
936 sql::StatementID statement_id,
937 const char* sql,
938 const std::vector<int64_t>& ids) {
939 DCHECK(sql);
940 if (!LazyOpen(kCreateIfNeeded))
941 return false;
942
943 sql::Transaction transaction(db_.get());
944 if (!transaction.Begin())
945 return false;
946
947 sql::Statement statement(db_->GetCachedStatement(statement_id, sql));
948
949 for (const auto& id : ids) {
950 statement.BindInt64(0, id);
951 if (!statement.Run())
952 return false;
953 statement.Reset(true);
954 }
955
956 return transaction.Commit();
957 }
958
RunUniqueStatementWithInt64Result(const char * sql,int64_t * result)959 bool AppCacheDatabase::RunUniqueStatementWithInt64Result(const char* sql,
960 int64_t* result) {
961 DCHECK(sql);
962 sql::Statement statement(db_->GetUniqueStatement(sql));
963 if (!statement.Step()) {
964 return false;
965 }
966 *result = statement.ColumnInt64(0);
967 return true;
968 }
969
FindResponseIdsForCacheHelper(int64_t cache_id,std::vector<int64_t> * ids_vector,std::set<int64_t> * ids_set)970 bool AppCacheDatabase::FindResponseIdsForCacheHelper(
971 int64_t cache_id,
972 std::vector<int64_t>* ids_vector,
973 std::set<int64_t>* ids_set) {
974 DCHECK(ids_vector || ids_set);
975 DCHECK(!(ids_vector && ids_set));
976 if (!LazyOpen(kDontCreate))
977 return false;
978
979 static const char kSql[] =
980 "SELECT response_id FROM Entries WHERE cache_id = ?";
981
982 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
983
984 statement.BindInt64(0, cache_id);
985 while (statement.Step()) {
986 int64_t id = statement.ColumnInt64(0);
987 if (ids_set)
988 ids_set->insert(id);
989 else
990 ids_vector->push_back(id);
991 }
992
993 return statement.Succeeded();
994 }
995
ReadGroupRecord(const sql::Statement & statement,GroupRecord * record)996 void AppCacheDatabase::ReadGroupRecord(
997 const sql::Statement& statement, GroupRecord* record) {
998 record->group_id = statement.ColumnInt64(0);
999 record->origin = url::Origin::Create(GURL(statement.ColumnString(1)));
1000 record->manifest_url = GURL(statement.ColumnString(2));
1001 record->creation_time =
1002 base::Time::FromInternalValue(statement.ColumnInt64(3));
1003
1004 const auto found = lazy_last_access_times_.find(record->group_id);
1005 if (found != lazy_last_access_times_.end()) {
1006 record->last_access_time = found->second;
1007 } else {
1008 record->last_access_time =
1009 base::Time::FromInternalValue(statement.ColumnInt64(4));
1010 }
1011
1012 record->last_full_update_check_time =
1013 base::Time::FromInternalValue(statement.ColumnInt64(5));
1014 record->first_evictable_error_time =
1015 base::Time::FromInternalValue(statement.ColumnInt64(6));
1016 record->token_expires =
1017 base::Time::FromInternalValue(statement.ColumnInt64(7));
1018 }
1019
ReadCacheRecord(const sql::Statement & statement,CacheRecord * record)1020 void AppCacheDatabase::ReadCacheRecord(
1021 const sql::Statement& statement, CacheRecord* record) {
1022 record->cache_id = statement.ColumnInt64(0);
1023 record->group_id = statement.ColumnInt64(1);
1024 record->online_wildcard = statement.ColumnBool(2);
1025 record->update_time =
1026 base::Time::FromInternalValue(statement.ColumnInt64(3));
1027 record->cache_size = statement.ColumnInt64(4);
1028 record->padding_size = statement.ColumnInt64(5);
1029 record->manifest_parser_version = statement.ColumnInt64(6);
1030 record->manifest_scope = statement.ColumnString(7);
1031 record->token_expires =
1032 base::Time::FromInternalValue(statement.ColumnInt64(8));
1033 }
1034
ReadEntryRecord(const sql::Statement & statement,EntryRecord * record)1035 void AppCacheDatabase::ReadEntryRecord(
1036 const sql::Statement& statement, EntryRecord* record) {
1037 record->cache_id = statement.ColumnInt64(0);
1038 record->url = GURL(statement.ColumnString(1));
1039 record->flags = statement.ColumnInt(2);
1040 record->response_id = statement.ColumnInt64(3);
1041 record->response_size = statement.ColumnInt64(4);
1042 record->padding_size = statement.ColumnInt64(5);
1043 record->token_expires =
1044 base::Time::FromInternalValue(statement.ColumnInt64(6));
1045 }
1046
ReadNamespaceRecords(sql::Statement * statement,NamespaceRecordVector * intercepts,NamespaceRecordVector * fallbacks)1047 void AppCacheDatabase::ReadNamespaceRecords(
1048 sql::Statement* statement,
1049 NamespaceRecordVector* intercepts,
1050 NamespaceRecordVector* fallbacks) {
1051 while (statement->Step()) {
1052 AppCacheNamespaceType type = static_cast<AppCacheNamespaceType>(
1053 statement->ColumnInt(2));
1054 NamespaceRecordVector* records =
1055 (type == APPCACHE_FALLBACK_NAMESPACE) ? fallbacks : intercepts;
1056 records->push_back(NamespaceRecord());
1057 ReadNamespaceRecord(statement, &records->back());
1058 }
1059 }
1060
ReadNamespaceRecord(const sql::Statement * statement,NamespaceRecord * record)1061 void AppCacheDatabase::ReadNamespaceRecord(
1062 const sql::Statement* statement, NamespaceRecord* record) {
1063 record->cache_id = statement->ColumnInt64(0);
1064 record->origin = url::Origin::Create(GURL(statement->ColumnString(1)));
1065 record->namespace_.type =
1066 static_cast<AppCacheNamespaceType>(statement->ColumnInt(2));
1067 record->namespace_.namespace_url = GURL(statement->ColumnString(3));
1068 record->namespace_.target_url = GURL(statement->ColumnString(4));
1069 DCHECK(record->namespace_.type == APPCACHE_FALLBACK_NAMESPACE ||
1070 record->namespace_.type == APPCACHE_INTERCEPT_NAMESPACE);
1071 // The APPCACHE_NETWORK_NAMESPACE are stored as OnlineSafeListRecords.
1072 record->token_expires =
1073 base::Time::FromInternalValue(statement->ColumnInt64(5));
1074 }
1075
ReadOnlineSafeListRecord(const sql::Statement & statement,OnlineSafeListRecord * record)1076 void AppCacheDatabase::ReadOnlineSafeListRecord(const sql::Statement& statement,
1077 OnlineSafeListRecord* record) {
1078 record->cache_id = statement.ColumnInt64(0);
1079 record->namespace_url = GURL(statement.ColumnString(1));
1080 }
1081
LazyOpen(bool create_if_needed)1082 bool AppCacheDatabase::LazyOpen(bool create_if_needed) {
1083 if (db_)
1084 return true;
1085
1086 // If we tried and failed once, don't try again in the same session
1087 // to avoid creating an incoherent mess on disk.
1088 if (is_disabled_)
1089 return false;
1090
1091 // Avoid creating a database at all if we can.
1092 bool use_in_memory_db = db_file_path_.empty();
1093 if (!create_if_needed &&
1094 (use_in_memory_db || !base::PathExists(db_file_path_))) {
1095 return false;
1096 }
1097
1098 db_ = std::make_unique<sql::Database>();
1099 meta_table_ = std::make_unique<sql::MetaTable>();
1100
1101 db_->set_histogram_tag("AppCache");
1102
1103 bool opened = false;
1104 if (use_in_memory_db) {
1105 opened = db_->OpenInMemory();
1106 } else if (!base::CreateDirectory(db_file_path_.DirName())) {
1107 LOG(ERROR) << "Failed to create appcache directory.";
1108 } else {
1109 opened = db_->Open(db_file_path_);
1110 if (opened)
1111 db_->Preload();
1112 }
1113
1114 if (!opened || !db_->QuickIntegrityCheck() || !EnsureDatabaseVersion()) {
1115 // We're unable to open the database. This is a fatal error
1116 // which we can't recover from. We try to handle it by deleting
1117 // the existing appcache data and starting with a clean slate in
1118 // this browser session.
1119 if (!use_in_memory_db && DeleteExistingAndCreateNewDatabase())
1120 return true;
1121
1122 Disable();
1123 return false;
1124 }
1125
1126 was_corruption_detected_ = false;
1127 db_->set_error_callback(base::BindRepeating(
1128 &AppCacheDatabase::OnDatabaseError, base::Unretained(this)));
1129 return true;
1130 }
1131
EnsureDatabaseVersion()1132 bool AppCacheDatabase::EnsureDatabaseVersion() {
1133 if (!sql::MetaTable::DoesTableExist(db_.get()))
1134 return CreateSchema();
1135
1136 if (!meta_table_->Init(db_.get(), kCurrentVersion, kCompatibleVersion))
1137 return false;
1138
1139 if (meta_table_->GetCompatibleVersionNumber() > kCurrentVersion) {
1140 LOG(WARNING) << "AppCache database is too new.";
1141 return false;
1142 }
1143
1144 std::string stored_flags;
1145 meta_table_->GetValue(kExperimentFlagsKey, &stored_flags);
1146 if (stored_flags != GetActiveExperimentFlags())
1147 return false;
1148
1149 if (meta_table_->GetVersionNumber() < kCurrentVersion)
1150 return UpgradeSchema();
1151
1152 #ifndef NDEBUG
1153 DCHECK(sql::MetaTable::DoesTableExist(db_.get()));
1154 for (const TableInfo& table : kTables)
1155 DCHECK(db_->DoesTableExist(table.table_name));
1156 for (const IndexInfo& index : kIndexes)
1157 DCHECK(db_->DoesIndexExist(index.index_name));
1158 #endif
1159
1160 return true;
1161 }
1162
CreateSchema()1163 bool AppCacheDatabase::CreateSchema() {
1164 sql::Transaction transaction(db_.get());
1165 if (!transaction.Begin())
1166 return false;
1167
1168 if (!meta_table_->Init(db_.get(), kCurrentVersion, kCompatibleVersion))
1169 return false;
1170
1171 if (!meta_table_->SetValue(kExperimentFlagsKey,
1172 GetActiveExperimentFlags())) {
1173 return false;
1174 }
1175
1176 for (const TableInfo& table : kTables) {
1177 if (!CreateTable(db_.get(), table))
1178 return false;
1179 }
1180
1181 for (const IndexInfo& index : kIndexes) {
1182 if (!CreateIndex(db_.get(), index))
1183 return false;
1184 }
1185
1186 return transaction.Commit();
1187 }
1188
UpgradeSchema()1189 bool AppCacheDatabase::UpgradeSchema() {
1190 // Start from scratch for versions that would require unsupported migrations.
1191 if (meta_table_->GetVersionNumber() < 7)
1192 return DeleteExistingAndCreateNewDatabase();
1193
1194 // Version 8 adds padding_size.
1195 if (meta_table_->GetVersionNumber() < 8) {
1196 sql::Transaction transaction(db_.get());
1197 if (!transaction.Begin())
1198 return false;
1199 if (!db_->Execute("ALTER TABLE Caches ADD COLUMN padding_size INTEGER"))
1200 return false;
1201 if (!db_->Execute("ALTER TABLE Entries ADD COLUMN padding_size INTEGER"))
1202 return false;
1203 meta_table_->SetVersionNumber(8);
1204 meta_table_->SetCompatibleVersionNumber(8);
1205 if (!AppCacheBackfillerVersion8(db_.get()).BackfillPaddingSizes())
1206 return false;
1207 if (!transaction.Commit())
1208 return false;
1209 }
1210
1211 // Version 9 adds manifest_parser_version and manifest_scope.
1212 if (meta_table_->GetVersionNumber() < 9) {
1213 sql::Transaction transaction(db_.get());
1214 if (!transaction.Begin())
1215 return false;
1216 if (!db_->Execute(
1217 "ALTER TABLE Caches ADD COLUMN manifest_parser_version INTEGER"))
1218 return false;
1219 if (!db_->Execute("ALTER TABLE Caches ADD COLUMN manifest_scope TEXT"))
1220 return false;
1221 meta_table_->SetVersionNumber(9);
1222 meta_table_->SetCompatibleVersionNumber(9);
1223 if (!AppCacheBackfillerVersion9(db_.get())
1224 .BackfillManifestParserVersionAndScope()) {
1225 return false;
1226 }
1227 if (!transaction.Commit())
1228 return false;
1229 }
1230
1231 if (meta_table_->GetVersionNumber() < 10) {
1232 sql::Transaction transaction(db_.get());
1233 if (!transaction.Begin())
1234 return false;
1235
1236 if (!db_->Execute("ALTER TABLE Groups ADD COLUMN token_expires INTEGER"))
1237 return false;
1238 if (!db_->Execute("ALTER TABLE Caches ADD COLUMN token_expires INTEGER"))
1239 return false;
1240 if (!db_->Execute("ALTER TABLE Entries ADD COLUMN token_expires INTEGER"))
1241 return false;
1242 if (!db_->Execute(
1243 "ALTER TABLE Namespaces ADD COLUMN token_expires INTEGER"))
1244 return false;
1245 meta_table_->SetVersionNumber(10);
1246 meta_table_->SetCompatibleVersionNumber(10);
1247 // No backfilling needed as all of these values default to zero,
1248 // which is desired.
1249 if (!transaction.Commit())
1250 return false;
1251 }
1252
1253 return true;
1254 }
1255
ResetConnectionAndTables()1256 void AppCacheDatabase::ResetConnectionAndTables() {
1257 meta_table_.reset();
1258 db_.reset();
1259 }
1260
DeleteExistingAndCreateNewDatabase()1261 bool AppCacheDatabase::DeleteExistingAndCreateNewDatabase() {
1262 DCHECK(!db_file_path_.empty());
1263 DCHECK(base::PathExists(db_file_path_));
1264 VLOG(1) << "Deleting existing appcache data and starting over.";
1265
1266 ResetConnectionAndTables();
1267
1268 // This also deletes the disk cache data.
1269 base::FilePath directory = db_file_path_.DirName();
1270 if (!base::DeletePathRecursively(directory))
1271 return false;
1272
1273 // Make sure the steps above actually deleted things.
1274 if (base::PathExists(directory))
1275 return false;
1276
1277 if (!base::CreateDirectory(directory))
1278 return false;
1279
1280 // So we can't go recursive.
1281 if (is_recreating_)
1282 return false;
1283
1284 base::AutoReset<bool> auto_reset(&is_recreating_, true);
1285 return LazyOpen(kCreateIfNeeded);
1286 }
1287
OnDatabaseError(int err,sql::Statement * stmt)1288 void AppCacheDatabase::OnDatabaseError(int err, sql::Statement* stmt) {
1289 was_corruption_detected_ |= sql::IsErrorCatastrophic(err);
1290 if (!db_->IsExpectedSqliteError(err))
1291 DLOG(ERROR) << db_->GetErrorMessage();
1292 // TODO: Maybe use non-catostrophic errors to trigger a full integrity check?
1293 }
1294
1295 } // namespace content
1296