1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "content/browser/indexed_db/indexed_db_backing_store.h"
6 
7 #include <algorithm>
8 #include <utility>
9 
10 #include "base/bind.h"
11 #include "base/files/file_path.h"
12 #include "base/format_macros.h"
13 #include "base/json/json_reader.h"
14 #include "base/json/json_writer.h"
15 #include "base/logging.h"
16 #include "base/macros.h"
17 #include "base/memory/ptr_util.h"
18 #include "base/metrics/histogram_functions.h"
19 #include "base/optional.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/stringprintf.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "base/task/post_task.h"
24 #include "base/trace_event/memory_dump_manager.h"
25 #include "build/build_config.h"
26 #include "components/services/storage/filesystem_proxy_factory.h"
27 #include "components/services/storage/indexed_db/scopes/leveldb_scope.h"
28 #include "components/services/storage/indexed_db/scopes/leveldb_scopes.h"
29 #include "components/services/storage/indexed_db/scopes/varint_coding.h"
30 #include "components/services/storage/indexed_db/transactional_leveldb/leveldb_write_batch.h"
31 #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_database.h"
32 #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_factory.h"
33 #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_iterator.h"
34 #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_transaction.h"
35 #include "components/services/storage/public/mojom/blob_storage_context.mojom.h"
36 #include "content/browser/indexed_db/indexed_db_active_blob_registry.h"
37 #include "content/browser/indexed_db/indexed_db_context_impl.h"
38 #include "content/browser/indexed_db/indexed_db_data_format_version.h"
39 #include "content/browser/indexed_db/indexed_db_database_error.h"
40 #include "content/browser/indexed_db/indexed_db_external_object.h"
41 #include "content/browser/indexed_db/indexed_db_factory.h"
42 #include "content/browser/indexed_db/indexed_db_leveldb_coding.h"
43 #include "content/browser/indexed_db/indexed_db_leveldb_env.h"
44 #include "content/browser/indexed_db/indexed_db_leveldb_operations.h"
45 #include "content/browser/indexed_db/indexed_db_metadata_coding.h"
46 #include "content/browser/indexed_db/indexed_db_reporting.h"
47 #include "content/browser/indexed_db/indexed_db_tracing.h"
48 #include "content/browser/indexed_db/indexed_db_value.h"
49 #include "mojo/public/cpp/bindings/pending_remote.h"
50 #include "net/base/load_flags.h"
51 #include "net/traffic_annotation/network_traffic_annotation.h"
52 #include "storage/browser/blob/blob_data_handle.h"
53 #include "storage/browser/file_system/file_stream_writer.h"
54 #include "storage/browser/file_system/file_writer_delegate.h"
55 #include "storage/browser/file_system/local_file_stream_writer.h"
56 #include "storage/common/database/database_identifier.h"
57 #include "storage/common/file_system/file_system_mount_option.h"
58 #include "third_party/blink/public/common/blob/blob_utils.h"
59 #include "third_party/blink/public/common/indexeddb/indexeddb_key_range.h"
60 #include "third_party/blink/public/common/indexeddb/web_idb_types.h"
61 #include "third_party/blink/public/mojom/blob/blob.mojom.h"
62 #include "third_party/leveldatabase/env_chromium.h"
63 
64 using base::FilePath;
65 using base::StringPiece;
66 using blink::IndexedDBDatabaseMetadata;
67 using blink::IndexedDBKey;
68 using blink::IndexedDBKeyRange;
69 using leveldb::Status;
70 using url::Origin;
71 
72 namespace content {
73 using indexed_db::CheckIndexAndMetaDataKey;
74 using indexed_db::CheckObjectStoreAndMetaDataType;
75 using indexed_db::FindGreatestKeyLessThanOrEqual;
76 using indexed_db::GetInt;
77 using indexed_db::GetString;
78 using indexed_db::GetVarInt;
79 using indexed_db::InternalInconsistencyStatus;
80 using indexed_db::InvalidDBKeyStatus;
81 using indexed_db::IOErrorStatus;
82 using indexed_db::PutBool;
83 using indexed_db::PutIDBKeyPath;
84 using indexed_db::PutInt;
85 using indexed_db::PutString;
86 using indexed_db::PutVarInt;
87 using indexed_db::ReportOpenStatus;
88 
89 // An RAII helper to ensure that "DidCommitTransaction" is called
90 // during this class's destruction.
91 class AutoDidCommitTransaction {
92  public:
AutoDidCommitTransaction(IndexedDBBackingStore * backing_store)93   explicit AutoDidCommitTransaction(IndexedDBBackingStore* backing_store)
94       : backing_store_(backing_store) {
95     DCHECK(backing_store_);
96   }
~AutoDidCommitTransaction()97   ~AutoDidCommitTransaction() { backing_store_->DidCommitTransaction(); }
98 
99   AutoDidCommitTransaction(const AutoDidCommitTransaction&) = delete;
100   AutoDidCommitTransaction operator=(const AutoDidCommitTransaction&) = delete;
101 
102  private:
103   IndexedDBBackingStore* backing_store_;
104 };
105 
106 namespace {
107 
GetBlobDirectoryName(const FilePath & path_base,int64_t database_id)108 FilePath GetBlobDirectoryName(const FilePath& path_base, int64_t database_id) {
109   return path_base.AppendASCII(base::StringPrintf("%" PRIx64, database_id));
110 }
111 
GetBlobDirectoryNameForKey(const FilePath & path_base,int64_t database_id,int64_t blob_number)112 FilePath GetBlobDirectoryNameForKey(const FilePath& path_base,
113                                     int64_t database_id,
114                                     int64_t blob_number) {
115   FilePath path = GetBlobDirectoryName(path_base, database_id);
116   path = path.AppendASCII(base::StringPrintf(
117       "%02x", static_cast<int>(blob_number & 0x000000000000ff00) >> 8));
118   return path;
119 }
120 
GetBlobFileNameForKey(const FilePath & path_base,int64_t database_id,int64_t blob_number)121 FilePath GetBlobFileNameForKey(const FilePath& path_base,
122                                int64_t database_id,
123                                int64_t blob_number) {
124   FilePath path =
125       GetBlobDirectoryNameForKey(path_base, database_id, blob_number);
126   path = path.AppendASCII(base::StringPrintf("%" PRIx64, blob_number));
127   return path;
128 }
129 
ComputeOriginIdentifier(const Origin & origin)130 std::string ComputeOriginIdentifier(const Origin& origin) {
131   return storage::GetIdentifierFromOrigin(origin) + "@1";
132 }
133 
134 // TODO(ericu): Error recovery. If we persistently can't read the
135 // blob journal, the safe thing to do is to clear it and leak the blobs,
136 // though that may be costly. Still, database/directory deletion should always
137 // clean things up, and we can write an fsck that will do a full correction if
138 // need be.
139 
140 // Read and decode the specified blob journal via the supplied transaction.
141 // The key must be either the recovery journal key or active journal key.
142 template <typename TransactionType>
GetBlobJournal(const StringPiece & key,TransactionType * transaction,BlobJournalType * journal)143 Status GetBlobJournal(const StringPiece& key,
144                       TransactionType* transaction,
145                       BlobJournalType* journal) {
146   IDB_TRACE("IndexedDBBackingStore::GetBlobJournal");
147   std::string data;
148   bool found = false;
149   Status s = transaction->Get(key, &data, &found);
150   if (!s.ok()) {
151     INTERNAL_READ_ERROR(READ_BLOB_JOURNAL);
152     return s;
153   }
154   journal->clear();
155   if (!found || data.empty())
156     return Status::OK();
157   StringPiece slice(data);
158   if (!DecodeBlobJournal(&slice, journal)) {
159     INTERNAL_CONSISTENCY_ERROR(DECODE_BLOB_JOURNAL);
160     s = InternalInconsistencyStatus();
161   }
162   return s;
163 }
164 
165 template <typename TransactionType>
GetRecoveryBlobJournal(TransactionType * transaction,BlobJournalType * journal)166 Status GetRecoveryBlobJournal(TransactionType* transaction,
167                               BlobJournalType* journal) {
168   return GetBlobJournal(RecoveryBlobJournalKey::Encode(), transaction, journal);
169 }
170 
171 template <typename TransactionType>
GetActiveBlobJournal(TransactionType * transaction,BlobJournalType * journal)172 Status GetActiveBlobJournal(TransactionType* transaction,
173                             BlobJournalType* journal) {
174   return GetBlobJournal(ActiveBlobJournalKey::Encode(), transaction, journal);
175 }
176 
177 // Clear the specified blob journal via the supplied transaction.
178 // The key must be either the recovery journal key or active journal key.
179 template <typename TransactionType>
ClearBlobJournal(TransactionType * transaction,const std::string & key)180 void ClearBlobJournal(TransactionType* transaction, const std::string& key) {
181   transaction->Remove(key);
182 }
183 
184 // Overwrite the specified blob journal via the supplied transaction.
185 // The key must be either the recovery journal key or active journal key.
186 template <typename TransactionType>
UpdateBlobJournal(TransactionType * transaction,const std::string & key,const BlobJournalType & journal)187 leveldb::Status UpdateBlobJournal(TransactionType* transaction,
188                                   const std::string& key,
189                                   const BlobJournalType& journal) {
190   std::string data;
191   EncodeBlobJournal(journal, &data);
192   return transaction->Put(key, &data);
193 }
194 
195 template <typename TransactionType>
UpdateRecoveryBlobJournal(TransactionType * transaction,const BlobJournalType & journal)196 leveldb::Status UpdateRecoveryBlobJournal(TransactionType* transaction,
197                                           const BlobJournalType& journal) {
198   return UpdateBlobJournal(transaction, RecoveryBlobJournalKey::Encode(),
199                            journal);
200 }
201 
202 template <typename TransactionType>
UpdateActiveBlobJournal(TransactionType * transaction,const BlobJournalType & journal)203 leveldb::Status UpdateActiveBlobJournal(TransactionType* transaction,
204                                         const BlobJournalType& journal) {
205   return UpdateBlobJournal(transaction, ActiveBlobJournalKey::Encode(),
206                            journal);
207 }
208 
209 // Append blobs to the specified blob journal via the supplied transaction.
210 // The key must be either the recovery journal key or active journal key.
211 template <typename TransactionType>
AppendBlobsToBlobJournal(TransactionType * transaction,const std::string & key,const BlobJournalType & journal)212 Status AppendBlobsToBlobJournal(TransactionType* transaction,
213                                 const std::string& key,
214                                 const BlobJournalType& journal) {
215   if (journal.empty())
216     return Status::OK();
217   BlobJournalType old_journal;
218   Status s = GetBlobJournal(key, transaction, &old_journal);
219   if (!s.ok())
220     return s;
221   old_journal.insert(old_journal.end(), journal.begin(), journal.end());
222   return UpdateBlobJournal(transaction, key, old_journal);
223 }
224 
225 template <typename TransactionType>
AppendBlobsToRecoveryBlobJournal(TransactionType * transaction,const BlobJournalType & journal)226 Status AppendBlobsToRecoveryBlobJournal(TransactionType* transaction,
227                                         const BlobJournalType& journal) {
228   return AppendBlobsToBlobJournal(transaction, RecoveryBlobJournalKey::Encode(),
229                                   journal);
230 }
231 
232 template <typename TransactionType>
AppendBlobsToActiveBlobJournal(TransactionType * transaction,const BlobJournalType & journal)233 Status AppendBlobsToActiveBlobJournal(TransactionType* transaction,
234                                       const BlobJournalType& journal) {
235   return AppendBlobsToBlobJournal(transaction, ActiveBlobJournalKey::Encode(),
236                                   journal);
237 }
238 
239 // Append a database to the specified blob journal via the supplied transaction.
240 // The key must be either the recovery journal key or active journal key.
MergeDatabaseIntoBlobJournal(TransactionalLevelDBTransaction * transaction,const std::string & key,int64_t database_id)241 Status MergeDatabaseIntoBlobJournal(
242     TransactionalLevelDBTransaction* transaction,
243     const std::string& key,
244     int64_t database_id) {
245   IDB_TRACE("IndexedDBBackingStore::MergeDatabaseIntoBlobJournal");
246   BlobJournalType journal;
247   Status s = GetBlobJournal(key, transaction, &journal);
248   if (!s.ok())
249     return s;
250   journal.push_back({database_id, DatabaseMetaDataKey::kAllBlobsNumber});
251   UpdateBlobJournal(transaction, key, journal);
252   return Status::OK();
253 }
254 
MergeDatabaseIntoRecoveryBlobJournal(TransactionalLevelDBTransaction * leveldb_transaction,int64_t database_id)255 Status MergeDatabaseIntoRecoveryBlobJournal(
256     TransactionalLevelDBTransaction* leveldb_transaction,
257     int64_t database_id) {
258   return MergeDatabaseIntoBlobJournal(
259       leveldb_transaction, RecoveryBlobJournalKey::Encode(), database_id);
260 }
261 
MergeDatabaseIntoActiveBlobJournal(TransactionalLevelDBTransaction * leveldb_transaction,int64_t database_id)262 Status MergeDatabaseIntoActiveBlobJournal(
263     TransactionalLevelDBTransaction* leveldb_transaction,
264     int64_t database_id) {
265   return MergeDatabaseIntoBlobJournal(
266       leveldb_transaction, ActiveBlobJournalKey::Encode(), database_id);
267 }
268 
269 // Blob Data is encoded as a series of:
270 //   { object_type [IndexedDBExternalObject::ObjectType as byte],
271 //     (for Blobs and Files only) blob_number [int64_t as varInt],
272 //     (for Blobs and Files only) type [string-with-length, may be empty],
273 //     (for Blobs and Files only) size [int64_t as varInt]
274 //     (for Files only) fileName [string-with-length]
275 //     (for Files only) lastModified [int64_t as varInt, in microseconds]
276 //     (for Native File System Handles only) token [binary-with-length]
277 //   }
278 // There is no length field; just read until you run out of data.
EncodeExternalObjects(const std::vector<IndexedDBExternalObject> & external_objects)279 std::string EncodeExternalObjects(
280     const std::vector<IndexedDBExternalObject>& external_objects) {
281   std::string ret;
282   for (const auto& info : external_objects) {
283     EncodeByte(static_cast<unsigned char>(info.object_type()), &ret);
284     switch (info.object_type()) {
285       case IndexedDBExternalObject::ObjectType::kBlob:
286       case IndexedDBExternalObject::ObjectType::kFile:
287         EncodeVarInt(info.blob_number(), &ret);
288         EncodeStringWithLength(info.type(), &ret);
289         EncodeVarInt(info.size(), &ret);
290         if (info.object_type() == IndexedDBExternalObject::ObjectType::kFile) {
291           EncodeStringWithLength(info.file_name(), &ret);
292           EncodeVarInt(
293               info.last_modified().ToDeltaSinceWindowsEpoch().InMicroseconds(),
294               &ret);
295         }
296         break;
297       case IndexedDBExternalObject::ObjectType::kNativeFileSystemHandle:
298         DCHECK(!info.native_file_system_token().empty());
299         EncodeBinary(info.native_file_system_token(), &ret);
300         break;
301     }
302   }
303   return ret;
304 }
305 
DecodeV3ExternalObjects(const base::StringPiece & data,std::vector<IndexedDBExternalObject> * output)306 bool DecodeV3ExternalObjects(const base::StringPiece& data,
307                              std::vector<IndexedDBExternalObject>* output) {
308   std::vector<IndexedDBExternalObject> ret;
309   output->clear();
310   StringPiece slice(data);
311   while (!slice.empty()) {
312     bool is_file;
313     int64_t blob_number;
314     base::string16 type;
315     int64_t size;
316     base::string16 file_name;
317 
318     if (!DecodeBool(&slice, &is_file))
319       return false;
320     if (!DecodeVarInt(&slice, &blob_number) ||
321         !DatabaseMetaDataKey::IsValidBlobNumber(blob_number))
322       return false;
323     if (!DecodeStringWithLength(&slice, &type))
324       return false;
325     if (is_file) {
326       if (!DecodeStringWithLength(&slice, &file_name))
327         return false;
328       ret.push_back(
329           IndexedDBExternalObject(blob_number, type, file_name, base::Time(),
330                                   IndexedDBExternalObject::kUnknownSize));
331     } else {
332       if (!DecodeVarInt(&slice, &size) || size < 0)
333         return false;
334       ret.push_back(IndexedDBExternalObject(type, size, blob_number));
335     }
336   }
337   output->swap(ret);
338 
339   return true;
340 }
341 
DecodeExternalObjects(const std::string & data,std::vector<IndexedDBExternalObject> * output)342 bool DecodeExternalObjects(const std::string& data,
343                            std::vector<IndexedDBExternalObject>* output) {
344   std::vector<IndexedDBExternalObject> ret;
345   output->clear();
346   StringPiece slice(data);
347   while (!slice.empty()) {
348     unsigned char raw_object_type;
349     if (!DecodeByte(&slice, &raw_object_type) ||
350         raw_object_type > static_cast<uint8_t>(
351                               IndexedDBExternalObject::ObjectType::kMaxValue)) {
352       return false;
353     }
354     IndexedDBExternalObject::ObjectType object_type =
355         static_cast<IndexedDBExternalObject::ObjectType>(raw_object_type);
356     switch (object_type) {
357       case IndexedDBExternalObject::ObjectType::kBlob:
358       case IndexedDBExternalObject::ObjectType::kFile: {
359         int64_t blob_number;
360         base::string16 type;
361         int64_t size;
362         base::string16 file_name;
363 
364         if (!DecodeVarInt(&slice, &blob_number) ||
365             !DatabaseMetaDataKey::IsValidBlobNumber(blob_number)) {
366           return false;
367         }
368         if (!DecodeStringWithLength(&slice, &type))
369           return false;
370         if (!DecodeVarInt(&slice, &size) || size < 0)
371           return false;
372         if (object_type != IndexedDBExternalObject::ObjectType::kFile) {
373           ret.push_back(IndexedDBExternalObject(type, size, blob_number));
374           break;
375         }
376         if (!DecodeStringWithLength(&slice, &file_name))
377           return false;
378         int64_t last_modified;
379         if (!DecodeVarInt(&slice, &last_modified) || size < 0)
380           return false;
381         ret.push_back(IndexedDBExternalObject(
382             blob_number, type, file_name,
383             base::Time::FromDeltaSinceWindowsEpoch(
384                 base::TimeDelta::FromMicroseconds(last_modified)),
385             size));
386         break;
387       }
388       case IndexedDBExternalObject::ObjectType::kNativeFileSystemHandle: {
389         base::span<const uint8_t> token;
390         if (!DecodeBinary(&slice, &token))
391           return false;
392         ret.push_back(IndexedDBExternalObject(
393             std::vector<uint8_t>(token.begin(), token.end())));
394         break;
395       }
396     }
397   }
398   output->swap(ret);
399 
400   return true;
401 }
402 
IsPathTooLong(storage::FilesystemProxy * filesystem,const base::FilePath & leveldb_dir)403 bool IsPathTooLong(storage::FilesystemProxy* filesystem,
404                    const base::FilePath& leveldb_dir) {
405   base::Optional<int> limit =
406       filesystem->GetMaximumPathComponentLength(leveldb_dir.DirName());
407   if (!limit.has_value()) {
408     DLOG(WARNING) << "GetMaximumPathComponentLength returned -1";
409 // In limited testing, ChromeOS returns 143, other OSes 255.
410 #if defined(OS_CHROMEOS)
411     limit = 143;
412 #else
413     limit = 255;
414 #endif
415   }
416   size_t component_length = leveldb_dir.BaseName().value().length();
417   if (component_length > static_cast<uint32_t>(*limit)) {
418     DLOG(WARNING) << "Path component length (" << component_length
419                   << ") exceeds maximum (" << *limit
420                   << ") allowed by this filesystem.";
421     const int min = 140;
422     const int max = 300;
423     const int num_buckets = 12;
424     base::UmaHistogramCustomCounts(
425         "WebCore.IndexedDB.BackingStore.OverlyLargeOriginLength",
426         component_length, min, max, num_buckets);
427     return true;
428   }
429   return false;
430 }
431 
DeleteBlobsInRange(IndexedDBBackingStore::Transaction * transaction,int64_t database_id,const std::string & start_key,const std::string & end_key,bool upper_open)432 Status DeleteBlobsInRange(IndexedDBBackingStore::Transaction* transaction,
433                           int64_t database_id,
434                           const std::string& start_key,
435                           const std::string& end_key,
436                           bool upper_open) {
437   Status s;
438   std::unique_ptr<TransactionalLevelDBIterator> it =
439       transaction->transaction()->CreateIterator(s);
440   if (!s.ok()) {
441     INTERNAL_WRITE_ERROR(CREATE_ITERATOR);
442     return s;
443   }
444   s = it->Seek(start_key);
445   for (; s.ok() && it->IsValid() &&
446          (upper_open ? CompareKeys(it->Key(), end_key) < 0
447                      : CompareKeys(it->Key(), end_key) <= 0);
448        s = it->Next()) {
449     StringPiece key_piece(it->Key());
450     std::string user_key =
451         BlobEntryKey::ReencodeToObjectStoreDataKey(&key_piece);
452     if (user_key.empty()) {
453       INTERNAL_CONSISTENCY_ERROR(GET_IDBDATABASE_METADATA);
454       return InternalInconsistencyStatus();
455     }
456     transaction->PutExternalObjects(database_id, user_key, nullptr);
457   }
458   return s;
459 }
460 
DeleteBlobsInObjectStore(IndexedDBBackingStore::Transaction * transaction,int64_t database_id,int64_t object_store_id)461 Status DeleteBlobsInObjectStore(IndexedDBBackingStore::Transaction* transaction,
462                                 int64_t database_id,
463                                 int64_t object_store_id) {
464   std::string start_key, stop_key;
465   start_key =
466       BlobEntryKey::EncodeMinKeyForObjectStore(database_id, object_store_id);
467   stop_key =
468       BlobEntryKey::EncodeStopKeyForObjectStore(database_id, object_store_id);
469   return DeleteBlobsInRange(transaction, database_id, start_key, stop_key,
470                             true);
471 }
472 
ObjectStoreCursorOptions(TransactionalLevelDBTransaction * transaction,int64_t database_id,int64_t object_store_id,const IndexedDBKeyRange & range,blink::mojom::IDBCursorDirection direction,IndexedDBBackingStore::Cursor::CursorOptions * cursor_options,Status * status)473 bool ObjectStoreCursorOptions(
474     TransactionalLevelDBTransaction* transaction,
475     int64_t database_id,
476     int64_t object_store_id,
477     const IndexedDBKeyRange& range,
478     blink::mojom::IDBCursorDirection direction,
479     IndexedDBBackingStore::Cursor::CursorOptions* cursor_options,
480     Status* status) {
481   cursor_options->database_id = database_id;
482   cursor_options->object_store_id = object_store_id;
483 
484   bool lower_bound = range.lower().IsValid();
485   bool upper_bound = range.upper().IsValid();
486   cursor_options->forward =
487       (direction == blink::mojom::IDBCursorDirection::NextNoDuplicate ||
488        direction == blink::mojom::IDBCursorDirection::Next);
489   cursor_options->unique =
490       (direction == blink::mojom::IDBCursorDirection::NextNoDuplicate ||
491        direction == blink::mojom::IDBCursorDirection::PrevNoDuplicate);
492 
493   if (!lower_bound) {
494     cursor_options->low_key =
495         ObjectStoreDataKey::Encode(database_id, object_store_id, MinIDBKey());
496     cursor_options->low_open = true;  // Not included.
497   } else {
498     cursor_options->low_key =
499         ObjectStoreDataKey::Encode(database_id, object_store_id, range.lower());
500     cursor_options->low_open = range.lower_open();
501   }
502 
503   if (!upper_bound) {
504     cursor_options->high_key =
505         ObjectStoreDataKey::Encode(database_id, object_store_id, MaxIDBKey());
506 
507     if (cursor_options->forward) {
508       cursor_options->high_open = true;  // Not included.
509     } else {
510       // We need a key that exists.
511       if (!FindGreatestKeyLessThanOrEqual(transaction, cursor_options->high_key,
512                                           &cursor_options->high_key, status))
513         return false;
514       cursor_options->high_open = false;
515     }
516   } else {
517     cursor_options->high_key =
518         ObjectStoreDataKey::Encode(database_id, object_store_id, range.upper());
519     cursor_options->high_open = range.upper_open();
520 
521     if (!cursor_options->forward) {
522       // For reverse cursors, we need a key that exists.
523       std::string found_high_key;
524       if (!FindGreatestKeyLessThanOrEqual(transaction, cursor_options->high_key,
525                                           &found_high_key, status))
526         return false;
527 
528       // If the target key should not be included, but we end up with a smaller
529       // key, we should include that.
530       if (cursor_options->high_open &&
531           CompareIndexKeys(found_high_key, cursor_options->high_key) < 0)
532         cursor_options->high_open = false;
533 
534       cursor_options->high_key = found_high_key;
535     }
536   }
537 
538   return true;
539 }
540 
IndexCursorOptions(TransactionalLevelDBTransaction * transaction,int64_t database_id,int64_t object_store_id,int64_t index_id,const IndexedDBKeyRange & range,blink::mojom::IDBCursorDirection direction,IndexedDBBackingStore::Cursor::CursorOptions * cursor_options,Status * status)541 bool IndexCursorOptions(
542     TransactionalLevelDBTransaction* transaction,
543     int64_t database_id,
544     int64_t object_store_id,
545     int64_t index_id,
546     const IndexedDBKeyRange& range,
547     blink::mojom::IDBCursorDirection direction,
548     IndexedDBBackingStore::Cursor::CursorOptions* cursor_options,
549     Status* status) {
550   IDB_TRACE("IndexedDBBackingStore::IndexCursorOptions");
551   DCHECK(transaction);
552   if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
553     return false;
554 
555   cursor_options->database_id = database_id;
556   cursor_options->object_store_id = object_store_id;
557   cursor_options->index_id = index_id;
558 
559   bool lower_bound = range.lower().IsValid();
560   bool upper_bound = range.upper().IsValid();
561   cursor_options->forward =
562       (direction == blink::mojom::IDBCursorDirection::NextNoDuplicate ||
563        direction == blink::mojom::IDBCursorDirection::Next);
564   cursor_options->unique =
565       (direction == blink::mojom::IDBCursorDirection::NextNoDuplicate ||
566        direction == blink::mojom::IDBCursorDirection::PrevNoDuplicate);
567 
568   if (!lower_bound) {
569     cursor_options->low_key =
570         IndexDataKey::EncodeMinKey(database_id, object_store_id, index_id);
571     cursor_options->low_open = false;  // Included.
572   } else {
573     cursor_options->low_key = IndexDataKey::Encode(database_id, object_store_id,
574                                                    index_id, range.lower());
575     cursor_options->low_open = range.lower_open();
576   }
577 
578   if (!upper_bound) {
579     cursor_options->high_key =
580         IndexDataKey::EncodeMaxKey(database_id, object_store_id, index_id);
581     cursor_options->high_open = false;  // Included.
582 
583     if (!cursor_options->forward) {
584       // We need a key that exists.
585       if (!FindGreatestKeyLessThanOrEqual(transaction, cursor_options->high_key,
586                                           &cursor_options->high_key, status))
587         return false;
588       cursor_options->high_open = false;
589     }
590   } else {
591     cursor_options->high_key = IndexDataKey::Encode(
592         database_id, object_store_id, index_id, range.upper());
593     cursor_options->high_open = range.upper_open();
594 
595     if (!cursor_options->forward) {
596       // For reverse cursors, we need a key that exists.
597       std::string found_high_key;
598       // Seek to the *last* key in the set of non-unique keys
599       if (!FindGreatestKeyLessThanOrEqual(transaction, cursor_options->high_key,
600                                           &found_high_key, status))
601         return false;
602 
603       // If the target key should not be included, but we end up with a smaller
604       // key, we should include that.
605       if (cursor_options->high_open &&
606           CompareIndexKeys(found_high_key, cursor_options->high_key) < 0)
607         cursor_options->high_open = false;
608 
609       cursor_options->high_key = found_high_key;
610     }
611   }
612 
613   return true;
614 }
615 
616 }  // namespace
617 
IndexedDBBackingStore(Mode backing_store_mode,TransactionalLevelDBFactory * transactional_leveldb_factory,const Origin & origin,const FilePath & blob_path,std::unique_ptr<TransactionalLevelDBDatabase> db,storage::mojom::BlobStorageContext * blob_storage_context,storage::mojom::NativeFileSystemContext * native_file_system_context,std::unique_ptr<storage::FilesystemProxy> filesystem_proxy,BlobFilesCleanedCallback blob_files_cleaned,ReportOutstandingBlobsCallback report_outstanding_blobs,scoped_refptr<base::SequencedTaskRunner> idb_task_runner,scoped_refptr<base::SequencedTaskRunner> io_task_runner)618 IndexedDBBackingStore::IndexedDBBackingStore(
619     Mode backing_store_mode,
620     TransactionalLevelDBFactory* transactional_leveldb_factory,
621     const Origin& origin,
622     const FilePath& blob_path,
623     std::unique_ptr<TransactionalLevelDBDatabase> db,
624     storage::mojom::BlobStorageContext* blob_storage_context,
625     storage::mojom::NativeFileSystemContext* native_file_system_context,
626     std::unique_ptr<storage::FilesystemProxy> filesystem_proxy,
627     BlobFilesCleanedCallback blob_files_cleaned,
628     ReportOutstandingBlobsCallback report_outstanding_blobs,
629     scoped_refptr<base::SequencedTaskRunner> idb_task_runner,
630     scoped_refptr<base::SequencedTaskRunner> io_task_runner)
631     : backing_store_mode_(backing_store_mode),
632       transactional_leveldb_factory_(transactional_leveldb_factory),
633       origin_(origin),
634       blob_path_(blob_path),
635       blob_storage_context_(blob_storage_context),
636       native_file_system_context_(native_file_system_context),
637       filesystem_proxy_(std::move(filesystem_proxy)),
638       origin_identifier_(ComputeOriginIdentifier(origin)),
639       idb_task_runner_(idb_task_runner),
640       io_task_runner_(io_task_runner),
641       db_(std::move(db)),
642       blob_files_cleaned_(std::move(blob_files_cleaned)) {
643   DCHECK(idb_task_runner_->RunsTasksInCurrentSequence());
644   if (backing_store_mode == Mode::kInMemory) {
645     blob_path_ = FilePath();
646   } else {
647     DCHECK(filesystem_proxy_) << "On disk database must have a filesystem";
648   }
649   active_blob_registry_ = std::make_unique<IndexedDBActiveBlobRegistry>(
650       std::move(report_outstanding_blobs),
651       base::BindRepeating(&IndexedDBBackingStore::ReportBlobUnused,
652                           weak_factory_.GetWeakPtr()));
653 }
654 
~IndexedDBBackingStore()655 IndexedDBBackingStore::~IndexedDBBackingStore() {
656   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
657 }
658 
RecordIdentifier(const std::string & primary_key,int64_t version)659 IndexedDBBackingStore::RecordIdentifier::RecordIdentifier(
660     const std::string& primary_key,
661     int64_t version)
662     : primary_key_(primary_key), version_(version) {
663   DCHECK(!primary_key.empty());
664 }
RecordIdentifier()665 IndexedDBBackingStore::RecordIdentifier::RecordIdentifier()
666     : primary_key_(), version_(-1) {}
~RecordIdentifier()667 IndexedDBBackingStore::RecordIdentifier::~RecordIdentifier() {}
668 
669 constexpr const int IndexedDBBackingStore::kMaxJournalCleanRequests;
670 constexpr const base::TimeDelta
671     IndexedDBBackingStore::kMaxJournalCleaningWindowTime;
672 constexpr const base::TimeDelta
673     IndexedDBBackingStore::kInitialJournalCleaningWindowTime;
674 
Initialize(bool clean_active_journal)675 leveldb::Status IndexedDBBackingStore::Initialize(bool clean_active_journal) {
676 #if DCHECK_IS_ON()
677   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
678   DCHECK(!initialized_);
679 #endif
680   const IndexedDBDataFormatVersion latest_known_data_version =
681       IndexedDBDataFormatVersion::GetCurrent();
682   const std::string schema_version_key = SchemaVersionKey::Encode();
683   const std::string data_version_key = DataVersionKey::Encode();
684 
685   std::unique_ptr<LevelDBWriteBatch> write_batch = LevelDBWriteBatch::Create();
686 
687   // This must have a default value to handle this case where
688   // ReportSchemaVersion reports a not-found entry.
689   int64_t db_schema_version = 0;
690   IndexedDBDataFormatVersion db_data_version;
691   bool found = false;
692   Status s = GetInt(db_.get(), schema_version_key, &db_schema_version, &found);
693   if (!s.ok()) {
694     INTERNAL_READ_ERROR(SET_UP_METADATA);
695     return s;
696   }
697   indexed_db::ReportSchemaVersion(db_schema_version, origin_);
698   if (!found) {
699     // Initialize new backing store.
700     db_schema_version = indexed_db::kLatestKnownSchemaVersion;
701     ignore_result(
702         PutInt(write_batch.get(), schema_version_key, db_schema_version));
703     db_data_version = latest_known_data_version;
704     ignore_result(
705         PutInt(write_batch.get(), data_version_key, db_data_version.Encode()));
706     // If a blob directory already exists for this database, blow it away.  It's
707     // leftover from a partially-purged previous generation of data.
708     if (filesystem_proxy_ &&
709         !filesystem_proxy_->DeletePathRecursively(blob_path_)) {
710       INTERNAL_WRITE_ERROR(SET_UP_METADATA);
711       return IOErrorStatus();
712     }
713   } else {
714     if (db_schema_version > indexed_db::kLatestKnownSchemaVersion)
715       return InternalInconsistencyStatus();
716 
717     // Upgrade old backing store.
718     if (s.ok() && db_schema_version < 1) {
719       s = MigrateToV1(write_batch.get());
720     }
721     if (s.ok() && db_schema_version < 2) {
722       s = MigrateToV2(write_batch.get());
723       db_data_version = latest_known_data_version;
724     }
725     if (s.ok() && db_schema_version < 3) {
726       s = MigrateToV3(write_batch.get());
727     }
728     if (s.ok() && db_schema_version < 4) {
729       s = MigrateToV4(write_batch.get());
730     }
731     if (s.ok() && db_schema_version < 5) {
732       s = MigrateToV5(write_batch.get());
733     }
734     db_schema_version = indexed_db::kLatestKnownSchemaVersion;
735   }
736 
737   if (!s.ok()) {
738     INTERNAL_READ_ERROR(SET_UP_METADATA);
739     return s;
740   }
741 
742   // All new values will be written using this serialization version.
743   found = false;
744   if (db_data_version.blink_version() == 0 &&
745       db_data_version.v8_version() == 0) {
746     // We didn't read |db_data_version| yet.
747     int64_t raw_db_data_version = 0;
748     s = GetInt(db_.get(), data_version_key, &raw_db_data_version, &found);
749     if (!s.ok()) {
750       INTERNAL_READ_ERROR(SET_UP_METADATA);
751       return s;
752     }
753     if (!found) {
754       INTERNAL_CONSISTENCY_ERROR(SET_UP_METADATA);
755       return InternalInconsistencyStatus();
756     }
757     db_data_version = IndexedDBDataFormatVersion::Decode(raw_db_data_version);
758   }
759   if (latest_known_data_version == db_data_version) {
760     // Up to date. Nothing to do.
761   } else if (latest_known_data_version.IsAtLeast(db_data_version)) {
762     db_data_version = latest_known_data_version;
763     ignore_result(
764         PutInt(write_batch.get(), data_version_key, db_data_version.Encode()));
765   } else {
766     // |db_data_version| is in the future according to at least one component.
767     INTERNAL_CONSISTENCY_ERROR(SET_UP_METADATA);
768     return InternalInconsistencyStatus();
769   }
770 
771   DCHECK_EQ(db_schema_version, indexed_db::kLatestKnownSchemaVersion);
772   DCHECK(db_data_version == latest_known_data_version);
773 
774   s = db_->Write(write_batch.get());
775   write_batch.reset();
776   if (!s.ok()) {
777     indexed_db::ReportOpenStatus(
778         indexed_db::INDEXED_DB_BACKING_STORE_OPEN_FAILED_METADATA_SETUP,
779         origin_);
780     INTERNAL_WRITE_ERROR(SET_UP_METADATA);
781     return s;
782   }
783 
784   if (clean_active_journal) {
785     s = CleanUpBlobJournal(ActiveBlobJournalKey::Encode());
786     if (!s.ok()) {
787       indexed_db::ReportOpenStatus(
788           indexed_db::
789               INDEXED_DB_BACKING_STORE_OPEN_FAILED_CLEANUP_JOURNAL_ERROR,
790           origin_);
791     }
792   }
793 #if DCHECK_IS_ON()
794   initialized_ = true;
795 #endif
796   return s;
797 }
798 
AnyDatabaseContainsBlobs(TransactionalLevelDBDatabase * db,bool * blobs_exist)799 Status IndexedDBBackingStore::AnyDatabaseContainsBlobs(
800     TransactionalLevelDBDatabase* db,
801     bool* blobs_exist) {
802   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
803 
804   Status status = leveldb::Status::OK();
805   std::vector<base::string16> names;
806   IndexedDBMetadataCoding metadata_coding;
807   status = metadata_coding.ReadDatabaseNames(db, origin_identifier_, &names);
808   if (!status.ok())
809     return status;
810 
811   *blobs_exist = false;
812   for (const auto& name : names) {
813     IndexedDBDatabaseMetadata metadata;
814     bool found = false;
815     status = metadata_coding.ReadMetadataForDatabaseName(
816         db, origin_identifier_, name, &metadata, &found);
817     if (!found)
818       return Status::NotFound("Metadata not found for \"%s\".",
819                               base::UTF16ToUTF8(name));
820     for (const auto& store_id_metadata_pair : metadata.object_stores) {
821       leveldb::ReadOptions options;
822       // Since this is a scan, don't fill up the cache, as it's not likely these
823       // blocks will be reloaded.
824       options.fill_cache = false;
825       options.verify_checksums = true;
826       std::unique_ptr<TransactionalLevelDBIterator> iterator =
827           db->CreateIterator(options);
828       std::string min_key = BlobEntryKey::EncodeMinKeyForObjectStore(
829           metadata.id, store_id_metadata_pair.first);
830       std::string max_key = BlobEntryKey::EncodeStopKeyForObjectStore(
831           metadata.id, store_id_metadata_pair.first);
832       status = iterator->Seek(base::StringPiece(min_key));
833       if (status.IsNotFound()) {
834         status = Status::OK();
835         continue;
836       }
837       if (!status.ok())
838         return status;
839       if (iterator->IsValid() &&
840           db->leveldb_state()->comparator()->Compare(
841               leveldb_env::MakeSlice(iterator->Key()), max_key) < 0) {
842         *blobs_exist = true;
843         return Status::OK();
844       }
845     }
846 
847     if (!status.ok())
848       return status;
849   }
850   return Status::OK();
851 }
852 
UpgradeBlobEntriesToV4(TransactionalLevelDBDatabase * db,LevelDBWriteBatch * write_batch,std::vector<base::FilePath> * empty_blobs_to_delete)853 Status IndexedDBBackingStore::UpgradeBlobEntriesToV4(
854     TransactionalLevelDBDatabase* db,
855     LevelDBWriteBatch* write_batch,
856     std::vector<base::FilePath>* empty_blobs_to_delete) {
857   DCHECK(filesystem_proxy_);
858 
859   Status status = leveldb::Status::OK();
860   std::vector<base::string16> names;
861   IndexedDBMetadataCoding metadata_coding;
862   status = metadata_coding.ReadDatabaseNames(db, origin_identifier_, &names);
863   if (!status.ok())
864     return status;
865 
866   for (const auto& name : names) {
867     IndexedDBDatabaseMetadata metadata;
868     bool found = false;
869     status = metadata_coding.ReadMetadataForDatabaseName(
870         db, origin_identifier_, name, &metadata, &found);
871     if (!found)
872       return Status::NotFound("Metadata not found for \"%s\".",
873                               base::UTF16ToUTF8(name));
874     for (const auto& store_id_metadata_pair : metadata.object_stores) {
875       leveldb::ReadOptions options;
876       // Since this is a scan, don't fill up the cache, as it's not likely these
877       // blocks will be reloaded.
878       options.fill_cache = false;
879       options.verify_checksums = true;
880       std::unique_ptr<TransactionalLevelDBIterator> iterator =
881           db->CreateIterator(options);
882       std::string min_key = BlobEntryKey::EncodeMinKeyForObjectStore(
883           metadata.id, store_id_metadata_pair.first);
884       std::string max_key = BlobEntryKey::EncodeStopKeyForObjectStore(
885           metadata.id, store_id_metadata_pair.first);
886       status = iterator->Seek(base::StringPiece(min_key));
887       if (status.IsNotFound()) {
888         status = Status::OK();
889         continue;
890       }
891       if (!status.ok())
892         return status;
893       // Loop through all blob entries in for the given object store.
894       for (; status.ok() && iterator->IsValid() &&
895              db->leveldb_state()->comparator()->Compare(
896                  leveldb_env::MakeSlice(iterator->Key()), max_key) < 0;
897            status = iterator->Next()) {
898         std::vector<IndexedDBExternalObject> temp_external_objects;
899         DecodeV3ExternalObjects(iterator->Value(), &temp_external_objects);
900         bool needs_rewrite = false;
901         // Read the old entries & modify them to add the missing data.
902         for (auto& object : temp_external_objects) {
903           if (object.object_type() !=
904               IndexedDBExternalObject::ObjectType::kFile) {
905             continue;
906           }
907           needs_rewrite = true;
908           base::FilePath path =
909               GetBlobFileName(metadata.id, object.blob_number());
910 
911           base::Optional<base::File::Info> info =
912               filesystem_proxy_->GetFileInfo(path);
913           if (!info.has_value()) {
914             return leveldb::Status::Corruption(
915                 "Unable to upgrade to database version 4.", "");
916           }
917           object.set_size(info->size);
918           object.set_last_modified(info->last_modified);
919           if (info->size == 0)
920             empty_blobs_to_delete->push_back(path);
921         }
922         if (!needs_rewrite)
923           continue;
924         std::string data = EncodeExternalObjects(temp_external_objects);
925         write_batch->Put(iterator->Key(), data);
926         if (!status.ok())
927           return status;
928       }
929       if (status.IsNotFound())
930         status = leveldb::Status::OK();
931       if (!status.ok())
932         return status;
933     }
934 
935     if (!status.ok())
936       return status;
937   }
938   return Status::OK();
939 }
940 
ValidateBlobFiles(TransactionalLevelDBDatabase * db)941 Status IndexedDBBackingStore::ValidateBlobFiles(
942     TransactionalLevelDBDatabase* db) {
943   DCHECK(filesystem_proxy_);
944 
945   Status status = leveldb::Status::OK();
946   std::vector<base::string16> names;
947   IndexedDBMetadataCoding metadata_coding;
948   status = metadata_coding.ReadDatabaseNames(db, origin_identifier_, &names);
949   if (!status.ok())
950     return status;
951 
952   for (const auto& name : names) {
953     IndexedDBDatabaseMetadata metadata;
954     bool found = false;
955     status = metadata_coding.ReadMetadataForDatabaseName(
956         db, origin_identifier_, name, &metadata, &found);
957     if (!found)
958       return Status::NotFound("Metadata not found for \"%s\".",
959                               base::UTF16ToUTF8(name));
960     for (const auto& store_id_metadata_pair : metadata.object_stores) {
961       leveldb::ReadOptions options;
962       // Since this is a scan, don't fill up the cache, as it's not likely these
963       // blocks will be reloaded.
964       options.fill_cache = false;
965       options.verify_checksums = true;
966       std::unique_ptr<TransactionalLevelDBIterator> iterator =
967           db->CreateIterator(options);
968       std::string min_key = BlobEntryKey::EncodeMinKeyForObjectStore(
969           metadata.id, store_id_metadata_pair.first);
970       std::string max_key = BlobEntryKey::EncodeStopKeyForObjectStore(
971           metadata.id, store_id_metadata_pair.first);
972       status = iterator->Seek(base::StringPiece(min_key));
973       if (status.IsNotFound()) {
974         status = Status::OK();
975         continue;
976       }
977       if (!status.ok())
978         return status;
979       // Loop through all blob entries in for the given object store.
980       for (; status.ok() && iterator->IsValid() &&
981              db->leveldb_state()->comparator()->Compare(
982                  leveldb_env::MakeSlice(iterator->Key()), max_key) < 0;
983            status = iterator->Next()) {
984         std::vector<IndexedDBExternalObject> temp_external_objects;
985         DecodeExternalObjects(iterator->Value().as_string(),
986                               &temp_external_objects);
987         for (auto& object : temp_external_objects) {
988           if (object.object_type() !=
989               IndexedDBExternalObject::ObjectType::kFile) {
990             continue;
991           }
992           // Empty blobs are not written to disk.
993           if (!object.size())
994             continue;
995 
996           base::FilePath path =
997               GetBlobFileName(metadata.id, object.blob_number());
998           base::Optional<base::File::Info> info =
999               filesystem_proxy_->GetFileInfo(path);
1000           if (!info.has_value()) {
1001             return leveldb::Status::Corruption(
1002                 "Unable to upgrade to database version 5.", "");
1003           }
1004         }
1005       }
1006       if (status.IsNotFound())
1007         status = leveldb::Status::OK();
1008       if (!status.ok())
1009         return status;
1010     }
1011 
1012     if (!status.ok())
1013       return status;
1014   }
1015   return Status::OK();
1016 }
1017 
RevertSchemaToV2()1018 Status IndexedDBBackingStore::RevertSchemaToV2() {
1019 #if DCHECK_IS_ON()
1020   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
1021   DCHECK(initialized_);
1022 #endif
1023   const std::string schema_version_key = SchemaVersionKey::Encode();
1024   std::string value_buffer;
1025   EncodeInt(2, &value_buffer);
1026   leveldb::Status s = db_->Put(schema_version_key, &value_buffer);
1027   if (!s.ok())
1028     INTERNAL_WRITE_ERROR(REVERT_SCHEMA_TO_V2);
1029   return s;
1030 }
1031 
HasV2SchemaCorruption()1032 V2SchemaCorruptionStatus IndexedDBBackingStore::HasV2SchemaCorruption() {
1033 #if DCHECK_IS_ON()
1034   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
1035   DCHECK(initialized_);
1036 #endif
1037   const std::string schema_version_key = SchemaVersionKey::Encode();
1038 
1039   int64_t db_schema_version = 0;
1040   bool found = false;
1041   Status s = GetInt(db_.get(), schema_version_key, &db_schema_version, &found);
1042   if (!s.ok())
1043     return V2SchemaCorruptionStatus::kUnknown;
1044   if (db_schema_version != 2)
1045     return V2SchemaCorruptionStatus::kNo;
1046 
1047   bool has_blobs = false;
1048   s = AnyDatabaseContainsBlobs(db_.get(), &has_blobs);
1049   if (!s.ok())
1050     return V2SchemaCorruptionStatus::kUnknown;
1051   if (!has_blobs)
1052     return V2SchemaCorruptionStatus::kNo;
1053   return V2SchemaCorruptionStatus::kYes;
1054 }
1055 
1056 std::unique_ptr<IndexedDBBackingStore::Transaction>
CreateTransaction(blink::mojom::IDBTransactionDurability durability,blink::mojom::IDBTransactionMode mode)1057 IndexedDBBackingStore::CreateTransaction(
1058     blink::mojom::IDBTransactionDurability durability,
1059     blink::mojom::IDBTransactionMode mode) {
1060   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
1061   return std::make_unique<IndexedDBBackingStore::Transaction>(
1062       weak_factory_.GetWeakPtr(), durability, mode);
1063 }
1064 
1065 // static
ShouldSyncOnCommit(blink::mojom::IDBTransactionDurability durability)1066 bool IndexedDBBackingStore::ShouldSyncOnCommit(
1067     blink::mojom::IDBTransactionDurability durability) {
1068   switch (durability) {
1069     case blink::mojom::IDBTransactionDurability::Default:
1070     case blink::mojom::IDBTransactionDurability::Strict:
1071       return true;
1072     case blink::mojom::IDBTransactionDurability::Relaxed:
1073       return false;
1074   }
1075 }
1076 
GetCompleteMetadata(std::vector<IndexedDBDatabaseMetadata> * output)1077 leveldb::Status IndexedDBBackingStore::GetCompleteMetadata(
1078     std::vector<IndexedDBDatabaseMetadata>* output) {
1079 #if DCHECK_IS_ON()
1080   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
1081   DCHECK(initialized_);
1082 #endif
1083 
1084   IndexedDBMetadataCoding metadata_coding;
1085   leveldb::Status status = leveldb::Status::OK();
1086   std::vector<base::string16> names;
1087   status =
1088       metadata_coding.ReadDatabaseNames(db_.get(), origin_identifier_, &names);
1089   if (!status.ok())
1090     return status;
1091 
1092   output->reserve(names.size());
1093   for (auto& name : names) {
1094     output->emplace_back();
1095     bool found = false;
1096     status = metadata_coding.ReadMetadataForDatabaseName(
1097         db_.get(), origin_identifier_, name, &output->back(), &found);
1098     output->back().name = std::move(name);
1099     if (!found)
1100       return Status::NotFound("Metadata not found for \"%s\".",
1101                               base::UTF16ToUTF8(output->back().name));
1102     if (!status.ok())
1103       return status;
1104   }
1105 
1106   return status;
1107 }
1108 
1109 // static
RecordCorruptionInfo(const base::FilePath & path_base,const Origin & origin,const std::string & message)1110 bool IndexedDBBackingStore::RecordCorruptionInfo(
1111     const base::FilePath& path_base,
1112     const Origin& origin,
1113     const std::string& message) {
1114   auto filesystem = storage::CreateFilesystemProxy();
1115   const base::FilePath info_path =
1116       path_base.Append(indexed_db::ComputeCorruptionFileName(origin));
1117   if (IsPathTooLong(filesystem.get(), info_path))
1118     return false;
1119 
1120   base::DictionaryValue root_dict;
1121   root_dict.SetString("message", message);
1122   std::string output_js;
1123 
1124   base::JSONWriter::Write(root_dict, &output_js);
1125   return filesystem->WriteFileAtomically(info_path, std::move(output_js));
1126 }
1127 
DeleteDatabase(const base::string16 & name,TransactionalLevelDBTransaction * transaction)1128 Status IndexedDBBackingStore::DeleteDatabase(
1129     const base::string16& name,
1130     TransactionalLevelDBTransaction* transaction) {
1131 #if DCHECK_IS_ON()
1132   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
1133   DCHECK(initialized_);
1134 #endif
1135   IDB_TRACE("IndexedDBBackingStore::DeleteDatabase");
1136 
1137   Status s;
1138   bool success = false;
1139   int64_t id = 0;
1140   s = IndexedDBMetadataCoding().FindDatabaseId(db_.get(), origin_identifier_,
1141                                                name, &id, &success);
1142   if (!s.ok())
1143     return s;
1144   if (!success)
1145     return Status::OK();
1146 
1147   // |ORIGIN_NAME| is the first key (0) in the database prefix, so this deletes
1148   // the whole database.
1149   const std::string start_key =
1150       DatabaseMetaDataKey::Encode(id, DatabaseMetaDataKey::ORIGIN_NAME);
1151   const std::string stop_key =
1152       DatabaseMetaDataKey::Encode(id + 1, DatabaseMetaDataKey::ORIGIN_NAME);
1153   {
1154     IDB_TRACE("IndexedDBBackingStore::DeleteDatabase.DeleteEntries");
1155     // It is safe to do deferred deletion here because database ids are never
1156     // reused, so this range of keys will never be accessed again.
1157     s = transaction->RemoveRange(
1158         start_key, stop_key, LevelDBScopeDeletionMode::kDeferredWithCompaction);
1159   }
1160   if (!s.ok()) {
1161     INTERNAL_WRITE_ERROR(DELETE_DATABASE);
1162     return s;
1163   }
1164 
1165   const std::string key = DatabaseNameKey::Encode(origin_identifier_, name);
1166   s = transaction->Remove(key);
1167   if (!s.ok())
1168     return s;
1169 
1170   bool need_cleanup = false;
1171   bool database_has_blob_references =
1172       active_blob_registry()->MarkDatabaseDeletedAndCheckIfReferenced(id);
1173   if (database_has_blob_references) {
1174     s = MergeDatabaseIntoActiveBlobJournal(transaction, id);
1175     if (!s.ok())
1176       return s;
1177   } else {
1178     s = MergeDatabaseIntoRecoveryBlobJournal(transaction, id);
1179     if (!s.ok())
1180       return s;
1181     need_cleanup = true;
1182   }
1183 
1184   bool sync_on_commit = false;
1185   s = transaction->Commit(sync_on_commit);
1186   if (!s.ok()) {
1187     INTERNAL_WRITE_ERROR(DELETE_DATABASE);
1188     return s;
1189   }
1190 
1191   // If another transaction is running, this will defer processing
1192   // the journal until completion.
1193   if (need_cleanup)
1194     CleanRecoveryJournalIgnoreReturn();
1195 
1196   return s;
1197 }
1198 
Compact()1199 void IndexedDBBackingStore::Compact() {
1200 #if DCHECK_IS_ON()
1201   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
1202   DCHECK(initialized_);
1203 #endif
1204   db_->CompactAll();
1205 }
1206 
GetRecord(IndexedDBBackingStore::Transaction * transaction,int64_t database_id,int64_t object_store_id,const IndexedDBKey & key,IndexedDBValue * record)1207 Status IndexedDBBackingStore::GetRecord(
1208     IndexedDBBackingStore::Transaction* transaction,
1209     int64_t database_id,
1210     int64_t object_store_id,
1211     const IndexedDBKey& key,
1212     IndexedDBValue* record) {
1213 #if DCHECK_IS_ON()
1214   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
1215   DCHECK(initialized_);
1216 #endif
1217 
1218   IDB_TRACE("IndexedDBBackingStore::GetRecord");
1219   if (!KeyPrefix::ValidIds(database_id, object_store_id))
1220     return InvalidDBKeyStatus();
1221   TransactionalLevelDBTransaction* leveldb_transaction =
1222       transaction->transaction();
1223 
1224   const std::string leveldb_key =
1225       ObjectStoreDataKey::Encode(database_id, object_store_id, key);
1226   std::string data;
1227 
1228   record->clear();
1229 
1230   bool found = false;
1231   Status s = leveldb_transaction->Get(leveldb_key, &data, &found);
1232   if (!s.ok()) {
1233     INTERNAL_READ_ERROR(GET_RECORD);
1234     return s;
1235   }
1236   if (!found)
1237     return s;
1238   if (data.empty()) {
1239     INTERNAL_READ_ERROR(GET_RECORD);
1240     return Status::NotFound("Record contained no data");
1241   }
1242 
1243   int64_t version;
1244   StringPiece slice(data);
1245   if (!DecodeVarInt(&slice, &version)) {
1246     INTERNAL_READ_ERROR(GET_RECORD);
1247     return InternalInconsistencyStatus();
1248   }
1249 
1250   record->bits = slice.as_string();
1251   return transaction->GetExternalObjectsForRecord(database_id, leveldb_key,
1252                                                   record);
1253 }
1254 
GetInMemoryBlobSize() const1255 int64_t IndexedDBBackingStore::GetInMemoryBlobSize() const {
1256   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
1257 
1258   int64_t total_size = 0;
1259   for (const auto& kvp : incognito_external_object_map_) {
1260     for (const IndexedDBExternalObject& object :
1261          kvp.second->external_objects()) {
1262       if (object.object_type() == IndexedDBExternalObject::ObjectType::kBlob) {
1263         total_size += object.size();
1264       }
1265     }
1266   }
1267   return total_size;
1268 }
1269 
PutRecord(IndexedDBBackingStore::Transaction * transaction,int64_t database_id,int64_t object_store_id,const IndexedDBKey & key,IndexedDBValue * value,RecordIdentifier * record_identifier)1270 Status IndexedDBBackingStore::PutRecord(
1271     IndexedDBBackingStore::Transaction* transaction,
1272     int64_t database_id,
1273     int64_t object_store_id,
1274     const IndexedDBKey& key,
1275     IndexedDBValue* value,
1276     RecordIdentifier* record_identifier) {
1277 #if DCHECK_IS_ON()
1278   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
1279   DCHECK(initialized_);
1280 #endif
1281 
1282   IDB_TRACE("IndexedDBBackingStore::PutRecord");
1283   if (!KeyPrefix::ValidIds(database_id, object_store_id))
1284     return InvalidDBKeyStatus();
1285   DCHECK(key.IsValid());
1286 
1287   TransactionalLevelDBTransaction* leveldb_transaction =
1288       transaction->transaction();
1289   int64_t version = -1;
1290   Status s = indexed_db::GetNewVersionNumber(leveldb_transaction, database_id,
1291                                              object_store_id, &version);
1292   if (!s.ok())
1293     return s;
1294   DCHECK_GE(version, 0);
1295   const std::string object_store_data_key =
1296       ObjectStoreDataKey::Encode(database_id, object_store_id, key);
1297 
1298   std::string v;
1299   EncodeVarInt(version, &v);
1300   v.append(value->bits);
1301 
1302   s = leveldb_transaction->Put(object_store_data_key, &v);
1303   if (!s.ok())
1304     return s;
1305   s = transaction->PutExternalObjectsIfNeeded(
1306       database_id, object_store_data_key, &value->external_objects);
1307   if (!s.ok())
1308     return s;
1309 
1310   const std::string exists_entry_key =
1311       ExistsEntryKey::Encode(database_id, object_store_id, key);
1312   std::string version_encoded;
1313   EncodeInt(version, &version_encoded);
1314   s = leveldb_transaction->Put(exists_entry_key, &version_encoded);
1315   if (!s.ok())
1316     return s;
1317 
1318   std::string key_encoded;
1319   EncodeIDBKey(key, &key_encoded);
1320   record_identifier->Reset(key_encoded, version);
1321   return s;
1322 }
1323 
ClearObjectStore(IndexedDBBackingStore::Transaction * transaction,int64_t database_id,int64_t object_store_id)1324 Status IndexedDBBackingStore::ClearObjectStore(
1325     IndexedDBBackingStore::Transaction* transaction,
1326     int64_t database_id,
1327     int64_t object_store_id) {
1328   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
1329 #if DCHECK_IS_ON()
1330   DCHECK(initialized_);
1331 #endif
1332 
1333   IDB_TRACE("IndexedDBBackingStore::ClearObjectStore");
1334   if (!KeyPrefix::ValidIds(database_id, object_store_id))
1335     return InvalidDBKeyStatus();
1336 
1337   Status s =
1338       DeleteBlobsInObjectStore(transaction, database_id, object_store_id);
1339   if (!s.ok()) {
1340     INTERNAL_WRITE_ERROR(CLEAR_OBJECT_STORE);
1341     return s;
1342   }
1343 
1344   // Don't delete the BlobEntryKeys so that they can be read and deleted
1345   // via CollectBlobFilesToRemove.
1346   // TODO(enne): This process could be optimized by storing the blob ids
1347   // in DeleteBlobsInObjectStore rather than re-reading them later.
1348   const std::string start_key1 =
1349       KeyPrefix(database_id, object_store_id).Encode();
1350   const std::string stop_key1 =
1351       BlobEntryKey::EncodeMinKeyForObjectStore(database_id, object_store_id);
1352   const std::string start_key2 =
1353       BlobEntryKey::EncodeStopKeyForObjectStore(database_id, object_store_id);
1354   const std::string stop_key2 =
1355       KeyPrefix(database_id, object_store_id + 1).Encode();
1356   s = transaction->transaction()->RemoveRange(
1357       start_key1, stop_key1,
1358       LevelDBScopeDeletionMode::kImmediateWithRangeEndExclusive);
1359   if (!s.ok())
1360     return s;
1361   return transaction->transaction()->RemoveRange(
1362       start_key2, stop_key2,
1363       LevelDBScopeDeletionMode::kImmediateWithRangeEndExclusive);
1364 }
1365 
DeleteRecord(IndexedDBBackingStore::Transaction * transaction,int64_t database_id,int64_t object_store_id,const RecordIdentifier & record_identifier)1366 Status IndexedDBBackingStore::DeleteRecord(
1367     IndexedDBBackingStore::Transaction* transaction,
1368     int64_t database_id,
1369     int64_t object_store_id,
1370     const RecordIdentifier& record_identifier) {
1371   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
1372 
1373   IDB_TRACE("IndexedDBBackingStore::DeleteRecord");
1374   if (!KeyPrefix::ValidIds(database_id, object_store_id))
1375     return InvalidDBKeyStatus();
1376   TransactionalLevelDBTransaction* leveldb_transaction =
1377       transaction->transaction();
1378 
1379   const std::string object_store_data_key = ObjectStoreDataKey::Encode(
1380       database_id, object_store_id, record_identifier.primary_key());
1381   Status s = leveldb_transaction->Remove(object_store_data_key);
1382   if (!s.ok())
1383     return s;
1384   s = transaction->PutExternalObjectsIfNeeded(database_id,
1385                                               object_store_data_key, nullptr);
1386   if (!s.ok())
1387     return s;
1388 
1389   const std::string exists_entry_key = ExistsEntryKey::Encode(
1390       database_id, object_store_id, record_identifier.primary_key());
1391   return leveldb_transaction->Remove(exists_entry_key);
1392 }
1393 
DeleteRange(IndexedDBBackingStore::Transaction * transaction,int64_t database_id,int64_t object_store_id,const IndexedDBKeyRange & key_range)1394 Status IndexedDBBackingStore::DeleteRange(
1395     IndexedDBBackingStore::Transaction* transaction,
1396     int64_t database_id,
1397     int64_t object_store_id,
1398     const IndexedDBKeyRange& key_range) {
1399 #if DCHECK_IS_ON()
1400   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
1401   DCHECK(initialized_);
1402 #endif
1403 
1404   // TODO(dmurph): Remove the need to create these cursors.
1405   // https://crbug.com/980678
1406   Status s;
1407   std::unique_ptr<IndexedDBBackingStore::Cursor> start_cursor =
1408       OpenObjectStoreCursor(transaction, database_id, object_store_id,
1409                             key_range, blink::mojom::IDBCursorDirection::Next,
1410                             &s);
1411   if (!s.ok())
1412     return s;
1413   if (!start_cursor)
1414     return Status::OK();  // Empty range == delete success.
1415   std::unique_ptr<IndexedDBBackingStore::Cursor> end_cursor =
1416       OpenObjectStoreCursor(transaction, database_id, object_store_id,
1417                             key_range, blink::mojom::IDBCursorDirection::Prev,
1418                             &s);
1419 
1420   if (!s.ok())
1421     return s;
1422   if (!end_cursor)
1423     return Status::OK();  // Empty range == delete success.
1424 
1425   BlobEntryKey start_blob_number, end_blob_number;
1426   std::string start_key = ObjectStoreDataKey::Encode(
1427       database_id, object_store_id, start_cursor->key());
1428   StringPiece start_key_piece(start_key);
1429   if (!BlobEntryKey::FromObjectStoreDataKey(&start_key_piece,
1430                                             &start_blob_number))
1431     return InternalInconsistencyStatus();
1432   std::string stop_key = ObjectStoreDataKey::Encode(
1433       database_id, object_store_id, end_cursor->key());
1434   StringPiece stop_key_piece(stop_key);
1435   if (!BlobEntryKey::FromObjectStoreDataKey(&stop_key_piece, &end_blob_number))
1436     return InternalInconsistencyStatus();
1437 
1438   s = DeleteBlobsInRange(transaction, database_id, start_blob_number.Encode(),
1439                          end_blob_number.Encode(), false);
1440   if (!s.ok())
1441     return s;
1442   s = transaction->transaction()->RemoveRange(
1443       start_key, stop_key,
1444       LevelDBScopeDeletionMode::kImmediateWithRangeEndInclusive);
1445   if (!s.ok())
1446     return s;
1447   start_key =
1448       ExistsEntryKey::Encode(database_id, object_store_id, start_cursor->key());
1449   stop_key =
1450       ExistsEntryKey::Encode(database_id, object_store_id, end_cursor->key());
1451 
1452   s = transaction->transaction()->RemoveRange(
1453       start_key, stop_key,
1454       LevelDBScopeDeletionMode::kImmediateWithRangeEndInclusive);
1455   return s;
1456 }
1457 
GetKeyGeneratorCurrentNumber(IndexedDBBackingStore::Transaction * transaction,int64_t database_id,int64_t object_store_id,int64_t * key_generator_current_number)1458 Status IndexedDBBackingStore::GetKeyGeneratorCurrentNumber(
1459     IndexedDBBackingStore::Transaction* transaction,
1460     int64_t database_id,
1461     int64_t object_store_id,
1462     int64_t* key_generator_current_number) {
1463 #if DCHECK_IS_ON()
1464   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
1465   DCHECK(initialized_);
1466 #endif
1467   if (!KeyPrefix::ValidIds(database_id, object_store_id))
1468     return InvalidDBKeyStatus();
1469   TransactionalLevelDBTransaction* leveldb_transaction =
1470       transaction->transaction();
1471 
1472   const std::string key_generator_current_number_key =
1473       ObjectStoreMetaDataKey::Encode(
1474           database_id, object_store_id,
1475           ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER);
1476 
1477   *key_generator_current_number = -1;
1478   std::string data;
1479 
1480   bool found = false;
1481   Status s =
1482       leveldb_transaction->Get(key_generator_current_number_key, &data, &found);
1483   if (!s.ok()) {
1484     INTERNAL_READ_ERROR(GET_KEY_GENERATOR_CURRENT_NUMBER);
1485     return s;
1486   }
1487   if (found && !data.empty()) {
1488     StringPiece slice(data);
1489     if (!DecodeInt(&slice, key_generator_current_number) || !slice.empty()) {
1490       INTERNAL_READ_ERROR(GET_KEY_GENERATOR_CURRENT_NUMBER);
1491       return InternalInconsistencyStatus();
1492     }
1493     return s;
1494   }
1495 
1496   // Previously, the key generator state was not stored explicitly
1497   // but derived from the maximum numeric key present in existing
1498   // data. This violates the spec as the data may be cleared but the
1499   // key generator state must be preserved.
1500   // TODO(jsbell): Fix this for all stores on database open?
1501   const std::string start_key =
1502       ObjectStoreDataKey::Encode(database_id, object_store_id, MinIDBKey());
1503   const std::string stop_key =
1504       ObjectStoreDataKey::Encode(database_id, object_store_id, MaxIDBKey());
1505 
1506   std::unique_ptr<TransactionalLevelDBIterator> it =
1507       leveldb_transaction->CreateIterator(s);
1508   if (!s.ok()) {
1509     INTERNAL_READ_ERROR(GET_KEY_GENERATOR_CURRENT_NUMBER);
1510     return s;
1511   }
1512   int64_t max_numeric_key = 0;
1513 
1514   for (s = it->Seek(start_key);
1515        s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0;
1516        s = it->Next()) {
1517     StringPiece slice(it->Key());
1518     ObjectStoreDataKey data_key;
1519     if (!ObjectStoreDataKey::Decode(&slice, &data_key) || !slice.empty()) {
1520       INTERNAL_READ_ERROR(GET_KEY_GENERATOR_CURRENT_NUMBER);
1521       return InternalInconsistencyStatus();
1522     }
1523     std::unique_ptr<IndexedDBKey> user_key = data_key.user_key();
1524     if (user_key->type() == blink::mojom::IDBKeyType::Number) {
1525       int64_t n = static_cast<int64_t>(user_key->number());
1526       if (n > max_numeric_key)
1527         max_numeric_key = n;
1528     }
1529   }
1530 
1531   if (s.ok())
1532     *key_generator_current_number = max_numeric_key + 1;
1533   else
1534     INTERNAL_READ_ERROR(GET_KEY_GENERATOR_CURRENT_NUMBER);
1535 
1536   return s;
1537 }
1538 
MaybeUpdateKeyGeneratorCurrentNumber(IndexedDBBackingStore::Transaction * transaction,int64_t database_id,int64_t object_store_id,int64_t new_number,bool check_current)1539 Status IndexedDBBackingStore::MaybeUpdateKeyGeneratorCurrentNumber(
1540     IndexedDBBackingStore::Transaction* transaction,
1541     int64_t database_id,
1542     int64_t object_store_id,
1543     int64_t new_number,
1544     bool check_current) {
1545 #if DCHECK_IS_ON()
1546   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
1547   DCHECK(initialized_);
1548 #endif
1549   if (!KeyPrefix::ValidIds(database_id, object_store_id))
1550     return InvalidDBKeyStatus();
1551 
1552   if (check_current) {
1553     int64_t current_number;
1554     Status s = GetKeyGeneratorCurrentNumber(transaction, database_id,
1555                                             object_store_id, &current_number);
1556     if (!s.ok())
1557       return s;
1558     if (new_number <= current_number)
1559       return s;
1560   }
1561 
1562   const std::string key_generator_current_number_key =
1563       ObjectStoreMetaDataKey::Encode(
1564           database_id, object_store_id,
1565           ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER);
1566   return PutInt(transaction->transaction(), key_generator_current_number_key,
1567                 new_number);
1568 }
1569 
KeyExistsInObjectStore(IndexedDBBackingStore::Transaction * transaction,int64_t database_id,int64_t object_store_id,const IndexedDBKey & key,RecordIdentifier * found_record_identifier,bool * found)1570 Status IndexedDBBackingStore::KeyExistsInObjectStore(
1571     IndexedDBBackingStore::Transaction* transaction,
1572     int64_t database_id,
1573     int64_t object_store_id,
1574     const IndexedDBKey& key,
1575     RecordIdentifier* found_record_identifier,
1576     bool* found) {
1577 #if DCHECK_IS_ON()
1578   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
1579   DCHECK(initialized_);
1580 #endif
1581   IDB_TRACE("IndexedDBBackingStore::KeyExistsInObjectStore");
1582   if (!KeyPrefix::ValidIds(database_id, object_store_id))
1583     return InvalidDBKeyStatus();
1584   *found = false;
1585   const std::string leveldb_key =
1586       ObjectStoreDataKey::Encode(database_id, object_store_id, key);
1587   std::string data;
1588 
1589   Status s = transaction->transaction()->Get(leveldb_key, &data, found);
1590   if (!s.ok()) {
1591     INTERNAL_READ_ERROR(KEY_EXISTS_IN_OBJECT_STORE);
1592     return s;
1593   }
1594   if (!*found)
1595     return Status::OK();
1596   if (data.empty()) {
1597     INTERNAL_READ_ERROR(KEY_EXISTS_IN_OBJECT_STORE);
1598     return InternalInconsistencyStatus();
1599   }
1600 
1601   int64_t version;
1602   StringPiece slice(data);
1603   if (!DecodeVarInt(&slice, &version))
1604     return InternalInconsistencyStatus();
1605 
1606   std::string encoded_key;
1607   EncodeIDBKey(key, &encoded_key);
1608   found_record_identifier->Reset(encoded_key, version);
1609   return s;
1610 }
1611 
ReportBlobUnused(int64_t database_id,int64_t blob_number)1612 void IndexedDBBackingStore::ReportBlobUnused(int64_t database_id,
1613                                              int64_t blob_number) {
1614   DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
1615 #if DCHECK_IS_ON()
1616   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
1617   DCHECK(initialized_);
1618 #endif
1619   bool all_blobs = blob_number == DatabaseMetaDataKey::kAllBlobsNumber;
1620   DCHECK(all_blobs || DatabaseMetaDataKey::IsValidBlobNumber(blob_number));
1621   std::unique_ptr<LevelDBDirectTransaction> transaction =
1622       transactional_leveldb_factory_->CreateLevelDBDirectTransaction(db_.get());
1623 
1624   BlobJournalType active_blob_journal, recovery_journal;
1625   if (!GetActiveBlobJournal(transaction.get(), &active_blob_journal).ok())
1626     return;
1627   DCHECK(!active_blob_journal.empty());
1628   if (!GetRecoveryBlobJournal(transaction.get(), &recovery_journal).ok())
1629     return;
1630 
1631   // There are several cases to handle.  If blob_number is kAllBlobsNumber, we
1632   // want to remove all entries with database_id from the active blob journal
1633   // and add only kAllBlobsNumber to the recovery journal.  Otherwise if
1634   // IsValidBlobNumber(blob_number) and we hit kAllBlobsNumber for the right
1635   // database_id in the journal, we leave the kAllBlobsNumber entry in the
1636   // active blob journal but add the specific blob to the recovery.  Otherwise
1637   // if IsValidBlobNumber(blob_number) and we find a matching (database_id,
1638   // blob_number) tuple, we should move it to the recovery journal.
1639   BlobJournalType new_active_blob_journal;
1640   for (auto journal_iter = active_blob_journal.begin();
1641        journal_iter != active_blob_journal.end(); ++journal_iter) {
1642     int64_t current_database_id = journal_iter->first;
1643     int64_t current_blob_number = journal_iter->second;
1644     bool current_all_blobs =
1645         current_blob_number == DatabaseMetaDataKey::kAllBlobsNumber;
1646     DCHECK(KeyPrefix::IsValidDatabaseId(current_database_id) ||
1647            current_all_blobs);
1648     if (current_database_id == database_id &&
1649         (all_blobs || current_all_blobs ||
1650          blob_number == current_blob_number)) {
1651       if (!all_blobs) {
1652         recovery_journal.push_back({database_id, current_blob_number});
1653         if (current_all_blobs)
1654           new_active_blob_journal.push_back(*journal_iter);
1655         new_active_blob_journal.insert(
1656             new_active_blob_journal.end(), ++journal_iter,
1657             active_blob_journal.end());  // All the rest.
1658         break;
1659       }
1660     } else {
1661       new_active_blob_journal.push_back(*journal_iter);
1662     }
1663   }
1664   if (all_blobs) {
1665     recovery_journal.push_back(
1666         {database_id, DatabaseMetaDataKey::kAllBlobsNumber});
1667   }
1668   UpdateRecoveryBlobJournal(transaction.get(), recovery_journal);
1669   UpdateActiveBlobJournal(transaction.get(), new_active_blob_journal);
1670   transaction->Commit();
1671   // We could just do the deletions/cleaning here, but if there are a lot of
1672   // blobs about to be garbage collected, it'd be better to wait and do them all
1673   // at once.
1674   StartJournalCleaningTimer();
1675 }
1676 
1677 // The this reference is a raw pointer that's declared Unretained inside the
1678 // timer code, so this won't confuse IndexedDBFactory's check for
1679 // HasLastBackingStoreReference.  It's safe because if the backing store is
1680 // deleted, the timer will automatically be canceled on destruction.
StartJournalCleaningTimer()1681 void IndexedDBBackingStore::StartJournalCleaningTimer() {
1682 #if DCHECK_IS_ON()
1683   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
1684   DCHECK(initialized_);
1685 #endif
1686   ++num_aggregated_journal_cleaning_requests_;
1687 
1688   if (execute_journal_cleaning_on_no_txns_)
1689     return;
1690 
1691   if (num_aggregated_journal_cleaning_requests_ >= kMaxJournalCleanRequests) {
1692     journal_cleaning_timer_.AbandonAndStop();
1693     CleanRecoveryJournalIgnoreReturn();
1694     return;
1695   }
1696 
1697   base::TimeTicks now = base::TimeTicks::Now();
1698 
1699   if (journal_cleaning_timer_window_start_ == base::TimeTicks() ||
1700       !journal_cleaning_timer_.IsRunning()) {
1701     journal_cleaning_timer_window_start_ = now;
1702   }
1703 
1704   base::TimeDelta time_until_max = kMaxJournalCleaningWindowTime -
1705                                    (now - journal_cleaning_timer_window_start_);
1706   base::TimeDelta delay =
1707       std::min(kInitialJournalCleaningWindowTime, time_until_max);
1708 
1709   if (delay <= base::TimeDelta::FromSeconds(0)) {
1710     journal_cleaning_timer_.AbandonAndStop();
1711     CleanRecoveryJournalIgnoreReturn();
1712     return;
1713   }
1714 
1715   journal_cleaning_timer_.Start(
1716       FROM_HERE, delay, this,
1717       &IndexedDBBackingStore::CleanRecoveryJournalIgnoreReturn);
1718 }
1719 
1720 // This assumes a file path of dbId/second-to-LSB-of-counter/counter.
GetBlobFileName(int64_t database_id,int64_t blob_number) const1721 FilePath IndexedDBBackingStore::GetBlobFileName(int64_t database_id,
1722                                                 int64_t blob_number) const {
1723   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
1724   return GetBlobFileNameForKey(blob_path_, database_id, blob_number);
1725 }
1726 
RemoveBlobFile(int64_t database_id,int64_t blob_number) const1727 bool IndexedDBBackingStore::RemoveBlobFile(int64_t database_id,
1728                                            int64_t blob_number) const {
1729   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
1730   DCHECK(filesystem_proxy_) << "Only call this for on disk databases";
1731   FilePath path = GetBlobFileName(database_id, blob_number);
1732 #if DCHECK_IS_ON()
1733   ++num_blob_files_deleted_;
1734   DVLOG(1) << "Deleting blob " << blob_number << " from IndexedDB database "
1735            << database_id << " at path " << path.value();
1736 #endif
1737   return filesystem_proxy_->DeleteFile(path);
1738 }
1739 
RemoveBlobDirectory(int64_t database_id) const1740 bool IndexedDBBackingStore::RemoveBlobDirectory(int64_t database_id) const {
1741   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
1742   DCHECK(filesystem_proxy_) << "Only call this for on disk databases";
1743   FilePath path = GetBlobDirectoryName(blob_path_, database_id);
1744   return filesystem_proxy_->DeletePathRecursively(path);
1745 }
1746 
CleanUpBlobJournal(const std::string & level_db_key) const1747 Status IndexedDBBackingStore::CleanUpBlobJournal(
1748     const std::string& level_db_key) const {
1749   IDB_TRACE("IndexedDBBackingStore::CleanUpBlobJournal");
1750   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
1751   DCHECK(!committing_transaction_count_);
1752   std::unique_ptr<LevelDBDirectTransaction> journal_transaction =
1753       transactional_leveldb_factory_->CreateLevelDBDirectTransaction(db_.get());
1754   BlobJournalType journal;
1755 
1756   Status s = GetBlobJournal(level_db_key, journal_transaction.get(), &journal);
1757   if (!s.ok())
1758     return s;
1759   if (journal.empty())
1760     return Status::OK();
1761   s = CleanUpBlobJournalEntries(journal);
1762   if (!s.ok())
1763     return s;
1764   ClearBlobJournal(journal_transaction.get(), level_db_key);
1765   s = journal_transaction->Commit();
1766   // Notify blob files cleaned even if commit fails, as files could still be
1767   // deleted.
1768   if (!is_incognito())
1769     blob_files_cleaned_.Run();
1770   return s;
1771 }
1772 
CleanUpBlobJournalEntries(const BlobJournalType & journal) const1773 Status IndexedDBBackingStore::CleanUpBlobJournalEntries(
1774     const BlobJournalType& journal) const {
1775   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
1776   IDB_TRACE("IndexedDBBackingStore::CleanUpBlobJournalEntries");
1777   if (journal.empty())
1778     return Status::OK();
1779   if (!filesystem_proxy_)
1780     return Status::OK();
1781   for (const auto& entry : journal) {
1782     int64_t database_id = entry.first;
1783     int64_t blob_number = entry.second;
1784     DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
1785     if (blob_number == DatabaseMetaDataKey::kAllBlobsNumber) {
1786       if (!RemoveBlobDirectory(database_id))
1787         return IOErrorStatus();
1788     } else {
1789       DCHECK(DatabaseMetaDataKey::IsValidBlobNumber(blob_number));
1790       if (!RemoveBlobFile(database_id, blob_number))
1791         return IOErrorStatus();
1792     }
1793   }
1794   return Status::OK();
1795 }
1796 
WillCommitTransaction()1797 void IndexedDBBackingStore::WillCommitTransaction() {
1798   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
1799   ++committing_transaction_count_;
1800 }
1801 
DidCommitTransaction()1802 void IndexedDBBackingStore::DidCommitTransaction() {
1803   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
1804   DCHECK_GT(committing_transaction_count_, 0UL);
1805   --committing_transaction_count_;
1806   if (committing_transaction_count_ == 0 &&
1807       execute_journal_cleaning_on_no_txns_) {
1808     execute_journal_cleaning_on_no_txns_ = false;
1809     CleanRecoveryJournalIgnoreReturn();
1810   }
1811 }
1812 
GetExternalObjectsForRecord(int64_t database_id,const std::string & object_store_data_key,IndexedDBValue * value)1813 Status IndexedDBBackingStore::Transaction::GetExternalObjectsForRecord(
1814     int64_t database_id,
1815     const std::string& object_store_data_key,
1816     IndexedDBValue* value) {
1817   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
1818   IndexedDBExternalObjectChangeRecord* change_record = nullptr;
1819   auto object_iter = external_object_change_map_.find(object_store_data_key);
1820   if (object_iter != external_object_change_map_.end()) {
1821     change_record = object_iter->second.get();
1822   } else {
1823     object_iter = incognito_external_object_map_.find(object_store_data_key);
1824     if (object_iter != incognito_external_object_map_.end())
1825       change_record = object_iter->second.get();
1826   }
1827   if (change_record) {
1828     // Either we haven't written the blob to disk yet or we're in incognito
1829     // mode, so we have to send back the one they sent us.  This change record
1830     // includes the original UUID.
1831     value->external_objects = change_record->external_objects();
1832     return Status::OK();
1833   }
1834 
1835   BlobEntryKey blob_entry_key;
1836   StringPiece leveldb_key_piece(object_store_data_key);
1837   if (!BlobEntryKey::FromObjectStoreDataKey(&leveldb_key_piece,
1838                                             &blob_entry_key)) {
1839     NOTREACHED();
1840     return InternalInconsistencyStatus();
1841   }
1842   std::string encoded_key = blob_entry_key.Encode();
1843   bool found;
1844   std::string encoded_value;
1845   Status s = transaction()->Get(encoded_key, &encoded_value, &found);
1846   if (!s.ok())
1847     return s;
1848   if (found) {
1849     if (!DecodeExternalObjects(encoded_value, &value->external_objects)) {
1850       INTERNAL_READ_ERROR(GET_BLOB_INFO_FOR_RECORD);
1851       return InternalInconsistencyStatus();
1852     }
1853     for (auto& entry : value->external_objects) {
1854       switch (entry.object_type()) {
1855         case IndexedDBExternalObject::ObjectType::kFile:
1856         case IndexedDBExternalObject::ObjectType::kBlob:
1857           entry.set_indexed_db_file_path(backing_store_->GetBlobFileName(
1858               database_id, entry.blob_number()));
1859           entry.set_mark_used_callback(
1860               backing_store_->active_blob_registry()->GetMarkBlobActiveCallback(
1861                   database_id, entry.blob_number()));
1862           entry.set_release_callback(
1863               backing_store_->active_blob_registry()->GetFinalReleaseCallback(
1864                   database_id, entry.blob_number()));
1865           break;
1866         case IndexedDBExternalObject::ObjectType::kNativeFileSystemHandle:
1867           break;
1868       }
1869     }
1870   }
1871   return Status::OK();
1872 }
1873 
1874 base::WeakPtr<IndexedDBBackingStore::Transaction>
AsWeakPtr()1875 IndexedDBBackingStore::Transaction::AsWeakPtr() {
1876   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
1877   return ptr_factory_.GetWeakPtr();
1878 }
1879 
CleanRecoveryJournalIgnoreReturn()1880 void IndexedDBBackingStore::CleanRecoveryJournalIgnoreReturn() {
1881   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
1882   // While a transaction is busy it is not safe to clean the journal.
1883   if (committing_transaction_count_ > 0) {
1884     execute_journal_cleaning_on_no_txns_ = true;
1885     return;
1886   }
1887   num_aggregated_journal_cleaning_requests_ = 0;
1888   CleanUpBlobJournal(RecoveryBlobJournalKey::Encode());
1889 }
1890 
ClearIndex(IndexedDBBackingStore::Transaction * transaction,int64_t database_id,int64_t object_store_id,int64_t index_id)1891 Status IndexedDBBackingStore::ClearIndex(
1892     IndexedDBBackingStore::Transaction* transaction,
1893     int64_t database_id,
1894     int64_t object_store_id,
1895     int64_t index_id) {
1896   IDB_TRACE("IndexedDBBackingStore::ClearIndex");
1897 #if DCHECK_IS_ON()
1898   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
1899   DCHECK(initialized_);
1900 #endif
1901   if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
1902     return InvalidDBKeyStatus();
1903   TransactionalLevelDBTransaction* leveldb_transaction =
1904       transaction->transaction();
1905 
1906   const std::string index_data_start =
1907       IndexDataKey::EncodeMinKey(database_id, object_store_id, index_id);
1908   const std::string index_data_end =
1909       IndexDataKey::EncodeMaxKey(database_id, object_store_id, index_id);
1910   Status s = leveldb_transaction->RemoveRange(
1911       index_data_start, index_data_end,
1912       LevelDBScopeDeletionMode::kImmediateWithRangeEndInclusive);
1913 
1914   if (!s.ok())
1915     INTERNAL_WRITE_ERROR(DELETE_INDEX);
1916 
1917   return s;
1918 }
1919 
PutIndexDataForRecord(IndexedDBBackingStore::Transaction * transaction,int64_t database_id,int64_t object_store_id,int64_t index_id,const IndexedDBKey & key,const RecordIdentifier & record_identifier)1920 Status IndexedDBBackingStore::PutIndexDataForRecord(
1921     IndexedDBBackingStore::Transaction* transaction,
1922     int64_t database_id,
1923     int64_t object_store_id,
1924     int64_t index_id,
1925     const IndexedDBKey& key,
1926     const RecordIdentifier& record_identifier) {
1927   IDB_TRACE("IndexedDBBackingStore::PutIndexDataForRecord");
1928 #if DCHECK_IS_ON()
1929   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
1930   DCHECK(initialized_);
1931 #endif
1932   DCHECK(key.IsValid());
1933   if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
1934     return InvalidDBKeyStatus();
1935 
1936   std::string encoded_key;
1937   EncodeIDBKey(key, &encoded_key);
1938 
1939   const std::string index_data_key =
1940       IndexDataKey::Encode(database_id, object_store_id, index_id, encoded_key,
1941                            record_identifier.primary_key(), 0);
1942 
1943   std::string data;
1944   EncodeVarInt(record_identifier.version(), &data);
1945   data.append(record_identifier.primary_key());
1946 
1947   return transaction->transaction()->Put(index_data_key, &data);
1948 }
1949 
FindKeyInIndex(IndexedDBBackingStore::Transaction * transaction,int64_t database_id,int64_t object_store_id,int64_t index_id,const IndexedDBKey & key,std::string * found_encoded_primary_key,bool * found)1950 Status IndexedDBBackingStore::FindKeyInIndex(
1951     IndexedDBBackingStore::Transaction* transaction,
1952     int64_t database_id,
1953     int64_t object_store_id,
1954     int64_t index_id,
1955     const IndexedDBKey& key,
1956     std::string* found_encoded_primary_key,
1957     bool* found) {
1958   IDB_TRACE("IndexedDBBackingStore::FindKeyInIndex");
1959 #if DCHECK_IS_ON()
1960   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
1961   DCHECK(initialized_);
1962 #endif
1963   DCHECK(KeyPrefix::ValidIds(database_id, object_store_id, index_id));
1964 
1965   DCHECK(found_encoded_primary_key->empty());
1966   *found = false;
1967 
1968   TransactionalLevelDBTransaction* leveldb_transaction =
1969       transaction->transaction();
1970   const std::string leveldb_key =
1971       IndexDataKey::Encode(database_id, object_store_id, index_id, key);
1972   Status s;
1973   std::unique_ptr<TransactionalLevelDBIterator> it =
1974       leveldb_transaction->CreateIterator(s);
1975   if (!s.ok()) {
1976     INTERNAL_WRITE_ERROR(CREATE_ITERATOR);
1977     return s;
1978   }
1979   s = it->Seek(leveldb_key);
1980   if (!s.ok()) {
1981     INTERNAL_READ_ERROR(FIND_KEY_IN_INDEX);
1982     return s;
1983   }
1984 
1985   for (;;) {
1986     if (!it->IsValid())
1987       return Status::OK();
1988     if (CompareIndexKeys(it->Key(), leveldb_key) > 0)
1989       return Status::OK();
1990 
1991     StringPiece slice(it->Value());
1992 
1993     int64_t version;
1994     if (!DecodeVarInt(&slice, &version)) {
1995       INTERNAL_READ_ERROR(FIND_KEY_IN_INDEX);
1996       return InternalInconsistencyStatus();
1997     }
1998     *found_encoded_primary_key = slice.as_string();
1999 
2000     bool exists = false;
2001     s = indexed_db::VersionExists(leveldb_transaction, database_id,
2002                                   object_store_id, version,
2003                                   *found_encoded_primary_key, &exists);
2004     if (!s.ok())
2005       return s;
2006     if (!exists) {
2007       // Delete stale index data entry and continue.
2008       s = leveldb_transaction->Remove(it->Key());
2009       if (!s.ok())
2010         return s;
2011       s = it->Next();
2012       continue;
2013     }
2014     *found = true;
2015     return s;
2016   }
2017 }
2018 
GetPrimaryKeyViaIndex(IndexedDBBackingStore::Transaction * transaction,int64_t database_id,int64_t object_store_id,int64_t index_id,const IndexedDBKey & key,std::unique_ptr<IndexedDBKey> * primary_key)2019 Status IndexedDBBackingStore::GetPrimaryKeyViaIndex(
2020     IndexedDBBackingStore::Transaction* transaction,
2021     int64_t database_id,
2022     int64_t object_store_id,
2023     int64_t index_id,
2024     const IndexedDBKey& key,
2025     std::unique_ptr<IndexedDBKey>* primary_key) {
2026   IDB_TRACE("IndexedDBBackingStore::GetPrimaryKeyViaIndex");
2027 #if DCHECK_IS_ON()
2028   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
2029   DCHECK(initialized_);
2030 #endif
2031   if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
2032     return InvalidDBKeyStatus();
2033 
2034   bool found = false;
2035   std::string found_encoded_primary_key;
2036   Status s = FindKeyInIndex(transaction, database_id, object_store_id, index_id,
2037                             key, &found_encoded_primary_key, &found);
2038   if (!s.ok()) {
2039     INTERNAL_READ_ERROR(GET_PRIMARY_KEY_VIA_INDEX);
2040     return s;
2041   }
2042   if (!found)
2043     return s;
2044   if (found_encoded_primary_key.empty()) {
2045     INTERNAL_READ_ERROR(GET_PRIMARY_KEY_VIA_INDEX);
2046     return InvalidDBKeyStatus();
2047   }
2048 
2049   StringPiece slice(found_encoded_primary_key);
2050   if (DecodeIDBKey(&slice, primary_key) && slice.empty())
2051     return s;
2052   else
2053     return InvalidDBKeyStatus();
2054 }
2055 
KeyExistsInIndex(IndexedDBBackingStore::Transaction * transaction,int64_t database_id,int64_t object_store_id,int64_t index_id,const IndexedDBKey & index_key,std::unique_ptr<IndexedDBKey> * found_primary_key,bool * exists)2056 Status IndexedDBBackingStore::KeyExistsInIndex(
2057     IndexedDBBackingStore::Transaction* transaction,
2058     int64_t database_id,
2059     int64_t object_store_id,
2060     int64_t index_id,
2061     const IndexedDBKey& index_key,
2062     std::unique_ptr<IndexedDBKey>* found_primary_key,
2063     bool* exists) {
2064   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
2065   IDB_TRACE("IndexedDBBackingStore::KeyExistsInIndex");
2066 #if DCHECK_IS_ON()
2067   DCHECK(initialized_);
2068 #endif
2069   if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
2070     return InvalidDBKeyStatus();
2071 
2072   *exists = false;
2073   std::string found_encoded_primary_key;
2074   Status s = FindKeyInIndex(transaction, database_id, object_store_id, index_id,
2075                             index_key, &found_encoded_primary_key, exists);
2076   if (!s.ok()) {
2077     INTERNAL_READ_ERROR(KEY_EXISTS_IN_INDEX);
2078     return s;
2079   }
2080   if (!*exists)
2081     return Status::OK();
2082   if (found_encoded_primary_key.empty()) {
2083     INTERNAL_READ_ERROR(KEY_EXISTS_IN_INDEX);
2084     return InvalidDBKeyStatus();
2085   }
2086 
2087   StringPiece slice(found_encoded_primary_key);
2088   if (DecodeIDBKey(&slice, found_primary_key) && slice.empty())
2089     return s;
2090   else
2091     return InvalidDBKeyStatus();
2092 }
2093 
Cursor(const IndexedDBBackingStore::Cursor * other,std::unique_ptr<TransactionalLevelDBIterator> iterator)2094 IndexedDBBackingStore::Cursor::Cursor(
2095     const IndexedDBBackingStore::Cursor* other,
2096     std::unique_ptr<TransactionalLevelDBIterator> iterator)
2097     : transaction_(other->transaction_),
2098       database_id_(other->database_id_),
2099       cursor_options_(other->cursor_options_),
2100       iterator_(std::move(iterator)),
2101       current_key_(std::make_unique<IndexedDBKey>(*other->current_key_)) {
2102   DCHECK(transaction_);
2103   DCHECK(iterator_);
2104 }
2105 
Cursor(base::WeakPtr<Transaction> transaction,int64_t database_id,const CursorOptions & cursor_options)2106 IndexedDBBackingStore::Cursor::Cursor(base::WeakPtr<Transaction> transaction,
2107                                       int64_t database_id,
2108                                       const CursorOptions& cursor_options)
2109     : transaction_(std::move(transaction)),
2110       database_id_(database_id),
2111       cursor_options_(cursor_options) {
2112   DCHECK(transaction_);
2113 }
2114 
~Cursor()2115 IndexedDBBackingStore::Cursor::~Cursor() {}
2116 
2117 std::unique_ptr<TransactionalLevelDBIterator>
CloneIterator(const IndexedDBBackingStore::Cursor * other)2118 IndexedDBBackingStore::Cursor::CloneIterator(
2119     const IndexedDBBackingStore::Cursor* other) {
2120   if (!other)
2121     return nullptr;
2122   if (!other->iterator_)
2123     return nullptr;
2124 
2125   Status s;
2126   auto iter = other->transaction_->transaction()->CreateIterator(s);
2127   if (!s.ok()) {
2128     INTERNAL_WRITE_ERROR(CREATE_ITERATOR);
2129     return nullptr;
2130   }
2131 
2132   if (other->iterator_->IsValid()) {
2133     s = iter->Seek(other->iterator_->Key());
2134     // TODO(cmumford): Handle this error (crbug.com/363397)
2135     DCHECK(iter->IsValid());
2136   }
2137 
2138   return iter;
2139 }
2140 
FirstSeek(Status * s)2141 bool IndexedDBBackingStore::Cursor::FirstSeek(Status* s) {
2142   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
2143   DCHECK(transaction_);
2144   DCHECK(s);
2145   iterator_ = transaction_->transaction()->CreateIterator(*s);
2146   if (!s->ok()) {
2147     INTERNAL_WRITE_ERROR(CREATE_ITERATOR);
2148     return false;
2149   }
2150 
2151   {
2152     IDB_TRACE("IndexedDBBackingStore::Cursor::FirstSeek::Seek");
2153     if (cursor_options_.forward)
2154       *s = iterator_->Seek(cursor_options_.low_key);
2155     else
2156       *s = iterator_->Seek(cursor_options_.high_key);
2157     if (!s->ok())
2158       return false;
2159   }
2160   return Continue(nullptr, READY, s);
2161 }
2162 
Advance(uint32_t count,Status * s)2163 bool IndexedDBBackingStore::Cursor::Advance(uint32_t count, Status* s) {
2164   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
2165   *s = Status::OK();
2166   while (count--) {
2167     if (!Continue(s))
2168       return false;
2169   }
2170   return true;
2171 }
2172 
Continue(const IndexedDBKey * key,const IndexedDBKey * primary_key,IteratorState next_state,Status * s)2173 bool IndexedDBBackingStore::Cursor::Continue(const IndexedDBKey* key,
2174                                              const IndexedDBKey* primary_key,
2175                                              IteratorState next_state,
2176                                              Status* s) {
2177   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
2178   IDB_TRACE("IndexedDBBackingStore::Cursor::Continue");
2179   DCHECK(!key || next_state == SEEK);
2180 
2181   if (cursor_options_.forward)
2182     return ContinueNext(key, primary_key, next_state, s) ==
2183            ContinueResult::DONE;
2184   else
2185     return ContinuePrevious(key, primary_key, next_state, s) ==
2186            ContinueResult::DONE;
2187 }
2188 
2189 IndexedDBBackingStore::Cursor::ContinueResult
ContinueNext(const IndexedDBKey * key,const IndexedDBKey * primary_key,IteratorState next_state,Status * s)2190 IndexedDBBackingStore::Cursor::ContinueNext(const IndexedDBKey* key,
2191                                             const IndexedDBKey* primary_key,
2192                                             IteratorState next_state,
2193                                             Status* s) {
2194   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
2195   DCHECK(cursor_options_.forward);
2196   DCHECK(!key || key->IsValid());
2197   DCHECK(!primary_key || primary_key->IsValid());
2198   *s = Status::OK();
2199 
2200   // TODO(alecflett): avoid a copy here?
2201   IndexedDBKey previous_key = current_key_ ? *current_key_ : IndexedDBKey();
2202 
2203   // If seeking to a particular key (or key and primary key), skip the cursor
2204   // forward rather than iterating it.
2205   if (next_state == SEEK && key) {
2206     std::string leveldb_key =
2207         primary_key ? EncodeKey(*key, *primary_key) : EncodeKey(*key);
2208     *s = iterator_->Seek(leveldb_key);
2209     if (!s->ok())
2210       return ContinueResult::LEVELDB_ERROR;
2211     // Cursor is at the next value already; don't advance it again below.
2212     next_state = READY;
2213   }
2214 
2215   for (;;) {
2216     // Only advance the cursor if it was not set to position already, either
2217     // because it is newly opened (and positioned at start of range) or
2218     // skipped forward by continue with a specific key.
2219     if (next_state == SEEK) {
2220       *s = iterator_->Next();
2221       if (!s->ok())
2222         return ContinueResult::LEVELDB_ERROR;
2223     } else {
2224       next_state = SEEK;
2225     }
2226 
2227     // Fail if we've run out of data or gone past the cursor's bounds.
2228     if (!iterator_->IsValid() || IsPastBounds())
2229       return ContinueResult::OUT_OF_BOUNDS;
2230 
2231     // TODO(jsbell): Document why this might be false. When do we ever not
2232     // seek into the range before starting cursor iteration?
2233     if (!HaveEnteredRange())
2234       continue;
2235 
2236     // The row may not load because there's a stale entry in the index. If no
2237     // error then not fatal.
2238     if (!LoadCurrentRow(s)) {
2239       if (!s->ok())
2240         return ContinueResult::LEVELDB_ERROR;
2241       continue;
2242     }
2243 
2244     // Cursor is now positioned at a non-stale record in range.
2245 
2246     // "Unique" cursors should continue seeking until a new key value is seen.
2247     if (cursor_options_.unique && previous_key.IsValid() &&
2248         current_key_->Equals(previous_key)) {
2249       continue;
2250     }
2251 
2252     break;
2253   }
2254 
2255   return ContinueResult::DONE;
2256 }
2257 
2258 IndexedDBBackingStore::Cursor::ContinueResult
ContinuePrevious(const IndexedDBKey * key,const IndexedDBKey * primary_key,IteratorState next_state,Status * s)2259 IndexedDBBackingStore::Cursor::ContinuePrevious(const IndexedDBKey* key,
2260                                                 const IndexedDBKey* primary_key,
2261                                                 IteratorState next_state,
2262                                                 Status* s) {
2263   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
2264   DCHECK(!cursor_options_.forward);
2265   DCHECK(!key || key->IsValid());
2266   DCHECK(!primary_key || primary_key->IsValid());
2267   *s = Status::OK();
2268 
2269   // TODO(alecflett): avoid a copy here?
2270   IndexedDBKey previous_key = current_key_ ? *current_key_ : IndexedDBKey();
2271 
2272   // When iterating with PrevNoDuplicate, spec requires that the value we
2273   // yield for each key is the *first* duplicate in forwards order. We do this
2274   // by remembering the duplicate key (implicitly, the first record seen with
2275   // a new key), keeping track of the earliest duplicate seen, and continuing
2276   // until yet another new key is seen, at which point the earliest duplicate
2277   // is the correct cursor position.
2278   IndexedDBKey duplicate_key;
2279   std::string earliest_duplicate;
2280 
2281   // TODO(jsbell): Optimize continuing to a specific key (or key and primary
2282   // key) for reverse cursors as well. See Seek() optimization at the start of
2283   // ContinueNext() for an example.
2284 
2285   for (;;) {
2286     if (next_state == SEEK) {
2287       *s = iterator_->Prev();
2288       if (!s->ok())
2289         return ContinueResult::LEVELDB_ERROR;
2290     } else {
2291       next_state = SEEK;  // for subsequent iterations
2292     }
2293 
2294     // If we've run out of data or gone past the cursor's bounds.
2295     if (!iterator_->IsValid() || IsPastBounds()) {
2296       if (duplicate_key.IsValid())
2297         break;
2298       return ContinueResult::OUT_OF_BOUNDS;
2299     }
2300 
2301     // TODO(jsbell): Document why this might be false. When do we ever not
2302     // seek into the range before starting cursor iteration?
2303     if (!HaveEnteredRange())
2304       continue;
2305 
2306     // The row may not load because there's a stale entry in the index. If no
2307     // error then not fatal.
2308     if (!LoadCurrentRow(s)) {
2309       if (!s->ok())
2310         return ContinueResult::LEVELDB_ERROR;
2311       continue;
2312     }
2313 
2314     // If seeking to a key (or key and primary key), continue until found.
2315     // TODO(jsbell): If Seek() optimization is added above, remove this.
2316     if (key) {
2317       if (primary_key && key->Equals(*current_key_) &&
2318           primary_key->IsLessThan(this->primary_key()))
2319         continue;
2320       if (key->IsLessThan(*current_key_))
2321         continue;
2322     }
2323 
2324     // Cursor is now positioned at a non-stale record in range.
2325 
2326     if (cursor_options_.unique) {
2327       // If entry is a duplicate of the previous, keep going. Although the
2328       // cursor should be positioned at the first duplicate already, new
2329       // duplicates may have been inserted since the cursor was last iterated,
2330       // and should be skipped to maintain "unique" iteration.
2331       if (previous_key.IsValid() && current_key_->Equals(previous_key))
2332         continue;
2333 
2334       // If we've found a new key, remember it and keep going.
2335       if (!duplicate_key.IsValid()) {
2336         duplicate_key = *current_key_;
2337         earliest_duplicate = iterator_->Key().as_string();
2338         continue;
2339       }
2340 
2341       // If we're still seeing duplicates, keep going.
2342       if (duplicate_key.Equals(*current_key_)) {
2343         earliest_duplicate = iterator_->Key().as_string();
2344         continue;
2345       }
2346     }
2347 
2348     break;
2349   }
2350 
2351   if (cursor_options_.unique) {
2352     DCHECK(duplicate_key.IsValid());
2353     DCHECK(!earliest_duplicate.empty());
2354 
2355     *s = iterator_->Seek(earliest_duplicate);
2356     if (!s->ok())
2357       return ContinueResult::LEVELDB_ERROR;
2358     if (!LoadCurrentRow(s)) {
2359       DCHECK(!s->ok());
2360       return ContinueResult::LEVELDB_ERROR;
2361     }
2362   }
2363 
2364   return ContinueResult::DONE;
2365 }
2366 
HaveEnteredRange() const2367 bool IndexedDBBackingStore::Cursor::HaveEnteredRange() const {
2368   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
2369   if (cursor_options_.forward) {
2370     int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.low_key);
2371     if (cursor_options_.low_open) {
2372       return compare > 0;
2373     }
2374     return compare >= 0;
2375   }
2376   int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.high_key);
2377   if (cursor_options_.high_open) {
2378     return compare < 0;
2379   }
2380   return compare <= 0;
2381 }
2382 
IsPastBounds() const2383 bool IndexedDBBackingStore::Cursor::IsPastBounds() const {
2384   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
2385   if (cursor_options_.forward) {
2386     int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.high_key);
2387     if (cursor_options_.high_open) {
2388       return compare >= 0;
2389     }
2390     return compare > 0;
2391   }
2392   int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.low_key);
2393   if (cursor_options_.low_open) {
2394     return compare <= 0;
2395   }
2396   return compare < 0;
2397 }
2398 
primary_key() const2399 const IndexedDBKey& IndexedDBBackingStore::Cursor::primary_key() const {
2400   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
2401   return *current_key_;
2402 }
2403 
2404 class ObjectStoreKeyCursorImpl : public IndexedDBBackingStore::Cursor {
2405  public:
ObjectStoreKeyCursorImpl(base::WeakPtr<IndexedDBBackingStore::Transaction> transaction,int64_t database_id,const IndexedDBBackingStore::Cursor::CursorOptions & cursor_options)2406   ObjectStoreKeyCursorImpl(
2407       base::WeakPtr<IndexedDBBackingStore::Transaction> transaction,
2408       int64_t database_id,
2409       const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
2410       : IndexedDBBackingStore::Cursor(std::move(transaction),
2411                                       database_id,
2412                                       cursor_options) {}
2413 
Clone() const2414   std::unique_ptr<Cursor> Clone() const override {
2415     DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
2416     auto iter = CloneIterator(this);
2417     if (!iter)
2418       return nullptr;
2419     return base::WrapUnique(
2420         new ObjectStoreKeyCursorImpl(this, std::move(iter)));
2421   }
2422 
2423   // IndexedDBBackingStore::Cursor
value()2424   IndexedDBValue* value() override {
2425     NOTREACHED();
2426     return nullptr;
2427   }
2428   bool LoadCurrentRow(Status* s) override;
2429 
2430  protected:
EncodeKey(const IndexedDBKey & key)2431   std::string EncodeKey(const IndexedDBKey& key) override {
2432     DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
2433     return ObjectStoreDataKey::Encode(cursor_options_.database_id,
2434                                       cursor_options_.object_store_id, key);
2435   }
EncodeKey(const IndexedDBKey & key,const IndexedDBKey & primary_key)2436   std::string EncodeKey(const IndexedDBKey& key,
2437                         const IndexedDBKey& primary_key) override {
2438     NOTREACHED();
2439     return std::string();
2440   }
2441 
2442  private:
ObjectStoreKeyCursorImpl(const ObjectStoreKeyCursorImpl * other,std::unique_ptr<TransactionalLevelDBIterator> iterator)2443   explicit ObjectStoreKeyCursorImpl(
2444       const ObjectStoreKeyCursorImpl* other,
2445       std::unique_ptr<TransactionalLevelDBIterator> iterator)
2446       : IndexedDBBackingStore::Cursor(other, std::move(iterator)) {}
2447 
2448   DISALLOW_COPY_AND_ASSIGN(ObjectStoreKeyCursorImpl);
2449 };
2450 
CursorOptions()2451 IndexedDBBackingStore::Cursor::CursorOptions::CursorOptions() {}
2452 IndexedDBBackingStore::Cursor::CursorOptions::CursorOptions(
2453     const CursorOptions& other) = default;
~CursorOptions()2454 IndexedDBBackingStore::Cursor::CursorOptions::~CursorOptions() {}
2455 const IndexedDBBackingStore::RecordIdentifier&
record_identifier() const2456 IndexedDBBackingStore::Cursor::record_identifier() const {
2457   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
2458   return record_identifier_;
2459 }
2460 
LoadCurrentRow(Status * s)2461 bool ObjectStoreKeyCursorImpl::LoadCurrentRow(Status* s) {
2462   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
2463   StringPiece slice(iterator_->Key());
2464   ObjectStoreDataKey object_store_data_key;
2465   if (!ObjectStoreDataKey::Decode(&slice, &object_store_data_key)) {
2466     INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
2467     *s = InvalidDBKeyStatus();
2468     return false;
2469   }
2470 
2471   current_key_ = object_store_data_key.user_key();
2472 
2473   int64_t version;
2474   slice = StringPiece(iterator_->Value());
2475   if (!DecodeVarInt(&slice, &version)) {
2476     INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
2477     *s = InternalInconsistencyStatus();
2478     return false;
2479   }
2480 
2481   // TODO(jsbell): This re-encodes what was just decoded; try and optimize.
2482   std::string encoded_key;
2483   EncodeIDBKey(*current_key_, &encoded_key);
2484   record_identifier_.Reset(encoded_key, version);
2485 
2486   return true;
2487 }
2488 
2489 class ObjectStoreCursorImpl : public IndexedDBBackingStore::Cursor {
2490  public:
ObjectStoreCursorImpl(base::WeakPtr<IndexedDBBackingStore::Transaction> transaction,int64_t database_id,const IndexedDBBackingStore::Cursor::CursorOptions & cursor_options)2491   ObjectStoreCursorImpl(
2492       base::WeakPtr<IndexedDBBackingStore::Transaction> transaction,
2493       int64_t database_id,
2494       const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
2495       : IndexedDBBackingStore::Cursor(std::move(transaction),
2496                                       database_id,
2497                                       cursor_options) {}
2498 
Clone() const2499   std::unique_ptr<Cursor> Clone() const override {
2500     DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
2501     auto iter = CloneIterator(this);
2502     if (!iter)
2503       return nullptr;
2504     return base::WrapUnique(new ObjectStoreCursorImpl(this, std::move(iter)));
2505   }
2506 
2507   // IndexedDBBackingStore::Cursor
value()2508   IndexedDBValue* value() override {
2509     DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
2510     return &current_value_;
2511   }
2512   bool LoadCurrentRow(Status* s) override;
2513 
2514  protected:
EncodeKey(const IndexedDBKey & key)2515   std::string EncodeKey(const IndexedDBKey& key) override {
2516     DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
2517     return ObjectStoreDataKey::Encode(cursor_options_.database_id,
2518                                       cursor_options_.object_store_id, key);
2519   }
EncodeKey(const IndexedDBKey & key,const IndexedDBKey & primary_key)2520   std::string EncodeKey(const IndexedDBKey& key,
2521                         const IndexedDBKey& primary_key) override {
2522     NOTREACHED();
2523     return std::string();
2524   }
2525 
2526  private:
ObjectStoreCursorImpl(const ObjectStoreCursorImpl * other,std::unique_ptr<TransactionalLevelDBIterator> iterator)2527   explicit ObjectStoreCursorImpl(
2528       const ObjectStoreCursorImpl* other,
2529       std::unique_ptr<TransactionalLevelDBIterator> iterator)
2530       : IndexedDBBackingStore::Cursor(other, std::move(iterator)) {}
2531 
2532   IndexedDBValue current_value_;
2533 
2534   DISALLOW_COPY_AND_ASSIGN(ObjectStoreCursorImpl);
2535 };
2536 
LoadCurrentRow(Status * s)2537 bool ObjectStoreCursorImpl::LoadCurrentRow(Status* s) {
2538   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
2539   DCHECK(transaction_);
2540   StringPiece key_slice(iterator_->Key());
2541   ObjectStoreDataKey object_store_data_key;
2542   if (!ObjectStoreDataKey::Decode(&key_slice, &object_store_data_key)) {
2543     INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
2544     *s = InvalidDBKeyStatus();
2545     return false;
2546   }
2547 
2548   current_key_ = object_store_data_key.user_key();
2549 
2550   int64_t version;
2551   StringPiece value_slice = StringPiece(iterator_->Value());
2552   if (!DecodeVarInt(&value_slice, &version)) {
2553     INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
2554     *s = InternalInconsistencyStatus();
2555     return false;
2556   }
2557 
2558   // TODO(jsbell): This re-encodes what was just decoded; try and optimize.
2559   std::string encoded_key;
2560   EncodeIDBKey(*current_key_, &encoded_key);
2561   record_identifier_.Reset(encoded_key, version);
2562 
2563   *s = transaction_->GetExternalObjectsForRecord(
2564       database_id_, iterator_->Key().as_string(), &current_value_);
2565   if (!s->ok())
2566     return false;
2567 
2568   current_value_.bits = value_slice.as_string();
2569   return true;
2570 }
2571 
2572 class IndexKeyCursorImpl : public IndexedDBBackingStore::Cursor {
2573  public:
IndexKeyCursorImpl(base::WeakPtr<IndexedDBBackingStore::Transaction> transaction,int64_t database_id,const IndexedDBBackingStore::Cursor::CursorOptions & cursor_options)2574   IndexKeyCursorImpl(
2575       base::WeakPtr<IndexedDBBackingStore::Transaction> transaction,
2576       int64_t database_id,
2577       const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
2578       : IndexedDBBackingStore::Cursor(std::move(transaction),
2579                                       database_id,
2580                                       cursor_options) {}
2581 
Clone() const2582   std::unique_ptr<Cursor> Clone() const override {
2583     DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
2584     auto iter = CloneIterator(this);
2585     if (!iter)
2586       return nullptr;
2587     return base::WrapUnique(new IndexKeyCursorImpl(this, std::move(iter)));
2588   }
2589 
2590   // IndexedDBBackingStore::Cursor
value()2591   IndexedDBValue* value() override {
2592     NOTREACHED();
2593     return nullptr;
2594   }
primary_key() const2595   const IndexedDBKey& primary_key() const override {
2596     DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
2597     return *primary_key_;
2598   }
record_identifier() const2599   const IndexedDBBackingStore::RecordIdentifier& record_identifier()
2600       const override {
2601     NOTREACHED();
2602     return record_identifier_;
2603   }
2604   bool LoadCurrentRow(Status* s) override;
2605 
2606  protected:
EncodeKey(const IndexedDBKey & key)2607   std::string EncodeKey(const IndexedDBKey& key) override {
2608     DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
2609     return IndexDataKey::Encode(cursor_options_.database_id,
2610                                 cursor_options_.object_store_id,
2611                                 cursor_options_.index_id, key);
2612   }
EncodeKey(const IndexedDBKey & key,const IndexedDBKey & primary_key)2613   std::string EncodeKey(const IndexedDBKey& key,
2614                         const IndexedDBKey& primary_key) override {
2615     DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
2616     return IndexDataKey::Encode(cursor_options_.database_id,
2617                                 cursor_options_.object_store_id,
2618                                 cursor_options_.index_id, key, primary_key);
2619   }
2620 
2621  private:
IndexKeyCursorImpl(const IndexKeyCursorImpl * other,std::unique_ptr<TransactionalLevelDBIterator> iterator)2622   explicit IndexKeyCursorImpl(
2623       const IndexKeyCursorImpl* other,
2624       std::unique_ptr<TransactionalLevelDBIterator> iterator)
2625       : IndexedDBBackingStore::Cursor(other, std::move(iterator)),
2626         primary_key_(std::make_unique<IndexedDBKey>(*other->primary_key_)) {}
2627 
2628   std::unique_ptr<IndexedDBKey> primary_key_;
2629 
2630   DISALLOW_COPY_AND_ASSIGN(IndexKeyCursorImpl);
2631 };
2632 
LoadCurrentRow(Status * s)2633 bool IndexKeyCursorImpl::LoadCurrentRow(Status* s) {
2634   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
2635   DCHECK(transaction_);
2636   StringPiece slice(iterator_->Key());
2637   IndexDataKey index_data_key;
2638   if (!IndexDataKey::Decode(&slice, &index_data_key)) {
2639     INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
2640     *s = InvalidDBKeyStatus();
2641     return false;
2642   }
2643 
2644   current_key_ = index_data_key.user_key();
2645   DCHECK(current_key_);
2646 
2647   slice = StringPiece(iterator_->Value());
2648   int64_t index_data_version;
2649   if (!DecodeVarInt(&slice, &index_data_version)) {
2650     INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
2651     *s = InternalInconsistencyStatus();
2652     return false;
2653   }
2654 
2655   if (!DecodeIDBKey(&slice, &primary_key_) || !slice.empty()) {
2656     INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
2657     *s = InternalInconsistencyStatus();
2658     return false;
2659   }
2660 
2661   std::string primary_leveldb_key =
2662       ObjectStoreDataKey::Encode(index_data_key.DatabaseId(),
2663                                  index_data_key.ObjectStoreId(), *primary_key_);
2664 
2665   std::string result;
2666   bool found = false;
2667   *s = transaction_->transaction()->Get(primary_leveldb_key, &result, &found);
2668   if (!s->ok()) {
2669     INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
2670     return false;
2671   }
2672   if (!found) {
2673     // If the version numbers don't match, that means this is an obsolete index
2674     // entry (a 'tombstone') that can be cleaned up. This removal can only
2675     // happen in non-read-only transactions.
2676     if (cursor_options_.mode != blink::mojom::IDBTransactionMode::ReadOnly)
2677       *s = transaction_->transaction()->Remove(iterator_->Key());
2678     return false;
2679   }
2680   if (result.empty()) {
2681     INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
2682     return false;
2683   }
2684 
2685   int64_t object_store_data_version;
2686   slice = StringPiece(result);
2687   if (!DecodeVarInt(&slice, &object_store_data_version)) {
2688     INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
2689     *s = InternalInconsistencyStatus();
2690     return false;
2691   }
2692 
2693   if (object_store_data_version != index_data_version) {
2694     *s = transaction_->transaction()->Remove(iterator_->Key());
2695     return false;
2696   }
2697 
2698   return true;
2699 }
2700 
2701 class IndexCursorImpl : public IndexedDBBackingStore::Cursor {
2702  public:
IndexCursorImpl(base::WeakPtr<IndexedDBBackingStore::Transaction> transaction,int64_t database_id,const IndexedDBBackingStore::Cursor::CursorOptions & cursor_options)2703   IndexCursorImpl(
2704       base::WeakPtr<IndexedDBBackingStore::Transaction> transaction,
2705       int64_t database_id,
2706       const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
2707       : IndexedDBBackingStore::Cursor(std::move(transaction),
2708                                       database_id,
2709                                       cursor_options) {}
2710 
Clone() const2711   std::unique_ptr<Cursor> Clone() const override {
2712     DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
2713     auto iter = CloneIterator(this);
2714     if (!iter)
2715       return nullptr;
2716     return base::WrapUnique(new IndexCursorImpl(this, std::move(iter)));
2717   }
2718 
2719   // IndexedDBBackingStore::Cursor
value()2720   IndexedDBValue* value() override {
2721     DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
2722     return &current_value_;
2723   }
primary_key() const2724   const IndexedDBKey& primary_key() const override {
2725     DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
2726     return *primary_key_;
2727   }
record_identifier() const2728   const IndexedDBBackingStore::RecordIdentifier& record_identifier()
2729       const override {
2730     NOTREACHED();
2731     return record_identifier_;
2732   }
2733   bool LoadCurrentRow(Status* s) override;
2734 
2735  protected:
EncodeKey(const IndexedDBKey & key)2736   std::string EncodeKey(const IndexedDBKey& key) override {
2737     DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
2738     return IndexDataKey::Encode(cursor_options_.database_id,
2739                                 cursor_options_.object_store_id,
2740                                 cursor_options_.index_id, key);
2741   }
EncodeKey(const IndexedDBKey & key,const IndexedDBKey & primary_key)2742   std::string EncodeKey(const IndexedDBKey& key,
2743                         const IndexedDBKey& primary_key) override {
2744     DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
2745     return IndexDataKey::Encode(cursor_options_.database_id,
2746                                 cursor_options_.object_store_id,
2747                                 cursor_options_.index_id, key, primary_key);
2748   }
2749 
2750  private:
IndexCursorImpl(const IndexCursorImpl * other,std::unique_ptr<TransactionalLevelDBIterator> iterator)2751   explicit IndexCursorImpl(
2752       const IndexCursorImpl* other,
2753       std::unique_ptr<TransactionalLevelDBIterator> iterator)
2754       : IndexedDBBackingStore::Cursor(other, std::move(iterator)),
2755         primary_key_(std::make_unique<IndexedDBKey>(*other->primary_key_)),
2756         current_value_(other->current_value_),
2757         primary_leveldb_key_(other->primary_leveldb_key_) {}
2758 
2759   std::unique_ptr<IndexedDBKey> primary_key_;
2760   IndexedDBValue current_value_;
2761   std::string primary_leveldb_key_;
2762 
2763   DISALLOW_COPY_AND_ASSIGN(IndexCursorImpl);
2764 };
2765 
LoadCurrentRow(Status * s)2766 bool IndexCursorImpl::LoadCurrentRow(Status* s) {
2767   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
2768   DCHECK(transaction_);
2769   StringPiece slice(iterator_->Key());
2770   IndexDataKey index_data_key;
2771   if (!IndexDataKey::Decode(&slice, &index_data_key)) {
2772     INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
2773     *s = InvalidDBKeyStatus();
2774     return false;
2775   }
2776 
2777   current_key_ = index_data_key.user_key();
2778   DCHECK(current_key_);
2779 
2780   slice = StringPiece(iterator_->Value());
2781   int64_t index_data_version;
2782   if (!DecodeVarInt(&slice, &index_data_version)) {
2783     INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
2784     *s = InternalInconsistencyStatus();
2785     return false;
2786   }
2787   if (!DecodeIDBKey(&slice, &primary_key_)) {
2788     INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
2789     *s = InvalidDBKeyStatus();
2790     return false;
2791   }
2792 
2793   DCHECK_EQ(index_data_key.DatabaseId(), database_id_);
2794   primary_leveldb_key_ =
2795       ObjectStoreDataKey::Encode(index_data_key.DatabaseId(),
2796                                  index_data_key.ObjectStoreId(), *primary_key_);
2797 
2798   std::string result;
2799   bool found = false;
2800   *s = transaction_->transaction()->Get(primary_leveldb_key_, &result, &found);
2801   if (!s->ok()) {
2802     INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
2803     return false;
2804   }
2805   if (!found) {
2806     // If the version numbers don't match, that means this is an obsolete index
2807     // entry (a 'tombstone') that can be cleaned up. This removal can only
2808     // happen in non-read-only transactions.
2809     if (cursor_options_.mode != blink::mojom::IDBTransactionMode::ReadOnly)
2810       *s = transaction_->transaction()->Remove(iterator_->Key());
2811     return false;
2812   }
2813   if (result.empty()) {
2814     INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
2815     return false;
2816   }
2817 
2818   int64_t object_store_data_version;
2819   slice = StringPiece(result);
2820   if (!DecodeVarInt(&slice, &object_store_data_version)) {
2821     INTERNAL_READ_ERROR(LOAD_CURRENT_ROW);
2822     *s = InternalInconsistencyStatus();
2823     return false;
2824   }
2825 
2826   if (object_store_data_version != index_data_version) {
2827     // If the version numbers don't match, that means this is an obsolete index
2828     // entry (a 'tombstone') that can be cleaned up. This removal can only
2829     // happen in non-read-only transactions.
2830     if (cursor_options_.mode != blink::mojom::IDBTransactionMode::ReadOnly)
2831       *s = transaction_->transaction()->Remove(iterator_->Key());
2832     return false;
2833   }
2834 
2835   current_value_.bits = slice.as_string();
2836   *s = transaction_->GetExternalObjectsForRecord(
2837       database_id_, primary_leveldb_key_, &current_value_);
2838   return s->ok();
2839 }
2840 
2841 std::unique_ptr<IndexedDBBackingStore::Cursor>
OpenObjectStoreCursor(IndexedDBBackingStore::Transaction * transaction,int64_t database_id,int64_t object_store_id,const IndexedDBKeyRange & range,blink::mojom::IDBCursorDirection direction,Status * s)2842 IndexedDBBackingStore::OpenObjectStoreCursor(
2843     IndexedDBBackingStore::Transaction* transaction,
2844     int64_t database_id,
2845     int64_t object_store_id,
2846     const IndexedDBKeyRange& range,
2847     blink::mojom::IDBCursorDirection direction,
2848     Status* s) {
2849   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
2850   IDB_TRACE("IndexedDBBackingStore::OpenObjectStoreCursor");
2851   TransactionalLevelDBTransaction* leveldb_transaction =
2852       transaction->transaction();
2853   IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
2854   cursor_options.mode = transaction->mode();
2855   // TODO(cmumford): Handle this error (crbug.com/363397)
2856   if (!ObjectStoreCursorOptions(leveldb_transaction, database_id,
2857                                 object_store_id, range, direction,
2858                                 &cursor_options, s)) {
2859     return nullptr;
2860   }
2861   std::unique_ptr<ObjectStoreCursorImpl> cursor(
2862       std::make_unique<ObjectStoreCursorImpl>(transaction->AsWeakPtr(),
2863                                               database_id, cursor_options));
2864   if (!cursor->FirstSeek(s))
2865     return nullptr;
2866 
2867   return std::move(cursor);
2868 }
2869 
2870 std::unique_ptr<IndexedDBBackingStore::Cursor>
OpenObjectStoreKeyCursor(IndexedDBBackingStore::Transaction * transaction,int64_t database_id,int64_t object_store_id,const IndexedDBKeyRange & range,blink::mojom::IDBCursorDirection direction,Status * s)2871 IndexedDBBackingStore::OpenObjectStoreKeyCursor(
2872     IndexedDBBackingStore::Transaction* transaction,
2873     int64_t database_id,
2874     int64_t object_store_id,
2875     const IndexedDBKeyRange& range,
2876     blink::mojom::IDBCursorDirection direction,
2877     Status* s) {
2878   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
2879   IDB_TRACE("IndexedDBBackingStore::OpenObjectStoreKeyCursor");
2880   TransactionalLevelDBTransaction* leveldb_transaction =
2881       transaction->transaction();
2882   IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
2883   cursor_options.mode = transaction->mode();
2884   // TODO(cmumford): Handle this error (crbug.com/363397)
2885   if (!ObjectStoreCursorOptions(leveldb_transaction, database_id,
2886                                 object_store_id, range, direction,
2887                                 &cursor_options, s)) {
2888     return nullptr;
2889   }
2890   std::unique_ptr<ObjectStoreKeyCursorImpl> cursor(
2891       std::make_unique<ObjectStoreKeyCursorImpl>(transaction->AsWeakPtr(),
2892                                                  database_id, cursor_options));
2893   if (!cursor->FirstSeek(s))
2894     return nullptr;
2895 
2896   return std::move(cursor);
2897 }
2898 
2899 std::unique_ptr<IndexedDBBackingStore::Cursor>
OpenIndexKeyCursor(IndexedDBBackingStore::Transaction * transaction,int64_t database_id,int64_t object_store_id,int64_t index_id,const IndexedDBKeyRange & range,blink::mojom::IDBCursorDirection direction,Status * s)2900 IndexedDBBackingStore::OpenIndexKeyCursor(
2901     IndexedDBBackingStore::Transaction* transaction,
2902     int64_t database_id,
2903     int64_t object_store_id,
2904     int64_t index_id,
2905     const IndexedDBKeyRange& range,
2906     blink::mojom::IDBCursorDirection direction,
2907     Status* s) {
2908   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
2909   IDB_TRACE("IndexedDBBackingStore::OpenIndexKeyCursor");
2910   *s = Status::OK();
2911   TransactionalLevelDBTransaction* leveldb_transaction =
2912       transaction->transaction();
2913   IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
2914   cursor_options.mode = transaction->mode();
2915   if (!IndexCursorOptions(leveldb_transaction, database_id, object_store_id,
2916                           index_id, range, direction, &cursor_options, s))
2917     return nullptr;
2918   std::unique_ptr<IndexKeyCursorImpl> cursor(
2919       std::make_unique<IndexKeyCursorImpl>(transaction->AsWeakPtr(),
2920                                            database_id, cursor_options));
2921   if (!cursor->FirstSeek(s))
2922     return nullptr;
2923 
2924   return std::move(cursor);
2925 }
2926 
2927 std::unique_ptr<IndexedDBBackingStore::Cursor>
OpenIndexCursor(IndexedDBBackingStore::Transaction * transaction,int64_t database_id,int64_t object_store_id,int64_t index_id,const IndexedDBKeyRange & range,blink::mojom::IDBCursorDirection direction,Status * s)2928 IndexedDBBackingStore::OpenIndexCursor(
2929     IndexedDBBackingStore::Transaction* transaction,
2930     int64_t database_id,
2931     int64_t object_store_id,
2932     int64_t index_id,
2933     const IndexedDBKeyRange& range,
2934     blink::mojom::IDBCursorDirection direction,
2935     Status* s) {
2936   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
2937   IDB_TRACE("IndexedDBBackingStore::OpenIndexCursor");
2938   TransactionalLevelDBTransaction* leveldb_transaction =
2939       transaction->transaction();
2940   IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
2941   cursor_options.mode = transaction->mode();
2942   if (!IndexCursorOptions(leveldb_transaction, database_id, object_store_id,
2943                           index_id, range, direction, &cursor_options, s))
2944     return nullptr;
2945   std::unique_ptr<IndexCursorImpl> cursor(new IndexCursorImpl(
2946       transaction->AsWeakPtr(), database_id, cursor_options));
2947   if (!cursor->FirstSeek(s))
2948     return nullptr;
2949 
2950   return std::move(cursor);
2951 }
2952 
IsBlobCleanupPending()2953 bool IndexedDBBackingStore::IsBlobCleanupPending() {
2954   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
2955   return journal_cleaning_timer_.IsRunning();
2956 }
2957 
ForceRunBlobCleanup()2958 void IndexedDBBackingStore::ForceRunBlobCleanup() {
2959   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
2960   journal_cleaning_timer_.FireNow();
2961 }
2962 
2963 IndexedDBBackingStore::Transaction::BlobWriteState::BlobWriteState() = default;
2964 
BlobWriteState(int calls_left,BlobWriteCallback on_complete)2965 IndexedDBBackingStore::Transaction::BlobWriteState::BlobWriteState(
2966     int calls_left,
2967     BlobWriteCallback on_complete)
2968     : calls_left(calls_left), on_complete(std::move(on_complete)) {}
2969 
2970 IndexedDBBackingStore::Transaction::BlobWriteState::~BlobWriteState() = default;
2971 
2972 // |backing_store| can be null in unittests (see FakeTransaction).
Transaction(base::WeakPtr<IndexedDBBackingStore> backing_store,blink::mojom::IDBTransactionDurability durability,blink::mojom::IDBTransactionMode mode)2973 IndexedDBBackingStore::Transaction::Transaction(
2974     base::WeakPtr<IndexedDBBackingStore> backing_store,
2975     blink::mojom::IDBTransactionDurability durability,
2976     blink::mojom::IDBTransactionMode mode)
2977     : backing_store_(std::move(backing_store)),
2978       transactional_leveldb_factory_(
2979           backing_store_ ? backing_store_->transactional_leveldb_factory_
2980                          : nullptr),
2981       database_id_(-1),
2982       committing_(false),
2983       durability_(durability),
2984       mode_(mode) {
2985   DCHECK(!backing_store_ ||
2986          backing_store_->idb_task_runner()->RunsTasksInCurrentSequence());
2987 }
2988 
~Transaction()2989 IndexedDBBackingStore::Transaction::~Transaction() {
2990   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
2991   DCHECK(!committing_);
2992 }
2993 
Begin(std::vector<ScopeLock> locks)2994 void IndexedDBBackingStore::Transaction::Begin(std::vector<ScopeLock> locks) {
2995   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
2996   IDB_TRACE("IndexedDBBackingStore::Transaction::Begin");
2997   DCHECK(backing_store_);
2998   DCHECK(!transaction_.get());
2999   transaction_ = transactional_leveldb_factory_->CreateLevelDBTransaction(
3000       backing_store_->db_.get(),
3001       backing_store_->db_->scopes()->CreateScope(
3002           std::move(locks), std::vector<LevelDBScopes::EmptyRange>()));
3003 
3004   // If incognito, this snapshots blobs just as the above transaction_
3005   // constructor snapshots the leveldb.
3006   for (const auto& iter : backing_store_->incognito_external_object_map_)
3007     incognito_external_object_map_[iter.first] = iter.second->Clone();
3008 }
3009 
MigrateToV1(LevelDBWriteBatch * write_batch)3010 Status IndexedDBBackingStore::MigrateToV1(LevelDBWriteBatch* write_batch) {
3011   const int64_t db_schema_version = 1;
3012   const std::string schema_version_key = SchemaVersionKey::Encode();
3013   const std::string data_version_key = DataVersionKey::Encode();
3014   Status s;
3015 
3016   ignore_result(PutInt(write_batch, schema_version_key, db_schema_version));
3017   const std::string start_key =
3018       DatabaseNameKey::EncodeMinKeyForOrigin(origin_identifier_);
3019   const std::string stop_key =
3020       DatabaseNameKey::EncodeStopKeyForOrigin(origin_identifier_);
3021   std::unique_ptr<TransactionalLevelDBIterator> it =
3022       db_->CreateIterator(db_->DefaultReadOptions());
3023   for (s = it->Seek(start_key);
3024        s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0;
3025        s = it->Next()) {
3026     int64_t database_id = 0;
3027     bool found = false;
3028     s = GetInt(db_.get(), it->Key(), &database_id, &found);
3029     if (!s.ok()) {
3030       INTERNAL_READ_ERROR(SET_UP_METADATA);
3031       return s;
3032     }
3033     if (!found) {
3034       INTERNAL_CONSISTENCY_ERROR(SET_UP_METADATA);
3035       return InternalInconsistencyStatus();
3036     }
3037     std::string version_key = DatabaseMetaDataKey::Encode(
3038         database_id, DatabaseMetaDataKey::USER_VERSION);
3039     ignore_result(PutVarInt(write_batch, version_key,
3040                             IndexedDBDatabaseMetadata::DEFAULT_VERSION));
3041   }
3042 
3043   return s;
3044 }
3045 
MigrateToV2(LevelDBWriteBatch * write_batch)3046 Status IndexedDBBackingStore::MigrateToV2(LevelDBWriteBatch* write_batch) {
3047   const int64_t db_schema_version = 2;
3048   const std::string schema_version_key = SchemaVersionKey::Encode();
3049   const std::string data_version_key = DataVersionKey::Encode();
3050   Status s;
3051 
3052   ignore_result(PutInt(write_batch, schema_version_key, db_schema_version));
3053   ignore_result(PutInt(write_batch, data_version_key,
3054                        IndexedDBDataFormatVersion::GetCurrent().Encode()));
3055   return s;
3056 }
3057 
MigrateToV3(LevelDBWriteBatch * write_batch)3058 Status IndexedDBBackingStore::MigrateToV3(LevelDBWriteBatch* write_batch) {
3059   const int64_t db_schema_version = 3;
3060   const std::string schema_version_key = SchemaVersionKey::Encode();
3061   const std::string data_version_key = DataVersionKey::Encode();
3062   Status s;
3063 
3064   // Up until http://crrev.com/3c0d175b, this migration path did not write
3065   // the updated schema version to disk. In consequence, any database that
3066   // started out as schema version <= 2 will remain at schema version 2
3067   // indefinitely. Furthermore, this migration path used to call
3068   // "base::DeletePathRecursively(blob_path_)", so databases stuck at
3069   // version 2 would lose their stored Blobs on every open call.
3070   //
3071   // In order to prevent corrupt databases, when upgrading from 2 to 3 this
3072   // will consider any v2 databases with BlobEntryKey entries as corrupt.
3073   // https://crbug.com/756447, https://crbug.com/829125,
3074   // https://crbug.com/829141
3075   bool has_blobs = false;
3076   s = AnyDatabaseContainsBlobs(db_.get(), &has_blobs);
3077   if (!s.ok()) {
3078     INTERNAL_CONSISTENCY_ERROR(SET_UP_METADATA);
3079     return InternalInconsistencyStatus();
3080   }
3081   indexed_db::ReportV2Schema(has_blobs, origin_);
3082   if (has_blobs) {
3083     INTERNAL_CONSISTENCY_ERROR(UPGRADING_SCHEMA_CORRUPTED_BLOBS);
3084     if (origin_.host() != "docs.google.com")
3085       return InternalInconsistencyStatus();
3086   } else {
3087     ignore_result(PutInt(write_batch, schema_version_key, db_schema_version));
3088   }
3089 
3090   return s;
3091 }
3092 
MigrateToV4(LevelDBWriteBatch * write_batch)3093 Status IndexedDBBackingStore::MigrateToV4(LevelDBWriteBatch* write_batch) {
3094   const int64_t db_schema_version = 4;
3095   const std::string schema_version_key = SchemaVersionKey::Encode();
3096   Status s;
3097 
3098   std::vector<base::FilePath> empty_blobs_to_delete;
3099   s = UpgradeBlobEntriesToV4(db_.get(), write_batch, &empty_blobs_to_delete);
3100   if (!s.ok()) {
3101     INTERNAL_CONSISTENCY_ERROR(SET_UP_METADATA);
3102     return InternalInconsistencyStatus();
3103   }
3104   ignore_result(PutInt(write_batch, schema_version_key, db_schema_version));
3105 
3106   // Delete all empty files that resulted from the migration to v4. If this
3107   // fails it's not a big deal.
3108   if (filesystem_proxy_) {
3109     for (const auto& path : empty_blobs_to_delete) {
3110       filesystem_proxy_->DeleteFile(path);
3111     }
3112   }
3113 
3114   return s;
3115 }
3116 
MigrateToV5(LevelDBWriteBatch * write_batch)3117 Status IndexedDBBackingStore::MigrateToV5(LevelDBWriteBatch* write_batch) {
3118   // Some blob files were not written to disk due to a bug.
3119   // Validate that all blob files in the db exist on disk,
3120   // and return InternalInconsistencyStatus if any do not.
3121   // See http://crbug.com/1131151 for more details.
3122   const int64_t db_schema_version = 5;
3123   const std::string schema_version_key = SchemaVersionKey::Encode();
3124   Status s;
3125 
3126   if (origin_.host() != "docs.google.com") {
3127     s = ValidateBlobFiles(db_.get());
3128     if (!s.ok()) {
3129       INTERNAL_CONSISTENCY_ERROR(SET_UP_METADATA);
3130       return InternalInconsistencyStatus();
3131     }
3132   }
3133   ignore_result(PutInt(write_batch, schema_version_key, db_schema_version));
3134 
3135   return s;
3136 }
3137 
HandleBlobPreTransaction()3138 Status IndexedDBBackingStore::Transaction::HandleBlobPreTransaction() {
3139   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
3140   DCHECK(backing_store_);
3141   if (backing_store_->is_incognito())
3142     return Status::OK();
3143 
3144   DCHECK(blobs_to_write_.empty());
3145 
3146   if (external_object_change_map_.empty())
3147     return Status::OK();
3148 
3149   std::unique_ptr<LevelDBDirectTransaction> direct_txn =
3150       transactional_leveldb_factory_->CreateLevelDBDirectTransaction(
3151           backing_store_->db_.get());
3152 
3153   int64_t next_blob_number = -1;
3154   bool result = indexed_db::GetBlobNumberGeneratorCurrentNumber(
3155       direct_txn.get(), database_id_, &next_blob_number);
3156   if (!result || next_blob_number < 0)
3157     return InternalInconsistencyStatus();
3158 
3159   // Because blob numbers were not incremented on the correct transaction for
3160   // m78 and m79, they need to be checked. See https://crbug.com/1039446
3161   base::FilePath blob_path =
3162       backing_store_->GetBlobFileName(database_id_, next_blob_number);
3163   while (backing_store_->filesystem_proxy_->PathExists(blob_path)) {
3164     ++next_blob_number;
3165     blob_path = backing_store_->GetBlobFileName(database_id_, next_blob_number);
3166   }
3167 
3168   for (auto& iter : external_object_change_map_) {
3169     for (auto& entry : iter.second->mutable_external_objects()) {
3170       switch (entry.object_type()) {
3171         case IndexedDBExternalObject::ObjectType::kFile:
3172         case IndexedDBExternalObject::ObjectType::kBlob:
3173           blobs_to_write_.push_back({database_id_, next_blob_number});
3174           DCHECK(entry.is_remote_valid());
3175           entry.set_blob_number(next_blob_number);
3176           ++next_blob_number;
3177           result = indexed_db::UpdateBlobNumberGeneratorCurrentNumber(
3178               direct_txn.get(), database_id_, next_blob_number);
3179           if (!result)
3180             return InternalInconsistencyStatus();
3181           break;
3182         case IndexedDBExternalObject::ObjectType::kNativeFileSystemHandle:
3183           break;
3184       }
3185     }
3186   }
3187 
3188   AppendBlobsToRecoveryBlobJournal(direct_txn.get(), blobs_to_write_);
3189 
3190   return direct_txn->Commit();
3191 }
3192 
CollectBlobFilesToRemove()3193 bool IndexedDBBackingStore::Transaction::CollectBlobFilesToRemove() {
3194   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
3195   DCHECK(backing_store_);
3196   if (backing_store_->is_incognito())
3197     return true;
3198 
3199   // Look up all old files to remove as part of the transaction, store their
3200   // names in blobs_to_remove_, and remove their old blob data entries.
3201   for (const auto& iter : external_object_change_map_) {
3202     BlobEntryKey blob_entry_key;
3203     StringPiece key_piece(iter.second->object_store_data_key());
3204     if (!BlobEntryKey::FromObjectStoreDataKey(&key_piece, &blob_entry_key)) {
3205       NOTREACHED();
3206       INTERNAL_WRITE_ERROR(TRANSACTION_COMMIT_METHOD);
3207       transaction_ = nullptr;
3208       return false;
3209     }
3210     if (database_id_ < 0)
3211       database_id_ = blob_entry_key.database_id();
3212     else
3213       DCHECK_EQ(database_id_, blob_entry_key.database_id());
3214     std::string blob_entry_key_bytes = blob_entry_key.Encode();
3215     bool found;
3216     std::string blob_entry_value_bytes;
3217     Status s = transaction_->Get(blob_entry_key_bytes, &blob_entry_value_bytes,
3218                                  &found);
3219     if (s.ok() && found) {
3220       std::vector<IndexedDBExternalObject> external_objects;
3221       if (!DecodeExternalObjects(blob_entry_value_bytes, &external_objects)) {
3222         INTERNAL_READ_ERROR(TRANSACTION_COMMIT_METHOD);
3223         transaction_ = nullptr;
3224         return false;
3225       }
3226       for (const auto& blob : external_objects) {
3227         if (blob.object_type() != IndexedDBExternalObject::ObjectType::kBlob &&
3228             blob.object_type() != IndexedDBExternalObject::ObjectType::kFile) {
3229           continue;
3230         }
3231         blobs_to_remove_.push_back({database_id_, blob.blob_number()});
3232         s = transaction_->Remove(blob_entry_key_bytes);
3233         if (!s.ok()) {
3234           transaction_ = nullptr;
3235           return false;
3236         }
3237       }
3238     }
3239   }
3240   return true;
3241 }
3242 
PartitionBlobsToRemove(BlobJournalType * inactive_blobs,BlobJournalType * active_blobs) const3243 void IndexedDBBackingStore::Transaction::PartitionBlobsToRemove(
3244     BlobJournalType* inactive_blobs,
3245     BlobJournalType* active_blobs) const {
3246   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
3247   DCHECK(backing_store_);
3248   IndexedDBActiveBlobRegistry* registry =
3249       backing_store_->active_blob_registry();
3250   for (const auto& iter : blobs_to_remove_) {
3251     bool is_blob_referenced = registry->MarkBlobInfoDeletedAndCheckIfReferenced(
3252         iter.first, iter.second);
3253     if (is_blob_referenced)
3254       active_blobs->push_back(iter);
3255     else
3256       inactive_blobs->push_back(iter);
3257   }
3258 }
3259 
CommitPhaseOne(BlobWriteCallback callback)3260 Status IndexedDBBackingStore::Transaction::CommitPhaseOne(
3261     BlobWriteCallback callback) {
3262   IDB_TRACE("IndexedDBBackingStore::Transaction::CommitPhaseOne");
3263   DCHECK(transaction_.get());
3264   DCHECK(backing_store_);
3265   DCHECK(backing_store_->idb_task_runner()->RunsTasksInCurrentSequence());
3266 
3267   Status s;
3268 
3269   s = HandleBlobPreTransaction();
3270   if (!s.ok()) {
3271     INTERNAL_WRITE_ERROR(TRANSACTION_COMMIT_METHOD);
3272     transaction_ = nullptr;
3273     return s;
3274   }
3275 
3276   DCHECK(external_object_change_map_.empty() ||
3277          KeyPrefix::IsValidDatabaseId(database_id_));
3278   if (!CollectBlobFilesToRemove()) {
3279     INTERNAL_WRITE_ERROR(TRANSACTION_COMMIT_METHOD);
3280     transaction_ = nullptr;
3281     return InternalInconsistencyStatus();
3282   }
3283 
3284   committing_ = true;
3285   backing_store_->WillCommitTransaction();
3286 
3287   if (!external_object_change_map_.empty() && !backing_store_->is_incognito()) {
3288     // This kicks off the writes of the new blobs, if any.
3289     return WriteNewBlobs(std::move(callback));
3290   } else {
3291     return std::move(callback).Run(
3292         BlobWriteResult::kRunPhaseTwoAndReturnResult);
3293   }
3294 }
3295 
CommitPhaseTwo()3296 Status IndexedDBBackingStore::Transaction::CommitPhaseTwo() {
3297   IDB_TRACE("IndexedDBBackingStore::Transaction::CommitPhaseTwo");
3298   DCHECK(backing_store_);
3299   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
3300   Status s;
3301 
3302   DCHECK(committing_);
3303   committing_ = false;
3304 
3305   // DidCommitTransaction must be called during CommitPhaseTwo,
3306   // as it decrements the number of active transactions that were
3307   // incremented from CommitPhaseOne.  However, it also potentially cleans up
3308   // the recovery blob journal, and so needs to be done after the newly
3309   // written blobs have been removed from the recovery journal further below.
3310   // As there are early outs in this function, use an RAII helper here.
3311   AutoDidCommitTransaction run_did_commit_transaction_on_return(
3312       backing_store_.get());
3313 
3314   BlobJournalType recovery_journal, active_journal, saved_recovery_journal,
3315       inactive_blobs;
3316   if (!external_object_change_map_.empty()) {
3317     if (!backing_store_->is_incognito()) {
3318       for (auto& iter : external_object_change_map_) {
3319         BlobEntryKey blob_entry_key;
3320         StringPiece key_piece(iter.second->object_store_data_key());
3321         if (!BlobEntryKey::FromObjectStoreDataKey(&key_piece,
3322                                                   &blob_entry_key)) {
3323           NOTREACHED();
3324           return InternalInconsistencyStatus();
3325         }
3326         // Add the new blob-table entry for each blob to the main transaction,
3327         // or remove any entry that may exist if there's no new one.
3328         if (iter.second->external_objects().empty()) {
3329           s = transaction_->Remove(blob_entry_key.Encode());
3330         } else {
3331           std::string tmp =
3332               EncodeExternalObjects(iter.second->external_objects());
3333           s = transaction_->Put(blob_entry_key.Encode(), &tmp);
3334         }
3335         if (!s.ok())
3336           return s;
3337       }
3338     }
3339 
3340     IDB_TRACE("IndexedDBBackingStore::Transaction.BlobJournal");
3341     // Read the persisted states of the recovery/live blob journals,
3342     // so that they can be updated correctly by the transaction.
3343     std::unique_ptr<LevelDBDirectTransaction> journal_transaction =
3344         transactional_leveldb_factory_->CreateLevelDBDirectTransaction(
3345             backing_store_->db_.get());
3346     s = GetRecoveryBlobJournal(journal_transaction.get(), &recovery_journal);
3347     if (!s.ok())
3348       return s;
3349     s = GetActiveBlobJournal(journal_transaction.get(), &active_journal);
3350     if (!s.ok())
3351       return s;
3352 
3353     // Remove newly added blobs from the journal - they will be accounted
3354     // for in blob entry tables in the transaction.
3355     std::sort(recovery_journal.begin(), recovery_journal.end());
3356     std::sort(blobs_to_write_.begin(), blobs_to_write_.end());
3357     BlobJournalType new_journal = base::STLSetDifference<BlobJournalType>(
3358         recovery_journal, blobs_to_write_);
3359     recovery_journal.swap(new_journal);
3360 
3361     // Append newly deleted blobs to appropriate recovery/active journals.
3362     saved_recovery_journal = recovery_journal;
3363     BlobJournalType active_blobs;
3364     if (!blobs_to_remove_.empty()) {
3365       DCHECK(!backing_store_->is_incognito());
3366       PartitionBlobsToRemove(&inactive_blobs, &active_blobs);
3367     }
3368     recovery_journal.insert(recovery_journal.end(), inactive_blobs.begin(),
3369                             inactive_blobs.end());
3370     active_journal.insert(active_journal.end(), active_blobs.begin(),
3371                           active_blobs.end());
3372     s = UpdateRecoveryBlobJournal(transaction_.get(), recovery_journal);
3373     if (!s.ok())
3374       return s;
3375     s = UpdateActiveBlobJournal(transaction_.get(), active_journal);
3376     if (!s.ok())
3377       return s;
3378   }
3379 
3380   // Actually commit. If this succeeds, the journals will appropriately
3381   // reflect pending blob work - dead files that should be deleted
3382   // immediately, and live files to monitor.
3383   s = transaction_->Commit(
3384       IndexedDBBackingStore::ShouldSyncOnCommit(durability_));
3385   transaction_ = nullptr;
3386 
3387   if (!s.ok()) {
3388     INTERNAL_WRITE_ERROR(TRANSACTION_COMMIT_METHOD);
3389     return s;
3390   }
3391 
3392   if (backing_store_->is_incognito()) {
3393     if (!external_object_change_map_.empty()) {
3394       auto& target_map = backing_store_->incognito_external_object_map_;
3395       for (auto& iter : external_object_change_map_) {
3396         auto target_record = target_map.find(iter.first);
3397         if (target_record != target_map.end())
3398           target_map.erase(target_record);
3399         if (iter.second)
3400           target_map[iter.first] = std::move(iter.second);
3401       }
3402     }
3403     return Status::OK();
3404   }
3405 
3406   // Actually delete dead blob files, then remove those entries
3407   // from the persisted recovery journal.
3408   if (inactive_blobs.empty())
3409     return Status::OK();
3410 
3411   DCHECK(!external_object_change_map_.empty());
3412 
3413   s = backing_store_->CleanUpBlobJournalEntries(inactive_blobs);
3414   if (!s.ok()) {
3415     INTERNAL_WRITE_ERROR(TRANSACTION_COMMIT_METHOD);
3416     return s;
3417   }
3418 
3419   std::unique_ptr<LevelDBDirectTransaction> update_journal_transaction =
3420       transactional_leveldb_factory_->CreateLevelDBDirectTransaction(
3421           backing_store_->db_.get());
3422   UpdateRecoveryBlobJournal(update_journal_transaction.get(),
3423                             saved_recovery_journal);
3424   s = update_journal_transaction->Commit();
3425   return s;
3426 }
3427 
WriteNewBlobs(BlobWriteCallback callback)3428 leveldb::Status IndexedDBBackingStore::Transaction::WriteNewBlobs(
3429     BlobWriteCallback callback) {
3430   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
3431   IDB_ASYNC_TRACE_BEGIN("IndexedDBBackingStore::Transaction::WriteNewBlobs",
3432                         this);
3433   DCHECK(backing_store_);
3434   DCHECK(!backing_store_->is_incognito());
3435   DCHECK(!external_object_change_map_.empty());
3436   DCHECK_GT(database_id_, 0);
3437 
3438   // Count how many objects we need to write by excluding all empty files and
3439   // blobs.
3440   int num_objects_to_write = 0;
3441   for (const auto& iter : external_object_change_map_) {
3442     for (const auto& entry : iter.second->external_objects()) {
3443       switch (entry.object_type()) {
3444         case IndexedDBExternalObject::ObjectType::kFile:
3445         case IndexedDBExternalObject::ObjectType::kBlob:
3446           if (entry.size() != 0)
3447             ++num_objects_to_write;
3448           break;
3449         case IndexedDBExternalObject::ObjectType::kNativeFileSystemHandle:
3450           if (entry.native_file_system_token().empty())
3451             ++num_objects_to_write;
3452           break;
3453       }
3454     }
3455   }
3456   if (num_objects_to_write == 0) {
3457     return std::move(callback).Run(
3458         BlobWriteResult::kRunPhaseTwoAndReturnResult);
3459   }
3460 
3461   write_state_.emplace(num_objects_to_write, std::move(callback));
3462 
3463   storage::mojom::BlobStorageContext* blob_storage_context =
3464       backing_store_->blob_storage_context_;
3465 
3466   auto write_result_callback = base::BindRepeating(
3467       [](base::WeakPtr<Transaction> transaction, bool success) {
3468         if (!transaction)
3469           return;
3470         // This can be null if Rollback() is called.
3471         if (!transaction->write_state_)
3472           return;
3473         auto& write_state = transaction->write_state_.value();
3474         DCHECK(!write_state.on_complete.is_null());
3475         if (!success) {
3476           auto on_complete = std::move(write_state.on_complete);
3477           transaction->write_state_.reset();
3478           std::move(on_complete).Run(BlobWriteResult::kFailure);
3479           return;
3480         }
3481         --(write_state.calls_left);
3482         if (write_state.calls_left == 0) {
3483           auto on_complete = std::move(write_state.on_complete);
3484           transaction->write_state_.reset();
3485           std::move(on_complete).Run(BlobWriteResult::kRunPhaseTwoAsync);
3486         }
3487       },
3488       ptr_factory_.GetWeakPtr());
3489 
3490   auto blob_write_result_callback = base::BindRepeating(
3491       [](const base::RepeatingCallback<void(bool success)>& callback,
3492          storage::mojom::WriteBlobToFileResult result) {
3493         callback.Run(result == storage::mojom::WriteBlobToFileResult::kSuccess);
3494       },
3495       write_result_callback);
3496 
3497   for (auto& iter : external_object_change_map_) {
3498     for (auto& entry : iter.second->mutable_external_objects()) {
3499       switch (entry.object_type()) {
3500         case IndexedDBExternalObject::ObjectType::kFile:
3501         case IndexedDBExternalObject::ObjectType::kBlob: {
3502           if (entry.size() == 0)
3503             continue;
3504           // If this directory creation fails then the WriteBlobToFile call
3505           // will fail. So there is no need to special-case handle it here.
3506           FilePath path = GetBlobDirectoryNameForKey(
3507               backing_store_->blob_path_, database_id_, entry.blob_number());
3508           backing_store_->filesystem_proxy_->CreateDirectory(path);
3509           // TODO(dmurph): Refactor IndexedDBExternalObject to not use a
3510           // SharedRemote, so this code can just move the remote, instead of
3511           // cloning.
3512           mojo::PendingRemote<blink::mojom::Blob> pending_blob;
3513           entry.remote()->Clone(pending_blob.InitWithNewPipeAndPassReceiver());
3514 
3515           // Android doesn't seem to consistently be able to set file
3516           // modification times. The timestamp is not checked during reading
3517           // on Android either. https://crbug.com/1045488
3518           base::Optional<base::Time> last_modified;
3519 #if !defined(OS_ANDROID)
3520           last_modified = entry.last_modified().is_null()
3521                               ? base::nullopt
3522                               : base::make_optional(entry.last_modified());
3523 #endif
3524           blob_storage_context->WriteBlobToFile(
3525               std::move(pending_blob),
3526               backing_store_->GetBlobFileName(database_id_,
3527                                               entry.blob_number()),
3528               IndexedDBBackingStore::ShouldSyncOnCommit(durability_),
3529               last_modified, blob_write_result_callback);
3530           break;
3531         }
3532         case IndexedDBExternalObject::ObjectType::kNativeFileSystemHandle: {
3533           if (!entry.native_file_system_token().empty())
3534             continue;
3535           // TODO(dmurph): Refactor IndexedDBExternalObject to not use a
3536           // SharedRemote, so this code can just move the remote, instead of
3537           // cloning.
3538           mojo::PendingRemote<blink::mojom::NativeFileSystemTransferToken>
3539               token_clone;
3540           entry.native_file_system_token_remote()->Clone(
3541               token_clone.InitWithNewPipeAndPassReceiver());
3542 
3543           backing_store_->native_file_system_context_->SerializeHandle(
3544               std::move(token_clone),
3545               base::BindOnce(
3546                   [](base::WeakPtr<Transaction> transaction,
3547                      IndexedDBExternalObject* object,
3548                      base::OnceCallback<void(bool success)> callback,
3549                      const std::vector<uint8_t>& serialized_token) {
3550                     // |object| is owned by |transaction|, so make sure
3551                     // |transaction| is still valid before doing anything else.
3552                     if (!transaction)
3553                       return;
3554                     if (serialized_token.empty()) {
3555                       std::move(callback).Run(/*success=*/false);
3556                       return;
3557                     }
3558                     object->set_native_file_system_token(serialized_token);
3559                     std::move(callback).Run(/*success=*/true);
3560                   },
3561                   ptr_factory_.GetWeakPtr(), &entry, write_result_callback));
3562           break;
3563         }
3564       }
3565     }
3566   }
3567   return leveldb::Status::OK();
3568 }
3569 
Reset()3570 void IndexedDBBackingStore::Transaction::Reset() {
3571   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
3572   backing_store_.reset();
3573   transaction_ = nullptr;
3574 }
3575 
Rollback()3576 leveldb::Status IndexedDBBackingStore::Transaction::Rollback() {
3577   IDB_TRACE("IndexedDBBackingStore::Transaction::Rollback");
3578   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
3579   DCHECK(backing_store_);
3580 
3581   if (committing_) {
3582     committing_ = false;
3583     backing_store_->DidCommitTransaction();
3584   }
3585 
3586   write_state_.reset();
3587 
3588   if (!transaction_)
3589     return leveldb::Status::OK();
3590   // The RollbackAndMaybeTearDown method could tear down the
3591   // IndexedDBOriginState, which would destroy |this|.
3592   scoped_refptr<TransactionalLevelDBTransaction> transaction =
3593       std::move(transaction_);
3594   return transaction->Rollback();
3595 }
3596 
GetTransactionSize()3597 uint64_t IndexedDBBackingStore::Transaction::GetTransactionSize() {
3598   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
3599   DCHECK(transaction_);
3600   return transaction_->GetTransactionSize();
3601 }
3602 
PutExternalObjectsIfNeeded(int64_t database_id,const std::string & object_store_data_key,std::vector<IndexedDBExternalObject> * external_objects)3603 Status IndexedDBBackingStore::Transaction::PutExternalObjectsIfNeeded(
3604     int64_t database_id,
3605     const std::string& object_store_data_key,
3606     std::vector<IndexedDBExternalObject>* external_objects) {
3607   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
3608   if (!external_objects || external_objects->empty()) {
3609     external_object_change_map_.erase(object_store_data_key);
3610     incognito_external_object_map_.erase(object_store_data_key);
3611 
3612     BlobEntryKey blob_entry_key;
3613     StringPiece leveldb_key_piece(object_store_data_key);
3614     if (!BlobEntryKey::FromObjectStoreDataKey(&leveldb_key_piece,
3615                                               &blob_entry_key)) {
3616       NOTREACHED();
3617       return InternalInconsistencyStatus();
3618     }
3619     std::string value;
3620     bool found = false;
3621     Status s = transaction()->Get(blob_entry_key.Encode(), &value, &found);
3622     if (!s.ok())
3623       return s;
3624     if (!found)
3625       return Status::OK();
3626   }
3627   PutExternalObjects(database_id, object_store_data_key, external_objects);
3628   return Status::OK();
3629 }
3630 
3631 // This is storing an info, even if empty, even if the previous key had no blob
3632 // info that we know of.  It duplicates a bunch of information stored in the
3633 // leveldb transaction, but only w.r.t. the user keys altered--we don't keep the
3634 // changes to exists or index keys here.
PutExternalObjects(int64_t database_id,const std::string & object_store_data_key,std::vector<IndexedDBExternalObject> * external_objects)3635 void IndexedDBBackingStore::Transaction::PutExternalObjects(
3636     int64_t database_id,
3637     const std::string& object_store_data_key,
3638     std::vector<IndexedDBExternalObject>* external_objects) {
3639   DCHECK_CALLED_ON_VALID_SEQUENCE(idb_sequence_checker_);
3640   DCHECK(!object_store_data_key.empty());
3641   if (database_id_ < 0)
3642     database_id_ = database_id;
3643   DCHECK_EQ(database_id_, database_id);
3644 
3645   const auto& it = external_object_change_map_.find(object_store_data_key);
3646   IndexedDBExternalObjectChangeRecord* record = nullptr;
3647   if (it == external_object_change_map_.end()) {
3648     std::unique_ptr<IndexedDBExternalObjectChangeRecord> new_record =
3649         std::make_unique<IndexedDBExternalObjectChangeRecord>(
3650             object_store_data_key);
3651     record = new_record.get();
3652     external_object_change_map_[object_store_data_key] = std::move(new_record);
3653   } else {
3654     record = it->second.get();
3655   }
3656   record->SetExternalObjects(external_objects);
3657 }
3658 
3659 }  // namespace content
3660