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, ¤t_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 ¤t_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(), ¤t_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 ¤t_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_, ¤t_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