1 // Copyright 2017 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/indexed_db/indexed_db_leveldb_operations.h"
6 
7 #include "base/json/json_reader.h"
8 #include "base/metrics/histogram_functions.h"
9 #include "base/no_destructor.h"
10 #include "base/values.h"
11 #include "components/services/storage/indexed_db/scopes/leveldb_scopes.h"
12 #include "components/services/storage/indexed_db/scopes/varint_coding.h"
13 #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_database.h"
14 #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_iterator.h"
15 #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_transaction.h"
16 #include "content/browser/indexed_db/indexed_db_data_format_version.h"
17 #include "content/browser/indexed_db/indexed_db_data_loss_info.h"
18 #include "content/browser/indexed_db/indexed_db_leveldb_env.h"
19 #include "content/browser/indexed_db/indexed_db_reporting.h"
20 #include "content/browser/indexed_db/indexed_db_tracing.h"
21 #include "storage/common/database/database_identifier.h"
22 #include "third_party/leveldatabase/env_chromium.h"
23 
24 using base::StringPiece;
25 using blink::IndexedDBKeyPath;
26 using leveldb::Status;
27 
28 namespace content {
29 namespace indexed_db {
30 namespace {
31 class LDBComparator : public leveldb::Comparator {
32  public:
33   LDBComparator() = default;
34   ~LDBComparator() override = default;
Compare(const leveldb::Slice & a,const leveldb::Slice & b) const35   int Compare(const leveldb::Slice& a, const leveldb::Slice& b) const override {
36     return content::Compare(leveldb_env::MakeStringPiece(a),
37                             leveldb_env::MakeStringPiece(b),
38                             false /*index_keys*/);
39   }
Name() const40   const char* Name() const override { return "idb_cmp1"; }
FindShortestSeparator(std::string * start,const leveldb::Slice & limit) const41   void FindShortestSeparator(std::string* start,
42                              const leveldb::Slice& limit) const override {}
FindShortSuccessor(std::string * key) const43   void FindShortSuccessor(std::string* key) const override {}
44 };
45 }  // namespace
46 
47 const base::FilePath::CharType kBlobExtension[] = FILE_PATH_LITERAL(".blob");
48 const base::FilePath::CharType kIndexedDBExtension[] =
49     FILE_PATH_LITERAL(".indexeddb");
50 const base::FilePath::CharType kLevelDBExtension[] =
51     FILE_PATH_LITERAL(".leveldb");
52 
53 // static
GetBlobStoreFileName(const url::Origin & origin)54 base::FilePath GetBlobStoreFileName(const url::Origin& origin) {
55   std::string origin_id = storage::GetIdentifierFromOrigin(origin);
56   return base::FilePath()
57       .AppendASCII(origin_id)
58       .AddExtension(kIndexedDBExtension)
59       .AddExtension(kBlobExtension);
60 }
61 
62 // static
GetLevelDBFileName(const url::Origin & origin)63 base::FilePath GetLevelDBFileName(const url::Origin& origin) {
64   std::string origin_id = storage::GetIdentifierFromOrigin(origin);
65   return base::FilePath()
66       .AppendASCII(origin_id)
67       .AddExtension(kIndexedDBExtension)
68       .AddExtension(kLevelDBExtension);
69 }
70 
ComputeCorruptionFileName(const url::Origin & origin)71 base::FilePath ComputeCorruptionFileName(const url::Origin& origin) {
72   return GetLevelDBFileName(origin).Append(
73       FILE_PATH_LITERAL("corruption_info.json"));
74 }
75 
IsPathTooLong(storage::FilesystemProxy * filesystem,const base::FilePath & leveldb_dir)76 bool IsPathTooLong(storage::FilesystemProxy* filesystem,
77                    const base::FilePath& leveldb_dir) {
78   base::Optional<int> limit =
79       filesystem->GetMaximumPathComponentLength(leveldb_dir.DirName());
80   if (!limit.has_value()) {
81     DLOG(WARNING) << "GetMaximumPathComponentLength returned -1";
82 // In limited testing, ChromeOS returns 143, other OSes 255.
83 #if defined(OS_CHROMEOS)
84     limit = 143;
85 #else
86     limit = 255;
87 #endif
88   }
89   size_t component_length = leveldb_dir.BaseName().value().length();
90   if (component_length > static_cast<uint32_t>(*limit)) {
91     DLOG(WARNING) << "Path component length (" << component_length
92                   << ") exceeds maximum (" << *limit
93                   << ") allowed by this filesystem.";
94     const int min = 140;
95     const int max = 300;
96     const int num_buckets = 12;
97     base::UmaHistogramCustomCounts(
98         "WebCore.IndexedDB.BackingStore.OverlyLargeOriginLength",
99         component_length, min, max, num_buckets);
100     return true;
101   }
102   return false;
103 }
104 
ReadCorruptionInfo(storage::FilesystemProxy * filesystem_proxy,const base::FilePath & path_base,const url::Origin & origin)105 std::string ReadCorruptionInfo(storage::FilesystemProxy* filesystem_proxy,
106                                const base::FilePath& path_base,
107                                const url::Origin& origin) {
108   const base::FilePath info_path =
109       path_base.Append(indexed_db::ComputeCorruptionFileName(origin));
110   std::string message;
111   if (IsPathTooLong(filesystem_proxy, info_path))
112     return message;
113 
114   const int64_t kMaxJsonLength = 4096;
115 
116   base::Optional<base::File::Info> file_info =
117       filesystem_proxy->GetFileInfo(info_path);
118   if (!file_info.has_value())
119     return message;
120   if (!file_info->size || file_info->size > kMaxJsonLength) {
121     filesystem_proxy->DeleteFile(info_path);
122     return message;
123   }
124 
125   storage::FileErrorOr<base::File> file_or_error = filesystem_proxy->OpenFile(
126       info_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
127   if (!file_or_error.is_error()) {
128     auto& file = file_or_error.value();
129     if (file.IsValid()) {
130       std::string input_js(file_info->size, '\0');
131       if (file_info->size ==
132           file.Read(0, base::data(input_js), file_info->size)) {
133         base::Optional<base::Value> val = base::JSONReader::Read(input_js);
134         if (val && val->is_dict()) {
135           std::string* s = val->FindStringKey("message");
136           if (s)
137             message = *s;
138         }
139       }
140       file.Close();
141     }
142   }
143 
144   filesystem_proxy->DeleteFile(info_path);
145 
146   return message;
147 }
148 
InternalInconsistencyStatus()149 leveldb::Status InternalInconsistencyStatus() {
150   return leveldb::Status::Corruption("Internal inconsistency");
151 }
152 
InvalidDBKeyStatus()153 leveldb::Status InvalidDBKeyStatus() {
154   return leveldb::Status::InvalidArgument("Invalid database key ID");
155 }
156 
IOErrorStatus()157 leveldb::Status IOErrorStatus() {
158   return leveldb::Status::IOError("IO Error");
159 }
160 
PutBool(TransactionalLevelDBTransaction * transaction,const StringPiece & key,bool value)161 leveldb::Status PutBool(TransactionalLevelDBTransaction* transaction,
162                         const StringPiece& key,
163                         bool value) {
164   std::string buffer;
165   EncodeBool(value, &buffer);
166   return transaction->Put(key, &buffer);
167 }
168 
169 template <typename DBOrTransaction>
GetVarInt(DBOrTransaction * db,const StringPiece & key,int64_t * found_int,bool * found)170 Status GetVarInt(DBOrTransaction* db,
171                  const StringPiece& key,
172                  int64_t* found_int,
173                  bool* found) {
174   std::string result;
175   Status s = db->Get(key, &result, found);
176   if (!s.ok())
177     return s;
178   if (!*found)
179     return Status::OK();
180   StringPiece slice(result);
181   if (DecodeVarInt(&slice, found_int) && slice.empty())
182     return s;
183   return InternalInconsistencyStatus();
184 }
185 template Status GetVarInt<TransactionalLevelDBTransaction>(
186     TransactionalLevelDBTransaction* txn,
187     const StringPiece& key,
188     int64_t* found_int,
189     bool* found);
190 template Status GetVarInt<TransactionalLevelDBDatabase>(
191     TransactionalLevelDBDatabase* db,
192     const StringPiece& key,
193     int64_t* found_int,
194     bool* found);
195 
196 template <typename TransactionOrWriteBatch>
PutVarInt(TransactionOrWriteBatch * transaction_or_write_batch,const StringPiece & key,int64_t value)197 leveldb::Status PutVarInt(TransactionOrWriteBatch* transaction_or_write_batch,
198                           const StringPiece& key,
199                           int64_t value) {
200   std::string buffer;
201   EncodeVarInt(value, &buffer);
202   return PutValue(transaction_or_write_batch, key, &buffer);
203 }
204 template leveldb::Status PutVarInt<TransactionalLevelDBTransaction>(
205     TransactionalLevelDBTransaction* transaction,
206     const StringPiece& key,
207     int64_t value);
208 template leveldb::Status PutVarInt<LevelDBDirectTransaction>(
209     LevelDBDirectTransaction* transaction,
210     const StringPiece& key,
211     int64_t value);
212 template leveldb::Status PutVarInt<LevelDBWriteBatch>(
213     LevelDBWriteBatch* transaction,
214     const StringPiece& key,
215     int64_t value);
216 
217 template <typename DBOrTransaction>
GetString(DBOrTransaction * db,const StringPiece & key,base::string16 * found_string,bool * found)218 Status GetString(DBOrTransaction* db,
219                  const StringPiece& key,
220                  base::string16* found_string,
221                  bool* found) {
222   std::string result;
223   *found = false;
224   Status s = db->Get(key, &result, found);
225   if (!s.ok())
226     return s;
227   if (!*found)
228     return Status::OK();
229   StringPiece slice(result);
230   if (DecodeString(&slice, found_string) && slice.empty())
231     return s;
232   return InternalInconsistencyStatus();
233 }
234 
235 template Status GetString<TransactionalLevelDBTransaction>(
236     TransactionalLevelDBTransaction* txn,
237     const StringPiece& key,
238     base::string16* found_string,
239     bool* found);
240 template Status GetString<TransactionalLevelDBDatabase>(
241     TransactionalLevelDBDatabase* db,
242     const StringPiece& key,
243     base::string16* found_string,
244     bool* found);
245 
PutString(TransactionalLevelDBTransaction * transaction,const StringPiece & key,const base::string16 & value)246 leveldb::Status PutString(TransactionalLevelDBTransaction* transaction,
247                           const StringPiece& key,
248                           const base::string16& value) {
249   std::string buffer;
250   EncodeString(value, &buffer);
251   return transaction->Put(key, &buffer);
252 }
253 
PutIDBKeyPath(TransactionalLevelDBTransaction * transaction,const StringPiece & key,const IndexedDBKeyPath & value)254 leveldb::Status PutIDBKeyPath(TransactionalLevelDBTransaction* transaction,
255                               const StringPiece& key,
256                               const IndexedDBKeyPath& value) {
257   std::string buffer;
258   EncodeIDBKeyPath(value, &buffer);
259   return transaction->Put(key, &buffer);
260 }
261 
262 template <typename DBOrTransaction>
GetMaxObjectStoreId(DBOrTransaction * db,int64_t database_id,int64_t * max_object_store_id)263 Status GetMaxObjectStoreId(DBOrTransaction* db,
264                            int64_t database_id,
265                            int64_t* max_object_store_id) {
266   const std::string max_object_store_id_key = DatabaseMetaDataKey::Encode(
267       database_id, DatabaseMetaDataKey::MAX_OBJECT_STORE_ID);
268   *max_object_store_id = -1;
269   bool found = false;
270   Status s = indexed_db::GetInt(db, max_object_store_id_key,
271                                 max_object_store_id, &found);
272   if (!s.ok())
273     return s;
274   if (!found)
275     *max_object_store_id = 0;
276 
277   DCHECK_GE(*max_object_store_id, 0);
278   return s;
279 }
280 
281 template Status GetMaxObjectStoreId<TransactionalLevelDBTransaction>(
282     TransactionalLevelDBTransaction* db,
283     int64_t database_id,
284     int64_t* max_object_store_id);
285 template Status GetMaxObjectStoreId<TransactionalLevelDBDatabase>(
286     TransactionalLevelDBDatabase* db,
287     int64_t database_id,
288     int64_t* max_object_store_id);
289 
SetMaxObjectStoreId(TransactionalLevelDBTransaction * transaction,int64_t database_id,int64_t object_store_id)290 Status SetMaxObjectStoreId(TransactionalLevelDBTransaction* transaction,
291                            int64_t database_id,
292                            int64_t object_store_id) {
293   const std::string max_object_store_id_key = DatabaseMetaDataKey::Encode(
294       database_id, DatabaseMetaDataKey::MAX_OBJECT_STORE_ID);
295   int64_t max_object_store_id = -1;
296   bool found = false;
297   Status s = GetInt(transaction, max_object_store_id_key, &max_object_store_id,
298                     &found);
299   if (!s.ok())
300     return s;
301   if (!found)
302     max_object_store_id = 0;
303 
304   DCHECK_GE(max_object_store_id, 0);
305   if (!s.ok()) {
306     INTERNAL_READ_ERROR(SET_MAX_OBJECT_STORE_ID);
307     return s;
308   }
309 
310   if (object_store_id <= max_object_store_id) {
311     INTERNAL_CONSISTENCY_ERROR(SET_MAX_OBJECT_STORE_ID);
312     return indexed_db::InternalInconsistencyStatus();
313   }
314   return indexed_db::PutInt(transaction, max_object_store_id_key,
315                             object_store_id);
316 }
317 
GetNewVersionNumber(TransactionalLevelDBTransaction * transaction,int64_t database_id,int64_t object_store_id,int64_t * new_version_number)318 Status GetNewVersionNumber(TransactionalLevelDBTransaction* transaction,
319                            int64_t database_id,
320                            int64_t object_store_id,
321                            int64_t* new_version_number) {
322   const std::string last_version_key = ObjectStoreMetaDataKey::Encode(
323       database_id, object_store_id, ObjectStoreMetaDataKey::LAST_VERSION);
324 
325   *new_version_number = -1;
326   int64_t last_version = -1;
327   bool found = false;
328   Status s = GetInt(transaction, last_version_key, &last_version, &found);
329   if (!s.ok()) {
330     INTERNAL_READ_ERROR(GET_NEW_VERSION_NUMBER);
331     return s;
332   }
333   if (!found)
334     last_version = 0;
335 
336   DCHECK_GE(last_version, 0);
337 
338   int64_t version = last_version + 1;
339   s = PutInt(transaction, last_version_key, version);
340   if (!s.ok()) {
341     INTERNAL_READ_ERROR(GET_NEW_VERSION_NUMBER);
342     return s;
343   }
344 
345   // TODO(jsbell): Think about how we want to handle the overflow scenario.
346   DCHECK(version > last_version);
347 
348   *new_version_number = version;
349   return s;
350 }
351 
SetMaxIndexId(TransactionalLevelDBTransaction * transaction,int64_t database_id,int64_t object_store_id,int64_t index_id)352 Status SetMaxIndexId(TransactionalLevelDBTransaction* transaction,
353                      int64_t database_id,
354                      int64_t object_store_id,
355                      int64_t index_id) {
356   int64_t max_index_id = -1;
357   const std::string max_index_id_key = ObjectStoreMetaDataKey::Encode(
358       database_id, object_store_id, ObjectStoreMetaDataKey::MAX_INDEX_ID);
359   bool found = false;
360   Status s = GetInt(transaction, max_index_id_key, &max_index_id, &found);
361   if (!s.ok()) {
362     INTERNAL_READ_ERROR(SET_MAX_INDEX_ID);
363     return s;
364   }
365   if (!found)
366     max_index_id = kMinimumIndexId;
367 
368   if (index_id <= max_index_id) {
369     INTERNAL_CONSISTENCY_ERROR(SET_MAX_INDEX_ID);
370     return InternalInconsistencyStatus();
371   }
372 
373   return PutInt(transaction, max_index_id_key, index_id);
374 }
375 
VersionExists(TransactionalLevelDBTransaction * transaction,int64_t database_id,int64_t object_store_id,int64_t version,const std::string & encoded_primary_key,bool * exists)376 Status VersionExists(TransactionalLevelDBTransaction* transaction,
377                      int64_t database_id,
378                      int64_t object_store_id,
379                      int64_t version,
380                      const std::string& encoded_primary_key,
381                      bool* exists) {
382   const std::string key =
383       ExistsEntryKey::Encode(database_id, object_store_id, encoded_primary_key);
384   std::string data;
385 
386   Status s = transaction->Get(key, &data, exists);
387   if (!s.ok()) {
388     INTERNAL_READ_ERROR(VERSION_EXISTS);
389     return s;
390   }
391   if (!*exists)
392     return s;
393 
394   StringPiece slice(data);
395   int64_t decoded;
396   if (!DecodeInt(&slice, &decoded) || !slice.empty())
397     return InternalInconsistencyStatus();
398   *exists = (decoded == version);
399   return s;
400 }
401 
402 template Status GetNewDatabaseId<LevelDBDirectTransaction>(
403     LevelDBDirectTransaction* transaction,
404     int64_t* new_id);
405 
406 template Status GetNewDatabaseId<TransactionalLevelDBTransaction>(
407     TransactionalLevelDBTransaction* transaction,
408     int64_t* new_id);
409 
410 template <typename Transaction>
GetNewDatabaseId(Transaction * transaction,int64_t * new_id)411 Status GetNewDatabaseId(Transaction* transaction, int64_t* new_id) {
412   *new_id = -1;
413   int64_t max_database_id = -1;
414   bool found = false;
415   Status s = indexed_db::GetInt(transaction, MaxDatabaseIdKey::Encode(),
416                                 &max_database_id, &found);
417   if (!s.ok()) {
418     INTERNAL_READ_ERROR(GET_NEW_DATABASE_ID);
419     return s;
420   }
421   if (!found)
422     max_database_id = 0;
423 
424   DCHECK_GE(max_database_id, 0);
425 
426   int64_t database_id = max_database_id + 1;
427   s = indexed_db::PutInt(transaction, MaxDatabaseIdKey::Encode(), database_id);
428   if (!s.ok()) {
429     INTERNAL_READ_ERROR(GET_NEW_DATABASE_ID);
430     return s;
431   }
432   *new_id = database_id;
433   return Status::OK();
434 }
435 
CheckObjectStoreAndMetaDataType(const TransactionalLevelDBIterator * it,const std::string & stop_key,int64_t object_store_id,int64_t meta_data_type)436 bool CheckObjectStoreAndMetaDataType(const TransactionalLevelDBIterator* it,
437                                      const std::string& stop_key,
438                                      int64_t object_store_id,
439                                      int64_t meta_data_type) {
440   if (!it->IsValid() || CompareKeys(it->Key(), stop_key) >= 0)
441     return false;
442 
443   StringPiece slice(it->Key());
444   ObjectStoreMetaDataKey meta_data_key;
445   bool ok =
446       ObjectStoreMetaDataKey::Decode(&slice, &meta_data_key) && slice.empty();
447   DCHECK(ok);
448   if (meta_data_key.ObjectStoreId() != object_store_id)
449     return false;
450   if (meta_data_key.MetaDataType() != meta_data_type)
451     return false;
452   return ok;
453 }
454 
CheckIndexAndMetaDataKey(const TransactionalLevelDBIterator * it,const std::string & stop_key,int64_t index_id,unsigned char meta_data_type)455 bool CheckIndexAndMetaDataKey(const TransactionalLevelDBIterator* it,
456                               const std::string& stop_key,
457                               int64_t index_id,
458                               unsigned char meta_data_type) {
459   if (!it->IsValid() || CompareKeys(it->Key(), stop_key) >= 0)
460     return false;
461 
462   StringPiece slice(it->Key());
463   IndexMetaDataKey meta_data_key;
464   bool ok = IndexMetaDataKey::Decode(&slice, &meta_data_key);
465   DCHECK(ok);
466   if (meta_data_key.IndexId() != index_id)
467     return false;
468   if (meta_data_key.meta_data_type() != meta_data_type)
469     return false;
470   return true;
471 }
472 
FindGreatestKeyLessThanOrEqual(TransactionalLevelDBTransaction * transaction,const std::string & target,std::string * found_key,Status * s)473 bool FindGreatestKeyLessThanOrEqual(
474     TransactionalLevelDBTransaction* transaction,
475     const std::string& target,
476     std::string* found_key,
477     Status* s) {
478   std::unique_ptr<TransactionalLevelDBIterator> it =
479       transaction->CreateIterator(*s);
480   if (!s->ok()) {
481     INTERNAL_WRITE_ERROR(CREATE_ITERATOR);
482     return false;
483   }
484 
485   *s = it->Seek(target);
486   if (!s->ok())
487     return false;
488 
489   if (!it->IsValid()) {
490     *s = it->SeekToLast();
491     if (!s->ok() || !it->IsValid())
492       return false;
493   }
494 
495   while (CompareIndexKeys(it->Key(), target) > 0) {
496     *s = it->Prev();
497     if (!s->ok() || !it->IsValid())
498       return false;
499   }
500 
501   do {
502     *found_key = it->Key().as_string();
503 
504     // There can be several index keys that compare equal. We want the last one.
505     *s = it->Next();
506   } while (s->ok() && it->IsValid() && !CompareIndexKeys(it->Key(), target));
507 
508   return true;
509 }
510 
GetBlobNumberGeneratorCurrentNumber(LevelDBDirectTransaction * leveldb_transaction,int64_t database_id,int64_t * blob_number_generator_current_number)511 bool GetBlobNumberGeneratorCurrentNumber(
512     LevelDBDirectTransaction* leveldb_transaction,
513     int64_t database_id,
514     int64_t* blob_number_generator_current_number) {
515   const std::string key_gen_key = DatabaseMetaDataKey::Encode(
516       database_id, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER);
517 
518   // Default to initial number if not found.
519   int64_t cur_number = DatabaseMetaDataKey::kBlobNumberGeneratorInitialNumber;
520   std::string data;
521 
522   bool found = false;
523   bool ok = leveldb_transaction->Get(key_gen_key, &data, &found).ok();
524   if (!ok) {
525     INTERNAL_READ_ERROR(GET_BLOB_KEY_GENERATOR_CURRENT_NUMBER);
526     return false;
527   }
528   if (found) {
529     StringPiece slice(data);
530     if (!DecodeVarInt(&slice, &cur_number) || !slice.empty() ||
531         !DatabaseMetaDataKey::IsValidBlobNumber(cur_number)) {
532       INTERNAL_READ_ERROR(GET_BLOB_KEY_GENERATOR_CURRENT_NUMBER);
533       return false;
534     }
535   }
536   *blob_number_generator_current_number = cur_number;
537   return true;
538 }
539 
UpdateBlobNumberGeneratorCurrentNumber(LevelDBDirectTransaction * leveldb_transaction,int64_t database_id,int64_t blob_number_generator_current_number)540 bool UpdateBlobNumberGeneratorCurrentNumber(
541     LevelDBDirectTransaction* leveldb_transaction,
542     int64_t database_id,
543     int64_t blob_number_generator_current_number) {
544 #if DCHECK_IS_ON()
545   int64_t old_number;
546   if (!GetBlobNumberGeneratorCurrentNumber(leveldb_transaction, database_id,
547                                            &old_number))
548     return false;
549   DCHECK_LT(old_number, blob_number_generator_current_number);
550 #endif
551   DCHECK(DatabaseMetaDataKey::IsValidBlobNumber(
552       blob_number_generator_current_number));
553   const std::string key = DatabaseMetaDataKey::Encode(
554       database_id, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER);
555 
556   leveldb::Status s =
557       PutVarInt(leveldb_transaction, key, blob_number_generator_current_number);
558   return s.ok();
559 }
560 
GetEarliestSweepTime(TransactionalLevelDBDatabase * db,base::Time * earliest_sweep)561 Status GetEarliestSweepTime(TransactionalLevelDBDatabase* db,
562                             base::Time* earliest_sweep) {
563   const std::string earliest_sweep_time_key = EarliestSweepKey::Encode();
564   *earliest_sweep = base::Time();
565   bool found = false;
566   int64_t time_micros = 0;
567   Status s =
568       indexed_db::GetInt(db, earliest_sweep_time_key, &time_micros, &found);
569   if (!s.ok())
570     return s;
571   if (!found)
572     time_micros = 0;
573 
574   DCHECK_GE(time_micros, 0);
575   *earliest_sweep += base::TimeDelta::FromMicroseconds(time_micros);
576 
577   return s;
578 }
579 
580 template leveldb::Status SetEarliestSweepTime<TransactionalLevelDBTransaction>(
581     TransactionalLevelDBTransaction* db,
582     base::Time earliest_sweep);
583 template leveldb::Status SetEarliestSweepTime<LevelDBDirectTransaction>(
584     LevelDBDirectTransaction* db,
585     base::Time earliest_sweep);
586 
587 template <typename Transaction>
SetEarliestSweepTime(Transaction * txn,base::Time earliest_sweep)588 leveldb::Status SetEarliestSweepTime(Transaction* txn,
589                                      base::Time earliest_sweep) {
590   const std::string earliest_sweep_time_key = EarliestSweepKey::Encode();
591   int64_t time_micros = (earliest_sweep - base::Time()).InMicroseconds();
592   return indexed_db::PutInt(txn, earliest_sweep_time_key, time_micros);
593 }
594 
GetDefaultLevelDBComparator()595 const leveldb::Comparator* GetDefaultLevelDBComparator() {
596   static const base::NoDestructor<LDBComparator> ldb_comparator;
597   return ldb_comparator.get();
598 }
599 
600 }  // namespace indexed_db
601 }  // namespace content
602