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