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 "base/file_util.h"
8 #include "base/files/file_path.h"
9 #include "base/format_macros.h"
10 #include "base/json/json_reader.h"
11 #include "base/json/json_writer.h"
12 #include "base/logging.h"
13 #include "base/metrics/histogram.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "content/browser/child_process_security_policy_impl.h"
18 #include "content/browser/indexed_db/indexed_db_blob_info.h"
19 #include "content/browser/indexed_db/indexed_db_class_factory.h"
20 #include "content/browser/indexed_db/indexed_db_database_error.h"
21 #include "content/browser/indexed_db/indexed_db_leveldb_coding.h"
22 #include "content/browser/indexed_db/indexed_db_metadata.h"
23 #include "content/browser/indexed_db/indexed_db_tracing.h"
24 #include "content/browser/indexed_db/indexed_db_value.h"
25 #include "content/browser/indexed_db/leveldb/leveldb_comparator.h"
26 #include "content/browser/indexed_db/leveldb/leveldb_database.h"
27 #include "content/browser/indexed_db/leveldb/leveldb_iterator.h"
28 #include "content/browser/indexed_db/leveldb/leveldb_transaction.h"
29 #include "content/common/indexed_db/indexed_db_key.h"
30 #include "content/common/indexed_db/indexed_db_key_path.h"
31 #include "content/common/indexed_db/indexed_db_key_range.h"
32 #include "content/public/browser/browser_thread.h"
33 #include "net/url_request/url_request_context.h"
34 #include "third_party/WebKit/public/platform/WebIDBTypes.h"
35 #include "third_party/WebKit/public/web/WebSerializedScriptValueVersion.h"
36 #include "third_party/leveldatabase/env_chromium.h"
37 #include "webkit/browser/blob/blob_data_handle.h"
38 #include "webkit/browser/fileapi/file_stream_writer.h"
39 #include "webkit/browser/fileapi/file_writer_delegate.h"
40 #include "webkit/browser/fileapi/local_file_stream_writer.h"
41 #include "webkit/common/database/database_identifier.h"
42
43 using base::FilePath;
44 using base::StringPiece;
45 using fileapi::FileWriterDelegate;
46
47 namespace content {
48
49 namespace {
50
GetBlobDirectoryName(const FilePath & pathBase,int64 database_id)51 FilePath GetBlobDirectoryName(const FilePath& pathBase, int64 database_id) {
52 return pathBase.AppendASCII(base::StringPrintf("%" PRIx64, database_id));
53 }
54
GetBlobDirectoryNameForKey(const FilePath & pathBase,int64 database_id,int64 key)55 FilePath GetBlobDirectoryNameForKey(const FilePath& pathBase,
56 int64 database_id,
57 int64 key) {
58 FilePath path = GetBlobDirectoryName(pathBase, database_id);
59 path = path.AppendASCII(base::StringPrintf(
60 "%02x", static_cast<int>(key & 0x000000000000ff00) >> 8));
61 return path;
62 }
63
GetBlobFileNameForKey(const FilePath & pathBase,int64 database_id,int64 key)64 FilePath GetBlobFileNameForKey(const FilePath& pathBase,
65 int64 database_id,
66 int64 key) {
67 FilePath path = GetBlobDirectoryNameForKey(pathBase, database_id, key);
68 path = path.AppendASCII(base::StringPrintf("%" PRIx64, key));
69 return path;
70 }
71
MakeIDBBlobDirectory(const FilePath & pathBase,int64 database_id,int64 key)72 bool MakeIDBBlobDirectory(const FilePath& pathBase,
73 int64 database_id,
74 int64 key) {
75 FilePath path = GetBlobDirectoryNameForKey(pathBase, database_id, key);
76 return base::CreateDirectory(path);
77 }
78
ComputeOriginIdentifier(const GURL & origin_url)79 static std::string ComputeOriginIdentifier(const GURL& origin_url) {
80 return webkit_database::GetIdentifierFromOrigin(origin_url) + "@1";
81 }
82
ComputeFileName(const GURL & origin_url)83 static base::FilePath ComputeFileName(const GURL& origin_url) {
84 return base::FilePath()
85 .AppendASCII(webkit_database::GetIdentifierFromOrigin(origin_url))
86 .AddExtension(FILE_PATH_LITERAL(".indexeddb.leveldb"));
87 }
88
ComputeBlobPath(const GURL & origin_url)89 static base::FilePath ComputeBlobPath(const GURL& origin_url) {
90 return base::FilePath()
91 .AppendASCII(webkit_database::GetIdentifierFromOrigin(origin_url))
92 .AddExtension(FILE_PATH_LITERAL(".indexeddb.blob"));
93 }
94
ComputeCorruptionFileName(const GURL & origin_url)95 static base::FilePath ComputeCorruptionFileName(const GURL& origin_url) {
96 return ComputeFileName(origin_url)
97 .Append(FILE_PATH_LITERAL("corruption_info.json"));
98 }
99
100 } // namespace
101
102 static const int64 kKeyGeneratorInitialNumber =
103 1; // From the IndexedDB specification.
104
105 enum IndexedDBBackingStoreErrorSource {
106 // 0 - 2 are no longer used.
107 FIND_KEY_IN_INDEX = 3,
108 GET_IDBDATABASE_METADATA,
109 GET_INDEXES,
110 GET_KEY_GENERATOR_CURRENT_NUMBER,
111 GET_OBJECT_STORES,
112 GET_RECORD,
113 KEY_EXISTS_IN_OBJECT_STORE,
114 LOAD_CURRENT_ROW,
115 SET_UP_METADATA,
116 GET_PRIMARY_KEY_VIA_INDEX,
117 KEY_EXISTS_IN_INDEX,
118 VERSION_EXISTS,
119 DELETE_OBJECT_STORE,
120 SET_MAX_OBJECT_STORE_ID,
121 SET_MAX_INDEX_ID,
122 GET_NEW_DATABASE_ID,
123 GET_NEW_VERSION_NUMBER,
124 CREATE_IDBDATABASE_METADATA,
125 DELETE_DATABASE,
126 TRANSACTION_COMMIT_METHOD, // TRANSACTION_COMMIT is a WinNT.h macro
127 GET_DATABASE_NAMES,
128 DELETE_INDEX,
129 CLEAR_OBJECT_STORE,
130 READ_BLOB_JOURNAL,
131 DECODE_BLOB_JOURNAL,
132 GET_BLOB_KEY_GENERATOR_CURRENT_NUMBER,
133 GET_BLOB_INFO_FOR_RECORD,
134 INTERNAL_ERROR_MAX,
135 };
136
RecordInternalError(const char * type,IndexedDBBackingStoreErrorSource location)137 static void RecordInternalError(const char* type,
138 IndexedDBBackingStoreErrorSource location) {
139 std::string name;
140 name.append("WebCore.IndexedDB.BackingStore.").append(type).append("Error");
141 base::Histogram::FactoryGet(name,
142 1,
143 INTERNAL_ERROR_MAX,
144 INTERNAL_ERROR_MAX + 1,
145 base::HistogramBase::kUmaTargetedHistogramFlag)
146 ->Add(location);
147 }
148
149 // Use to signal conditions caused by data corruption.
150 // A macro is used instead of an inline function so that the assert and log
151 // report the line number.
152 #define REPORT_ERROR(type, location) \
153 do { \
154 LOG(ERROR) << "IndexedDB " type " Error: " #location; \
155 RecordInternalError(type, location); \
156 } while (0)
157
158 #define INTERNAL_READ_ERROR(location) REPORT_ERROR("Read", location)
159 #define INTERNAL_CONSISTENCY_ERROR(location) \
160 REPORT_ERROR("Consistency", location)
161 #define INTERNAL_WRITE_ERROR(location) REPORT_ERROR("Write", location)
162
163 // Use to signal conditions that usually indicate developer error, but
164 // could be caused by data corruption. A macro is used instead of an
165 // inline function so that the assert and log report the line number.
166 // TODO(cmumford): Improve test coverage so that all error conditions are
167 // "tested" and then delete this macro.
168 #define REPORT_ERROR_UNTESTED(type, location) \
169 do { \
170 LOG(ERROR) << "IndexedDB " type " Error: " #location; \
171 NOTREACHED(); \
172 RecordInternalError(type, location); \
173 } while (0)
174
175 #define INTERNAL_READ_ERROR_UNTESTED(location) \
176 REPORT_ERROR_UNTESTED("Read", location)
177 #define INTERNAL_CONSISTENCY_ERROR_UNTESTED(location) \
178 REPORT_ERROR_UNTESTED("Consistency", location)
179 #define INTERNAL_WRITE_ERROR_UNTESTED(location) \
180 REPORT_ERROR_UNTESTED("Write", location)
181
PutBool(LevelDBTransaction * transaction,const StringPiece & key,bool value)182 static void PutBool(LevelDBTransaction* transaction,
183 const StringPiece& key,
184 bool value) {
185 std::string buffer;
186 EncodeBool(value, &buffer);
187 transaction->Put(key, &buffer);
188 }
189
190 // Was able to use LevelDB to read the data w/o error, but the data read was not
191 // in the expected format.
InternalInconsistencyStatus()192 static leveldb::Status InternalInconsistencyStatus() {
193 return leveldb::Status::Corruption("Internal inconsistency");
194 }
195
InvalidDBKeyStatus()196 static leveldb::Status InvalidDBKeyStatus() {
197 return leveldb::Status::InvalidArgument("Invalid database key ID");
198 }
199
IOErrorStatus()200 static leveldb::Status IOErrorStatus() {
201 return leveldb::Status::IOError("IO Error");
202 }
203
204 template <typename DBOrTransaction>
GetInt(DBOrTransaction * db,const StringPiece & key,int64 * found_int,bool * found)205 static leveldb::Status GetInt(DBOrTransaction* db,
206 const StringPiece& key,
207 int64* found_int,
208 bool* found) {
209 std::string result;
210 leveldb::Status s = db->Get(key, &result, found);
211 if (!s.ok())
212 return s;
213 if (!*found)
214 return leveldb::Status::OK();
215 StringPiece slice(result);
216 if (DecodeInt(&slice, found_int) && slice.empty())
217 return s;
218 return InternalInconsistencyStatus();
219 }
220
PutInt(LevelDBTransaction * transaction,const StringPiece & key,int64 value)221 static void PutInt(LevelDBTransaction* transaction,
222 const StringPiece& key,
223 int64 value) {
224 DCHECK_GE(value, 0);
225 std::string buffer;
226 EncodeInt(value, &buffer);
227 transaction->Put(key, &buffer);
228 }
229
230 template <typename DBOrTransaction>
GetVarInt(DBOrTransaction * db,const StringPiece & key,int64 * found_int,bool * found)231 WARN_UNUSED_RESULT static leveldb::Status GetVarInt(DBOrTransaction* db,
232 const StringPiece& key,
233 int64* found_int,
234 bool* found) {
235 std::string result;
236 leveldb::Status s = db->Get(key, &result, found);
237 if (!s.ok())
238 return s;
239 if (!*found)
240 return leveldb::Status::OK();
241 StringPiece slice(result);
242 if (DecodeVarInt(&slice, found_int) && slice.empty())
243 return s;
244 return InternalInconsistencyStatus();
245 }
246
PutVarInt(LevelDBTransaction * transaction,const StringPiece & key,int64 value)247 static void PutVarInt(LevelDBTransaction* transaction,
248 const StringPiece& key,
249 int64 value) {
250 std::string buffer;
251 EncodeVarInt(value, &buffer);
252 transaction->Put(key, &buffer);
253 }
254
255 template <typename DBOrTransaction>
GetString(DBOrTransaction * db,const StringPiece & key,base::string16 * found_string,bool * found)256 WARN_UNUSED_RESULT static leveldb::Status GetString(
257 DBOrTransaction* db,
258 const StringPiece& key,
259 base::string16* found_string,
260 bool* found) {
261 std::string result;
262 *found = false;
263 leveldb::Status s = db->Get(key, &result, found);
264 if (!s.ok())
265 return s;
266 if (!*found)
267 return leveldb::Status::OK();
268 StringPiece slice(result);
269 if (DecodeString(&slice, found_string) && slice.empty())
270 return s;
271 return InternalInconsistencyStatus();
272 }
273
PutString(LevelDBTransaction * transaction,const StringPiece & key,const base::string16 & value)274 static void PutString(LevelDBTransaction* transaction,
275 const StringPiece& key,
276 const base::string16& value) {
277 std::string buffer;
278 EncodeString(value, &buffer);
279 transaction->Put(key, &buffer);
280 }
281
PutIDBKeyPath(LevelDBTransaction * transaction,const StringPiece & key,const IndexedDBKeyPath & value)282 static void PutIDBKeyPath(LevelDBTransaction* transaction,
283 const StringPiece& key,
284 const IndexedDBKeyPath& value) {
285 std::string buffer;
286 EncodeIDBKeyPath(value, &buffer);
287 transaction->Put(key, &buffer);
288 }
289
CompareKeys(const StringPiece & a,const StringPiece & b)290 static int CompareKeys(const StringPiece& a, const StringPiece& b) {
291 return Compare(a, b, false /*index_keys*/);
292 }
293
CompareIndexKeys(const StringPiece & a,const StringPiece & b)294 static int CompareIndexKeys(const StringPiece& a, const StringPiece& b) {
295 return Compare(a, b, true /*index_keys*/);
296 }
297
Compare(const StringPiece & a,const StringPiece & b) const298 int IndexedDBBackingStore::Comparator::Compare(const StringPiece& a,
299 const StringPiece& b) const {
300 return content::Compare(a, b, false /*index_keys*/);
301 }
302
Name() const303 const char* IndexedDBBackingStore::Comparator::Name() const {
304 return "idb_cmp1";
305 }
306
307 // 0 - Initial version.
308 // 1 - Adds UserIntVersion to DatabaseMetaData.
309 // 2 - Adds DataVersion to to global metadata.
310 // 3 - Adds metadata needed for blob support.
311 static const int64 kLatestKnownSchemaVersion = 3;
IsSchemaKnown(LevelDBDatabase * db,bool * known)312 WARN_UNUSED_RESULT static bool IsSchemaKnown(LevelDBDatabase* db, bool* known) {
313 int64 db_schema_version = 0;
314 bool found = false;
315 leveldb::Status s =
316 GetInt(db, SchemaVersionKey::Encode(), &db_schema_version, &found);
317 if (!s.ok())
318 return false;
319 if (!found) {
320 *known = true;
321 return true;
322 }
323 if (db_schema_version > kLatestKnownSchemaVersion) {
324 *known = false;
325 return true;
326 }
327
328 const uint32 latest_known_data_version =
329 blink::kSerializedScriptValueVersion;
330 int64 db_data_version = 0;
331 s = GetInt(db, DataVersionKey::Encode(), &db_data_version, &found);
332 if (!s.ok())
333 return false;
334 if (!found) {
335 *known = true;
336 return true;
337 }
338
339 if (db_data_version > latest_known_data_version) {
340 *known = false;
341 return true;
342 }
343
344 *known = true;
345 return true;
346 }
347
348 // TODO(ericu): Move this down into the member section of this file. I'm
349 // leaving it here for this CL as it's easier to see the diffs in place.
SetUpMetadata()350 WARN_UNUSED_RESULT bool IndexedDBBackingStore::SetUpMetadata() {
351 const uint32 latest_known_data_version =
352 blink::kSerializedScriptValueVersion;
353 const std::string schema_version_key = SchemaVersionKey::Encode();
354 const std::string data_version_key = DataVersionKey::Encode();
355
356 scoped_refptr<LevelDBTransaction> transaction =
357 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_.get());
358
359 int64 db_schema_version = 0;
360 int64 db_data_version = 0;
361 bool found = false;
362 leveldb::Status s =
363 GetInt(transaction.get(), schema_version_key, &db_schema_version, &found);
364 if (!s.ok()) {
365 INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA);
366 return false;
367 }
368 if (!found) {
369 // Initialize new backing store.
370 db_schema_version = kLatestKnownSchemaVersion;
371 PutInt(transaction.get(), schema_version_key, db_schema_version);
372 db_data_version = latest_known_data_version;
373 PutInt(transaction.get(), data_version_key, db_data_version);
374 // If a blob directory already exists for this database, blow it away. It's
375 // leftover from a partially-purged previous generation of data.
376 if (!base::DeleteFile(blob_path_, true)) {
377 INTERNAL_WRITE_ERROR_UNTESTED(SET_UP_METADATA);
378 return false;
379 }
380 } else {
381 // Upgrade old backing store.
382 DCHECK_LE(db_schema_version, kLatestKnownSchemaVersion);
383 if (db_schema_version < 1) {
384 db_schema_version = 1;
385 PutInt(transaction.get(), schema_version_key, db_schema_version);
386 const std::string start_key =
387 DatabaseNameKey::EncodeMinKeyForOrigin(origin_identifier_);
388 const std::string stop_key =
389 DatabaseNameKey::EncodeStopKeyForOrigin(origin_identifier_);
390 scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
391 for (s = it->Seek(start_key);
392 s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0;
393 s = it->Next()) {
394 int64 database_id = 0;
395 found = false;
396 s = GetInt(transaction.get(), it->Key(), &database_id, &found);
397 if (!s.ok()) {
398 INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA);
399 return false;
400 }
401 if (!found) {
402 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_UP_METADATA);
403 return false;
404 }
405 std::string int_version_key = DatabaseMetaDataKey::Encode(
406 database_id, DatabaseMetaDataKey::USER_INT_VERSION);
407 PutVarInt(transaction.get(),
408 int_version_key,
409 IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
410 }
411 }
412 if (s.ok() && db_schema_version < 2) {
413 db_schema_version = 2;
414 PutInt(transaction.get(), schema_version_key, db_schema_version);
415 db_data_version = blink::kSerializedScriptValueVersion;
416 PutInt(transaction.get(), data_version_key, db_data_version);
417 }
418 if (db_schema_version < 3) {
419 db_schema_version = 3;
420 if (!base::DeleteFile(blob_path_, true)) {
421 INTERNAL_WRITE_ERROR_UNTESTED(SET_UP_METADATA);
422 return false;
423 }
424 }
425 }
426
427 if (!s.ok()) {
428 INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA);
429 return false;
430 }
431
432 // All new values will be written using this serialization version.
433 found = false;
434 s = GetInt(transaction.get(), data_version_key, &db_data_version, &found);
435 if (!s.ok()) {
436 INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA);
437 return false;
438 }
439 if (!found) {
440 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_UP_METADATA);
441 return false;
442 }
443 if (db_data_version < latest_known_data_version) {
444 db_data_version = latest_known_data_version;
445 PutInt(transaction.get(), data_version_key, db_data_version);
446 }
447
448 DCHECK_EQ(db_schema_version, kLatestKnownSchemaVersion);
449 DCHECK_EQ(db_data_version, latest_known_data_version);
450
451 s = transaction->Commit();
452 if (!s.ok()) {
453 INTERNAL_WRITE_ERROR_UNTESTED(SET_UP_METADATA);
454 return false;
455 }
456 return true;
457 }
458
459 template <typename DBOrTransaction>
GetMaxObjectStoreId(DBOrTransaction * db,int64 database_id,int64 * max_object_store_id)460 WARN_UNUSED_RESULT static leveldb::Status GetMaxObjectStoreId(
461 DBOrTransaction* db,
462 int64 database_id,
463 int64* max_object_store_id) {
464 const std::string max_object_store_id_key = DatabaseMetaDataKey::Encode(
465 database_id, DatabaseMetaDataKey::MAX_OBJECT_STORE_ID);
466 return GetMaxObjectStoreId(db, max_object_store_id_key, max_object_store_id);
467 }
468
469 template <typename DBOrTransaction>
GetMaxObjectStoreId(DBOrTransaction * db,const std::string & max_object_store_id_key,int64 * max_object_store_id)470 WARN_UNUSED_RESULT static leveldb::Status GetMaxObjectStoreId(
471 DBOrTransaction* db,
472 const std::string& max_object_store_id_key,
473 int64* max_object_store_id) {
474 *max_object_store_id = -1;
475 bool found = false;
476 leveldb::Status s =
477 GetInt(db, max_object_store_id_key, max_object_store_id, &found);
478 if (!s.ok())
479 return s;
480 if (!found)
481 *max_object_store_id = 0;
482
483 DCHECK_GE(*max_object_store_id, 0);
484 return s;
485 }
486
487 class DefaultLevelDBFactory : public LevelDBFactory {
488 public:
DefaultLevelDBFactory()489 DefaultLevelDBFactory() {}
OpenLevelDB(const base::FilePath & file_name,const LevelDBComparator * comparator,scoped_ptr<LevelDBDatabase> * db,bool * is_disk_full)490 virtual leveldb::Status OpenLevelDB(const base::FilePath& file_name,
491 const LevelDBComparator* comparator,
492 scoped_ptr<LevelDBDatabase>* db,
493 bool* is_disk_full) OVERRIDE {
494 return LevelDBDatabase::Open(file_name, comparator, db, is_disk_full);
495 }
DestroyLevelDB(const base::FilePath & file_name)496 virtual leveldb::Status DestroyLevelDB(const base::FilePath& file_name)
497 OVERRIDE {
498 return LevelDBDatabase::Destroy(file_name);
499 }
500
501 private:
502 DISALLOW_COPY_AND_ASSIGN(DefaultLevelDBFactory);
503 };
504
GetBlobKeyGeneratorCurrentNumber(LevelDBTransaction * leveldb_transaction,int64 database_id,int64 * blob_key_generator_current_number)505 static bool GetBlobKeyGeneratorCurrentNumber(
506 LevelDBTransaction* leveldb_transaction,
507 int64 database_id,
508 int64* blob_key_generator_current_number) {
509 const std::string key_gen_key = DatabaseMetaDataKey::Encode(
510 database_id, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER);
511
512 // Default to initial number if not found.
513 int64 cur_number = DatabaseMetaDataKey::kBlobKeyGeneratorInitialNumber;
514 std::string data;
515
516 bool found = false;
517 bool ok = leveldb_transaction->Get(key_gen_key, &data, &found).ok();
518 if (!ok) {
519 INTERNAL_READ_ERROR_UNTESTED(GET_BLOB_KEY_GENERATOR_CURRENT_NUMBER);
520 return false;
521 }
522 if (found) {
523 StringPiece slice(data);
524 if (!DecodeVarInt(&slice, &cur_number) || !slice.empty() ||
525 !DatabaseMetaDataKey::IsValidBlobKey(cur_number)) {
526 INTERNAL_READ_ERROR_UNTESTED(GET_BLOB_KEY_GENERATOR_CURRENT_NUMBER);
527 return false;
528 }
529 }
530 *blob_key_generator_current_number = cur_number;
531 return true;
532 }
533
UpdateBlobKeyGeneratorCurrentNumber(LevelDBTransaction * leveldb_transaction,int64 database_id,int64 blob_key_generator_current_number)534 static bool UpdateBlobKeyGeneratorCurrentNumber(
535 LevelDBTransaction* leveldb_transaction,
536 int64 database_id,
537 int64 blob_key_generator_current_number) {
538 #ifndef NDEBUG
539 int64 old_number;
540 if (!GetBlobKeyGeneratorCurrentNumber(
541 leveldb_transaction, database_id, &old_number))
542 return false;
543 DCHECK_LT(old_number, blob_key_generator_current_number);
544 #endif
545 DCHECK(
546 DatabaseMetaDataKey::IsValidBlobKey(blob_key_generator_current_number));
547 const std::string key = DatabaseMetaDataKey::Encode(
548 database_id, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER);
549
550 PutVarInt(leveldb_transaction, key, blob_key_generator_current_number);
551 return true;
552 }
553
554 // TODO(ericu): Error recovery. If we persistently can't read the
555 // blob journal, the safe thing to do is to clear it and leak the blobs,
556 // though that may be costly. Still, database/directory deletion should always
557 // clean things up, and we can write an fsck that will do a full correction if
558 // need be.
559 template <typename T>
GetBlobJournal(const StringPiece & leveldb_key,T * leveldb_transaction,BlobJournalType * journal)560 static leveldb::Status GetBlobJournal(const StringPiece& leveldb_key,
561 T* leveldb_transaction,
562 BlobJournalType* journal) {
563 std::string data;
564 bool found = false;
565 leveldb::Status s = leveldb_transaction->Get(leveldb_key, &data, &found);
566 if (!s.ok()) {
567 INTERNAL_READ_ERROR_UNTESTED(READ_BLOB_JOURNAL);
568 return s;
569 }
570 journal->clear();
571 if (!found || !data.size())
572 return leveldb::Status::OK();
573 StringPiece slice(data);
574 if (!DecodeBlobJournal(&slice, journal)) {
575 INTERNAL_READ_ERROR_UNTESTED(DECODE_BLOB_JOURNAL);
576 s = InternalInconsistencyStatus();
577 }
578 return s;
579 }
580
ClearBlobJournal(LevelDBTransaction * leveldb_transaction,const std::string & level_db_key)581 static void ClearBlobJournal(LevelDBTransaction* leveldb_transaction,
582 const std::string& level_db_key) {
583 leveldb_transaction->Remove(level_db_key);
584 }
585
UpdatePrimaryJournalWithBlobList(LevelDBTransaction * leveldb_transaction,const BlobJournalType & journal)586 static void UpdatePrimaryJournalWithBlobList(
587 LevelDBTransaction* leveldb_transaction,
588 const BlobJournalType& journal) {
589 const std::string leveldb_key = BlobJournalKey::Encode();
590 std::string data;
591 EncodeBlobJournal(journal, &data);
592 leveldb_transaction->Put(leveldb_key, &data);
593 }
594
UpdateLiveBlobJournalWithBlobList(LevelDBTransaction * leveldb_transaction,const BlobJournalType & journal)595 static void UpdateLiveBlobJournalWithBlobList(
596 LevelDBTransaction* leveldb_transaction,
597 const BlobJournalType& journal) {
598 const std::string leveldb_key = LiveBlobJournalKey::Encode();
599 std::string data;
600 EncodeBlobJournal(journal, &data);
601 leveldb_transaction->Put(leveldb_key, &data);
602 }
603
MergeBlobsIntoLiveBlobJournal(LevelDBTransaction * leveldb_transaction,const BlobJournalType & journal)604 static leveldb::Status MergeBlobsIntoLiveBlobJournal(
605 LevelDBTransaction* leveldb_transaction,
606 const BlobJournalType& journal) {
607 BlobJournalType old_journal;
608 const std::string key = LiveBlobJournalKey::Encode();
609 leveldb::Status s = GetBlobJournal(key, leveldb_transaction, &old_journal);
610 if (!s.ok())
611 return s;
612
613 old_journal.insert(old_journal.end(), journal.begin(), journal.end());
614
615 UpdateLiveBlobJournalWithBlobList(leveldb_transaction, old_journal);
616 return leveldb::Status::OK();
617 }
618
UpdateBlobJournalWithDatabase(LevelDBDirectTransaction * leveldb_transaction,int64 database_id)619 static void UpdateBlobJournalWithDatabase(
620 LevelDBDirectTransaction* leveldb_transaction,
621 int64 database_id) {
622 BlobJournalType journal;
623 journal.push_back(
624 std::make_pair(database_id, DatabaseMetaDataKey::kAllBlobsKey));
625 const std::string key = BlobJournalKey::Encode();
626 std::string data;
627 EncodeBlobJournal(journal, &data);
628 leveldb_transaction->Put(key, &data);
629 }
630
MergeDatabaseIntoLiveBlobJournal(LevelDBDirectTransaction * leveldb_transaction,int64 database_id)631 static leveldb::Status MergeDatabaseIntoLiveBlobJournal(
632 LevelDBDirectTransaction* leveldb_transaction,
633 int64 database_id) {
634 BlobJournalType journal;
635 const std::string key = LiveBlobJournalKey::Encode();
636 leveldb::Status s = GetBlobJournal(key, leveldb_transaction, &journal);
637 if (!s.ok())
638 return s;
639 journal.push_back(
640 std::make_pair(database_id, DatabaseMetaDataKey::kAllBlobsKey));
641 std::string data;
642 EncodeBlobJournal(journal, &data);
643 leveldb_transaction->Put(key, &data);
644 return leveldb::Status::OK();
645 }
646
647 // Blob Data is encoded as a series of:
648 // { is_file [bool], key [int64 as varInt],
649 // type [string-with-length, may be empty],
650 // (for Blobs only) size [int64 as varInt]
651 // (for Files only) fileName [string-with-length]
652 // }
653 // There is no length field; just read until you run out of data.
EncodeBlobData(const std::vector<IndexedDBBlobInfo * > & blob_info)654 static std::string EncodeBlobData(
655 const std::vector<IndexedDBBlobInfo*>& blob_info) {
656 std::string ret;
657 std::vector<IndexedDBBlobInfo*>::const_iterator iter;
658 for (iter = blob_info.begin(); iter != blob_info.end(); ++iter) {
659 const IndexedDBBlobInfo& info = **iter;
660 EncodeBool(info.is_file(), &ret);
661 EncodeVarInt(info.key(), &ret);
662 EncodeStringWithLength(info.type(), &ret);
663 if (info.is_file())
664 EncodeStringWithLength(info.file_name(), &ret);
665 else
666 EncodeVarInt(info.size(), &ret);
667 }
668 return ret;
669 }
670
DecodeBlobData(const std::string & data,std::vector<IndexedDBBlobInfo> * output)671 static bool DecodeBlobData(const std::string& data,
672 std::vector<IndexedDBBlobInfo>* output) {
673 std::vector<IndexedDBBlobInfo> ret;
674 output->clear();
675 StringPiece slice(data);
676 while (!slice.empty()) {
677 bool is_file;
678 int64 key;
679 base::string16 type;
680 int64 size;
681 base::string16 file_name;
682
683 if (!DecodeBool(&slice, &is_file))
684 return false;
685 if (!DecodeVarInt(&slice, &key) ||
686 !DatabaseMetaDataKey::IsValidBlobKey(key))
687 return false;
688 if (!DecodeStringWithLength(&slice, &type))
689 return false;
690 if (is_file) {
691 if (!DecodeStringWithLength(&slice, &file_name))
692 return false;
693 ret.push_back(IndexedDBBlobInfo(key, type, file_name));
694 } else {
695 if (!DecodeVarInt(&slice, &size) || size < 0)
696 return false;
697 ret.push_back(IndexedDBBlobInfo(type, static_cast<uint64>(size), key));
698 }
699 }
700 output->swap(ret);
701
702 return true;
703 }
704
IndexedDBBackingStore(IndexedDBFactory * indexed_db_factory,const GURL & origin_url,const base::FilePath & blob_path,net::URLRequestContext * request_context,scoped_ptr<LevelDBDatabase> db,scoped_ptr<LevelDBComparator> comparator,base::TaskRunner * task_runner)705 IndexedDBBackingStore::IndexedDBBackingStore(
706 IndexedDBFactory* indexed_db_factory,
707 const GURL& origin_url,
708 const base::FilePath& blob_path,
709 net::URLRequestContext* request_context,
710 scoped_ptr<LevelDBDatabase> db,
711 scoped_ptr<LevelDBComparator> comparator,
712 base::TaskRunner* task_runner)
713 : indexed_db_factory_(indexed_db_factory),
714 origin_url_(origin_url),
715 blob_path_(blob_path),
716 origin_identifier_(ComputeOriginIdentifier(origin_url)),
717 request_context_(request_context),
718 task_runner_(task_runner),
719 db_(db.Pass()),
720 comparator_(comparator.Pass()),
721 active_blob_registry_(this) {
722 }
723
~IndexedDBBackingStore()724 IndexedDBBackingStore::~IndexedDBBackingStore() {
725 if (!blob_path_.empty() && !child_process_ids_granted_.empty()) {
726 ChildProcessSecurityPolicyImpl* policy =
727 ChildProcessSecurityPolicyImpl::GetInstance();
728 std::set<int>::const_iterator iter;
729 for (iter = child_process_ids_granted_.begin();
730 iter != child_process_ids_granted_.end();
731 ++iter) {
732 policy->RevokeAllPermissionsForFile(*iter, blob_path_);
733 }
734 }
735 STLDeleteContainerPairSecondPointers(incognito_blob_map_.begin(),
736 incognito_blob_map_.end());
737 // db_'s destructor uses comparator_. The order of destruction is important.
738 db_.reset();
739 comparator_.reset();
740 }
741
RecordIdentifier(const std::string & primary_key,int64 version)742 IndexedDBBackingStore::RecordIdentifier::RecordIdentifier(
743 const std::string& primary_key,
744 int64 version)
745 : primary_key_(primary_key), version_(version) {
746 DCHECK(!primary_key.empty());
747 }
RecordIdentifier()748 IndexedDBBackingStore::RecordIdentifier::RecordIdentifier()
749 : primary_key_(), version_(-1) {}
~RecordIdentifier()750 IndexedDBBackingStore::RecordIdentifier::~RecordIdentifier() {}
751
CursorOptions()752 IndexedDBBackingStore::Cursor::CursorOptions::CursorOptions() {}
~CursorOptions()753 IndexedDBBackingStore::Cursor::CursorOptions::~CursorOptions() {}
754
755 enum IndexedDBBackingStoreOpenResult {
756 INDEXED_DB_BACKING_STORE_OPEN_MEMORY_SUCCESS,
757 INDEXED_DB_BACKING_STORE_OPEN_SUCCESS,
758 INDEXED_DB_BACKING_STORE_OPEN_FAILED_DIRECTORY,
759 INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_SCHEMA,
760 INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_DESTROY_FAILED,
761 INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_FAILED,
762 INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_SUCCESS,
763 INDEXED_DB_BACKING_STORE_OPEN_FAILED_IO_ERROR_CHECKING_SCHEMA,
764 INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_ERR,
765 INDEXED_DB_BACKING_STORE_OPEN_MEMORY_FAILED,
766 INDEXED_DB_BACKING_STORE_OPEN_ATTEMPT_NON_ASCII,
767 INDEXED_DB_BACKING_STORE_OPEN_DISK_FULL_DEPRECATED,
768 INDEXED_DB_BACKING_STORE_OPEN_ORIGIN_TOO_LONG,
769 INDEXED_DB_BACKING_STORE_OPEN_NO_RECOVERY,
770 INDEXED_DB_BACKING_STORE_OPEN_FAILED_PRIOR_CORRUPTION,
771 INDEXED_DB_BACKING_STORE_OPEN_FAILED_CLEANUP_JOURNAL_ERROR,
772 INDEXED_DB_BACKING_STORE_OPEN_MAX,
773 };
774
775 // static
Open(IndexedDBFactory * indexed_db_factory,const GURL & origin_url,const base::FilePath & path_base,net::URLRequestContext * request_context,blink::WebIDBDataLoss * data_loss,std::string * data_loss_message,bool * disk_full,base::TaskRunner * task_runner,bool clean_journal)776 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open(
777 IndexedDBFactory* indexed_db_factory,
778 const GURL& origin_url,
779 const base::FilePath& path_base,
780 net::URLRequestContext* request_context,
781 blink::WebIDBDataLoss* data_loss,
782 std::string* data_loss_message,
783 bool* disk_full,
784 base::TaskRunner* task_runner,
785 bool clean_journal) {
786 *data_loss = blink::WebIDBDataLossNone;
787 DefaultLevelDBFactory leveldb_factory;
788 return IndexedDBBackingStore::Open(indexed_db_factory,
789 origin_url,
790 path_base,
791 request_context,
792 data_loss,
793 data_loss_message,
794 disk_full,
795 &leveldb_factory,
796 task_runner,
797 clean_journal);
798 }
799
OriginToCustomHistogramSuffix(const GURL & origin_url)800 static std::string OriginToCustomHistogramSuffix(const GURL& origin_url) {
801 if (origin_url.host() == "docs.google.com")
802 return ".Docs";
803 return std::string();
804 }
805
HistogramOpenStatus(IndexedDBBackingStoreOpenResult result,const GURL & origin_url)806 static void HistogramOpenStatus(IndexedDBBackingStoreOpenResult result,
807 const GURL& origin_url) {
808 UMA_HISTOGRAM_ENUMERATION("WebCore.IndexedDB.BackingStore.OpenStatus",
809 result,
810 INDEXED_DB_BACKING_STORE_OPEN_MAX);
811 const std::string suffix = OriginToCustomHistogramSuffix(origin_url);
812 // Data from the WebCore.IndexedDB.BackingStore.OpenStatus histogram is used
813 // to generate a graph. So as not to alter the meaning of that graph,
814 // continue to collect all stats there (above) but also now collect docs stats
815 // separately (below).
816 if (!suffix.empty()) {
817 base::LinearHistogram::FactoryGet(
818 "WebCore.IndexedDB.BackingStore.OpenStatus" + suffix,
819 1,
820 INDEXED_DB_BACKING_STORE_OPEN_MAX,
821 INDEXED_DB_BACKING_STORE_OPEN_MAX + 1,
822 base::HistogramBase::kUmaTargetedHistogramFlag)->Add(result);
823 }
824 }
825
IsPathTooLong(const base::FilePath & leveldb_dir)826 static bool IsPathTooLong(const base::FilePath& leveldb_dir) {
827 int limit = base::GetMaximumPathComponentLength(leveldb_dir.DirName());
828 if (limit == -1) {
829 DLOG(WARNING) << "GetMaximumPathComponentLength returned -1";
830 // In limited testing, ChromeOS returns 143, other OSes 255.
831 #if defined(OS_CHROMEOS)
832 limit = 143;
833 #else
834 limit = 255;
835 #endif
836 }
837 size_t component_length = leveldb_dir.BaseName().value().length();
838 if (component_length > static_cast<uint32_t>(limit)) {
839 DLOG(WARNING) << "Path component length (" << component_length
840 << ") exceeds maximum (" << limit
841 << ") allowed by this filesystem.";
842 const int min = 140;
843 const int max = 300;
844 const int num_buckets = 12;
845 UMA_HISTOGRAM_CUSTOM_COUNTS(
846 "WebCore.IndexedDB.BackingStore.OverlyLargeOriginLength",
847 component_length,
848 min,
849 max,
850 num_buckets);
851 return true;
852 }
853 return false;
854 }
855
DestroyBackingStore(const base::FilePath & path_base,const GURL & origin_url)856 leveldb::Status IndexedDBBackingStore::DestroyBackingStore(
857 const base::FilePath& path_base,
858 const GURL& origin_url) {
859 const base::FilePath file_path =
860 path_base.Append(ComputeFileName(origin_url));
861 DefaultLevelDBFactory leveldb_factory;
862 return leveldb_factory.DestroyLevelDB(file_path);
863 }
864
ReadCorruptionInfo(const base::FilePath & path_base,const GURL & origin_url,std::string & message)865 bool IndexedDBBackingStore::ReadCorruptionInfo(const base::FilePath& path_base,
866 const GURL& origin_url,
867 std::string& message) {
868 const base::FilePath info_path =
869 path_base.Append(ComputeCorruptionFileName(origin_url));
870
871 if (IsPathTooLong(info_path))
872 return false;
873
874 const int64 max_json_len = 4096;
875 int64 file_size(0);
876 if (!GetFileSize(info_path, &file_size) || file_size > max_json_len)
877 return false;
878 if (!file_size) {
879 NOTREACHED();
880 return false;
881 }
882
883 base::File file(info_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
884 bool success = false;
885 if (file.IsValid()) {
886 std::vector<char> bytes(file_size);
887 if (file_size == file.Read(0, &bytes[0], file_size)) {
888 std::string input_js(&bytes[0], file_size);
889 base::JSONReader reader;
890 scoped_ptr<base::Value> val(reader.ReadToValue(input_js));
891 if (val && val->GetType() == base::Value::TYPE_DICTIONARY) {
892 base::DictionaryValue* dict_val =
893 static_cast<base::DictionaryValue*>(val.get());
894 success = dict_val->GetString("message", &message);
895 }
896 }
897 file.Close();
898 }
899
900 base::DeleteFile(info_path, false);
901
902 return success;
903 }
904
RecordCorruptionInfo(const base::FilePath & path_base,const GURL & origin_url,const std::string & message)905 bool IndexedDBBackingStore::RecordCorruptionInfo(
906 const base::FilePath& path_base,
907 const GURL& origin_url,
908 const std::string& message) {
909 const base::FilePath info_path =
910 path_base.Append(ComputeCorruptionFileName(origin_url));
911 if (IsPathTooLong(info_path))
912 return false;
913
914 base::DictionaryValue root_dict;
915 root_dict.SetString("message", message);
916 std::string output_js;
917 base::JSONWriter::Write(&root_dict, &output_js);
918
919 base::File file(info_path,
920 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
921 if (!file.IsValid())
922 return false;
923 int written = file.Write(0, output_js.c_str(), output_js.length());
924 return size_t(written) == output_js.length();
925 }
926
927 // static
Open(IndexedDBFactory * indexed_db_factory,const GURL & origin_url,const base::FilePath & path_base,net::URLRequestContext * request_context,blink::WebIDBDataLoss * data_loss,std::string * data_loss_message,bool * is_disk_full,LevelDBFactory * leveldb_factory,base::TaskRunner * task_runner,bool clean_journal)928 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open(
929 IndexedDBFactory* indexed_db_factory,
930 const GURL& origin_url,
931 const base::FilePath& path_base,
932 net::URLRequestContext* request_context,
933 blink::WebIDBDataLoss* data_loss,
934 std::string* data_loss_message,
935 bool* is_disk_full,
936 LevelDBFactory* leveldb_factory,
937 base::TaskRunner* task_runner,
938 bool clean_journal) {
939 IDB_TRACE("IndexedDBBackingStore::Open");
940 DCHECK(!path_base.empty());
941 *data_loss = blink::WebIDBDataLossNone;
942 *data_loss_message = "";
943 *is_disk_full = false;
944
945 scoped_ptr<LevelDBComparator> comparator(new Comparator());
946
947 if (!base::IsStringASCII(path_base.AsUTF8Unsafe())) {
948 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_ATTEMPT_NON_ASCII,
949 origin_url);
950 }
951 if (!base::CreateDirectory(path_base)) {
952 LOG(ERROR) << "Unable to create IndexedDB database path "
953 << path_base.AsUTF8Unsafe();
954 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_DIRECTORY,
955 origin_url);
956 return scoped_refptr<IndexedDBBackingStore>();
957 }
958
959 const base::FilePath file_path =
960 path_base.Append(ComputeFileName(origin_url));
961 const base::FilePath blob_path =
962 path_base.Append(ComputeBlobPath(origin_url));
963
964 if (IsPathTooLong(file_path)) {
965 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_ORIGIN_TOO_LONG,
966 origin_url);
967 return scoped_refptr<IndexedDBBackingStore>();
968 }
969
970 scoped_ptr<LevelDBDatabase> db;
971 leveldb::Status status = leveldb_factory->OpenLevelDB(
972 file_path, comparator.get(), &db, is_disk_full);
973
974 DCHECK(!db == !status.ok());
975 if (!status.ok()) {
976 if (leveldb_env::IndicatesDiskFull(status)) {
977 *is_disk_full = true;
978 } else if (leveldb_env::IsCorruption(status)) {
979 *data_loss = blink::WebIDBDataLossTotal;
980 *data_loss_message = leveldb_env::GetCorruptionMessage(status);
981 }
982 }
983
984 bool is_schema_known = false;
985 if (db) {
986 std::string corruption_message;
987 if (ReadCorruptionInfo(path_base, origin_url, corruption_message)) {
988 LOG(ERROR) << "IndexedDB recovering from a corrupted (and deleted) "
989 "database.";
990 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_PRIOR_CORRUPTION,
991 origin_url);
992 db.reset();
993 *data_loss = blink::WebIDBDataLossTotal;
994 *data_loss_message =
995 "IndexedDB (database was corrupt): " + corruption_message;
996 } else if (!IsSchemaKnown(db.get(), &is_schema_known)) {
997 LOG(ERROR) << "IndexedDB had IO error checking schema, treating it as "
998 "failure to open";
999 HistogramOpenStatus(
1000 INDEXED_DB_BACKING_STORE_OPEN_FAILED_IO_ERROR_CHECKING_SCHEMA,
1001 origin_url);
1002 db.reset();
1003 *data_loss = blink::WebIDBDataLossTotal;
1004 *data_loss_message = "I/O error checking schema";
1005 } else if (!is_schema_known) {
1006 LOG(ERROR) << "IndexedDB backing store had unknown schema, treating it "
1007 "as failure to open";
1008 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_SCHEMA,
1009 origin_url);
1010 db.reset();
1011 *data_loss = blink::WebIDBDataLossTotal;
1012 *data_loss_message = "Unknown schema";
1013 }
1014 }
1015
1016 DCHECK(status.ok() || !is_schema_known || leveldb_env::IsIOError(status) ||
1017 leveldb_env::IsCorruption(status));
1018
1019 if (db) {
1020 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_SUCCESS, origin_url);
1021 } else if (leveldb_env::IsIOError(status)) {
1022 LOG(ERROR) << "Unable to open backing store, not trying to recover - "
1023 << status.ToString();
1024 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_NO_RECOVERY, origin_url);
1025 return scoped_refptr<IndexedDBBackingStore>();
1026 } else {
1027 DCHECK(!is_schema_known || leveldb_env::IsCorruption(status));
1028 LOG(ERROR) << "IndexedDB backing store open failed, attempting cleanup";
1029 status = leveldb_factory->DestroyLevelDB(file_path);
1030 if (!status.ok()) {
1031 LOG(ERROR) << "IndexedDB backing store cleanup failed";
1032 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_DESTROY_FAILED,
1033 origin_url);
1034 return scoped_refptr<IndexedDBBackingStore>();
1035 }
1036
1037 LOG(ERROR) << "IndexedDB backing store cleanup succeeded, reopening";
1038 leveldb_factory->OpenLevelDB(file_path, comparator.get(), &db, NULL);
1039 if (!db) {
1040 LOG(ERROR) << "IndexedDB backing store reopen after recovery failed";
1041 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_FAILED,
1042 origin_url);
1043 return scoped_refptr<IndexedDBBackingStore>();
1044 }
1045 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_SUCCESS,
1046 origin_url);
1047 }
1048
1049 if (!db) {
1050 NOTREACHED();
1051 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_ERR,
1052 origin_url);
1053 return scoped_refptr<IndexedDBBackingStore>();
1054 }
1055
1056 scoped_refptr<IndexedDBBackingStore> backing_store =
1057 Create(indexed_db_factory,
1058 origin_url,
1059 blob_path,
1060 request_context,
1061 db.Pass(),
1062 comparator.Pass(),
1063 task_runner);
1064
1065 if (clean_journal && backing_store &&
1066 !backing_store->CleanUpBlobJournal(LiveBlobJournalKey::Encode()).ok()) {
1067 HistogramOpenStatus(
1068 INDEXED_DB_BACKING_STORE_OPEN_FAILED_CLEANUP_JOURNAL_ERROR, origin_url);
1069 return scoped_refptr<IndexedDBBackingStore>();
1070 }
1071 return backing_store;
1072 }
1073
1074 // static
OpenInMemory(const GURL & origin_url,base::TaskRunner * task_runner)1075 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::OpenInMemory(
1076 const GURL& origin_url,
1077 base::TaskRunner* task_runner) {
1078 DefaultLevelDBFactory leveldb_factory;
1079 return IndexedDBBackingStore::OpenInMemory(
1080 origin_url, &leveldb_factory, task_runner);
1081 }
1082
1083 // static
OpenInMemory(const GURL & origin_url,LevelDBFactory * leveldb_factory,base::TaskRunner * task_runner)1084 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::OpenInMemory(
1085 const GURL& origin_url,
1086 LevelDBFactory* leveldb_factory,
1087 base::TaskRunner* task_runner) {
1088 IDB_TRACE("IndexedDBBackingStore::OpenInMemory");
1089
1090 scoped_ptr<LevelDBComparator> comparator(new Comparator());
1091 scoped_ptr<LevelDBDatabase> db =
1092 LevelDBDatabase::OpenInMemory(comparator.get());
1093 if (!db) {
1094 LOG(ERROR) << "LevelDBDatabase::OpenInMemory failed.";
1095 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_MEMORY_FAILED,
1096 origin_url);
1097 return scoped_refptr<IndexedDBBackingStore>();
1098 }
1099 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_MEMORY_SUCCESS, origin_url);
1100
1101 return Create(NULL /* indexed_db_factory */,
1102 origin_url,
1103 base::FilePath(),
1104 NULL /* request_context */,
1105 db.Pass(),
1106 comparator.Pass(),
1107 task_runner);
1108 }
1109
1110 // static
Create(IndexedDBFactory * indexed_db_factory,const GURL & origin_url,const base::FilePath & blob_path,net::URLRequestContext * request_context,scoped_ptr<LevelDBDatabase> db,scoped_ptr<LevelDBComparator> comparator,base::TaskRunner * task_runner)1111 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Create(
1112 IndexedDBFactory* indexed_db_factory,
1113 const GURL& origin_url,
1114 const base::FilePath& blob_path,
1115 net::URLRequestContext* request_context,
1116 scoped_ptr<LevelDBDatabase> db,
1117 scoped_ptr<LevelDBComparator> comparator,
1118 base::TaskRunner* task_runner) {
1119 // TODO(jsbell): Handle comparator name changes.
1120 scoped_refptr<IndexedDBBackingStore> backing_store(
1121 new IndexedDBBackingStore(indexed_db_factory,
1122 origin_url,
1123 blob_path,
1124 request_context,
1125 db.Pass(),
1126 comparator.Pass(),
1127 task_runner));
1128 if (!backing_store->SetUpMetadata())
1129 return scoped_refptr<IndexedDBBackingStore>();
1130
1131 return backing_store;
1132 }
1133
GrantChildProcessPermissions(int child_process_id)1134 void IndexedDBBackingStore::GrantChildProcessPermissions(int child_process_id) {
1135 if (!child_process_ids_granted_.count(child_process_id)) {
1136 child_process_ids_granted_.insert(child_process_id);
1137 ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadFile(
1138 child_process_id, blob_path_);
1139 }
1140 }
1141
GetDatabaseNames(leveldb::Status * s)1142 std::vector<base::string16> IndexedDBBackingStore::GetDatabaseNames(
1143 leveldb::Status* s) {
1144 *s = leveldb::Status::OK();
1145 std::vector<base::string16> found_names;
1146 const std::string start_key =
1147 DatabaseNameKey::EncodeMinKeyForOrigin(origin_identifier_);
1148 const std::string stop_key =
1149 DatabaseNameKey::EncodeStopKeyForOrigin(origin_identifier_);
1150
1151 DCHECK(found_names.empty());
1152
1153 scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
1154 for (*s = it->Seek(start_key);
1155 s->ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0;
1156 *s = it->Next()) {
1157 StringPiece slice(it->Key());
1158 DatabaseNameKey database_name_key;
1159 if (!DatabaseNameKey::Decode(&slice, &database_name_key) ||
1160 !slice.empty()) {
1161 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_DATABASE_NAMES);
1162 continue;
1163 }
1164 found_names.push_back(database_name_key.database_name());
1165 }
1166
1167 if (!s->ok())
1168 INTERNAL_READ_ERROR_UNTESTED(GET_DATABASE_NAMES);
1169
1170 return found_names;
1171 }
1172
GetIDBDatabaseMetaData(const base::string16 & name,IndexedDBDatabaseMetadata * metadata,bool * found)1173 leveldb::Status IndexedDBBackingStore::GetIDBDatabaseMetaData(
1174 const base::string16& name,
1175 IndexedDBDatabaseMetadata* metadata,
1176 bool* found) {
1177 const std::string key = DatabaseNameKey::Encode(origin_identifier_, name);
1178 *found = false;
1179
1180 leveldb::Status s = GetInt(db_.get(), key, &metadata->id, found);
1181 if (!s.ok()) {
1182 INTERNAL_READ_ERROR(GET_IDBDATABASE_METADATA);
1183 return s;
1184 }
1185 if (!*found)
1186 return leveldb::Status::OK();
1187
1188 s = GetString(db_.get(),
1189 DatabaseMetaDataKey::Encode(metadata->id,
1190 DatabaseMetaDataKey::USER_VERSION),
1191 &metadata->version,
1192 found);
1193 if (!s.ok()) {
1194 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1195 return s;
1196 }
1197 if (!*found) {
1198 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1199 return InternalInconsistencyStatus();
1200 }
1201
1202 s = GetVarInt(db_.get(),
1203 DatabaseMetaDataKey::Encode(
1204 metadata->id, DatabaseMetaDataKey::USER_INT_VERSION),
1205 &metadata->int_version,
1206 found);
1207 if (!s.ok()) {
1208 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1209 return s;
1210 }
1211 if (!*found) {
1212 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1213 return InternalInconsistencyStatus();
1214 }
1215
1216 if (metadata->int_version == IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION)
1217 metadata->int_version = IndexedDBDatabaseMetadata::NO_INT_VERSION;
1218
1219 s = GetMaxObjectStoreId(
1220 db_.get(), metadata->id, &metadata->max_object_store_id);
1221 if (!s.ok()) {
1222 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1223 }
1224
1225 // We don't cache this, we just check it if it's there.
1226 int64 blob_key_generator_current_number =
1227 DatabaseMetaDataKey::kInvalidBlobKey;
1228
1229 s = GetVarInt(
1230 db_.get(),
1231 DatabaseMetaDataKey::Encode(
1232 metadata->id, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER),
1233 &blob_key_generator_current_number,
1234 found);
1235 if (!s.ok()) {
1236 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1237 return s;
1238 }
1239 if (!*found) {
1240 // This database predates blob support.
1241 *found = true;
1242 } else if (!DatabaseMetaDataKey::IsValidBlobKey(
1243 blob_key_generator_current_number)) {
1244 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1245 return InternalInconsistencyStatus();
1246 }
1247
1248 return s;
1249 }
1250
GetNewDatabaseId(LevelDBTransaction * transaction,int64 * new_id)1251 WARN_UNUSED_RESULT static leveldb::Status GetNewDatabaseId(
1252 LevelDBTransaction* transaction,
1253 int64* new_id) {
1254 *new_id = -1;
1255 int64 max_database_id = -1;
1256 bool found = false;
1257 leveldb::Status s =
1258 GetInt(transaction, MaxDatabaseIdKey::Encode(), &max_database_id, &found);
1259 if (!s.ok()) {
1260 INTERNAL_READ_ERROR_UNTESTED(GET_NEW_DATABASE_ID);
1261 return s;
1262 }
1263 if (!found)
1264 max_database_id = 0;
1265
1266 DCHECK_GE(max_database_id, 0);
1267
1268 int64 database_id = max_database_id + 1;
1269 PutInt(transaction, MaxDatabaseIdKey::Encode(), database_id);
1270 *new_id = database_id;
1271 return leveldb::Status::OK();
1272 }
1273
CreateIDBDatabaseMetaData(const base::string16 & name,const base::string16 & version,int64 int_version,int64 * row_id)1274 leveldb::Status IndexedDBBackingStore::CreateIDBDatabaseMetaData(
1275 const base::string16& name,
1276 const base::string16& version,
1277 int64 int_version,
1278 int64* row_id) {
1279 scoped_refptr<LevelDBTransaction> transaction =
1280 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_.get());
1281
1282 leveldb::Status s = GetNewDatabaseId(transaction.get(), row_id);
1283 if (!s.ok())
1284 return s;
1285 DCHECK_GE(*row_id, 0);
1286
1287 if (int_version == IndexedDBDatabaseMetadata::NO_INT_VERSION)
1288 int_version = IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION;
1289
1290 PutInt(transaction.get(),
1291 DatabaseNameKey::Encode(origin_identifier_, name),
1292 *row_id);
1293 PutString(
1294 transaction.get(),
1295 DatabaseMetaDataKey::Encode(*row_id, DatabaseMetaDataKey::USER_VERSION),
1296 version);
1297 PutVarInt(transaction.get(),
1298 DatabaseMetaDataKey::Encode(*row_id,
1299 DatabaseMetaDataKey::USER_INT_VERSION),
1300 int_version);
1301 PutVarInt(
1302 transaction.get(),
1303 DatabaseMetaDataKey::Encode(
1304 *row_id, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER),
1305 DatabaseMetaDataKey::kBlobKeyGeneratorInitialNumber);
1306
1307 s = transaction->Commit();
1308 if (!s.ok())
1309 INTERNAL_WRITE_ERROR_UNTESTED(CREATE_IDBDATABASE_METADATA);
1310 return s;
1311 }
1312
UpdateIDBDatabaseIntVersion(IndexedDBBackingStore::Transaction * transaction,int64 row_id,int64 int_version)1313 bool IndexedDBBackingStore::UpdateIDBDatabaseIntVersion(
1314 IndexedDBBackingStore::Transaction* transaction,
1315 int64 row_id,
1316 int64 int_version) {
1317 if (int_version == IndexedDBDatabaseMetadata::NO_INT_VERSION)
1318 int_version = IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION;
1319 DCHECK_GE(int_version, 0) << "int_version was " << int_version;
1320 PutVarInt(transaction->transaction(),
1321 DatabaseMetaDataKey::Encode(row_id,
1322 DatabaseMetaDataKey::USER_INT_VERSION),
1323 int_version);
1324 return true;
1325 }
1326
1327 // If you're deleting a range that contains user keys that have blob info, this
1328 // won't clean up the blobs.
DeleteRangeBasic(LevelDBTransaction * transaction,const std::string & begin,const std::string & end,bool upper_open)1329 static leveldb::Status DeleteRangeBasic(LevelDBTransaction* transaction,
1330 const std::string& begin,
1331 const std::string& end,
1332 bool upper_open) {
1333 scoped_ptr<LevelDBIterator> it = transaction->CreateIterator();
1334 leveldb::Status s;
1335 for (s = it->Seek(begin); s.ok() && it->IsValid() &&
1336 (upper_open ? CompareKeys(it->Key(), end) < 0
1337 : CompareKeys(it->Key(), end) <= 0);
1338 s = it->Next())
1339 transaction->Remove(it->Key());
1340 return s;
1341 }
1342
DeleteBlobsInRange(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id,const std::string & start_key,const std::string & end_key,bool upper_open)1343 static leveldb::Status DeleteBlobsInRange(
1344 IndexedDBBackingStore::Transaction* transaction,
1345 int64 database_id,
1346 int64 object_store_id,
1347 const std::string& start_key,
1348 const std::string& end_key,
1349 bool upper_open) {
1350 scoped_ptr<LevelDBIterator> it = transaction->transaction()->CreateIterator();
1351 leveldb::Status s = it->Seek(start_key);
1352 for (; s.ok() && it->IsValid() &&
1353 (upper_open ? CompareKeys(it->Key(), end_key) < 0
1354 : CompareKeys(it->Key(), end_key) <= 0);
1355 s = it->Next()) {
1356 StringPiece key_piece(it->Key());
1357 std::string user_key =
1358 BlobEntryKey::ReencodeToObjectStoreDataKey(&key_piece);
1359 if (!user_key.size()) {
1360 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1361 return InternalInconsistencyStatus();
1362 }
1363 transaction->PutBlobInfo(
1364 database_id, object_store_id, user_key, NULL, NULL);
1365 }
1366 return s;
1367 }
1368
DeleteBlobsInObjectStore(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id)1369 static leveldb::Status DeleteBlobsInObjectStore(
1370 IndexedDBBackingStore::Transaction* transaction,
1371 int64 database_id,
1372 int64 object_store_id) {
1373 std::string start_key, stop_key;
1374 start_key =
1375 BlobEntryKey::EncodeMinKeyForObjectStore(database_id, object_store_id);
1376 stop_key =
1377 BlobEntryKey::EncodeStopKeyForObjectStore(database_id, object_store_id);
1378 return DeleteBlobsInRange(
1379 transaction, database_id, object_store_id, start_key, stop_key, true);
1380 }
1381
DeleteDatabase(const base::string16 & name)1382 leveldb::Status IndexedDBBackingStore::DeleteDatabase(
1383 const base::string16& name) {
1384 IDB_TRACE("IndexedDBBackingStore::DeleteDatabase");
1385 scoped_ptr<LevelDBDirectTransaction> transaction =
1386 LevelDBDirectTransaction::Create(db_.get());
1387
1388 leveldb::Status s;
1389 s = CleanUpBlobJournal(BlobJournalKey::Encode());
1390 if (!s.ok())
1391 return s;
1392
1393 IndexedDBDatabaseMetadata metadata;
1394 bool success = false;
1395 s = GetIDBDatabaseMetaData(name, &metadata, &success);
1396 if (!s.ok())
1397 return s;
1398 if (!success)
1399 return leveldb::Status::OK();
1400
1401 const std::string start_key = DatabaseMetaDataKey::Encode(
1402 metadata.id, DatabaseMetaDataKey::ORIGIN_NAME);
1403 const std::string stop_key = DatabaseMetaDataKey::Encode(
1404 metadata.id + 1, DatabaseMetaDataKey::ORIGIN_NAME);
1405 scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
1406 for (s = it->Seek(start_key);
1407 s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0;
1408 s = it->Next())
1409 transaction->Remove(it->Key());
1410 if (!s.ok()) {
1411 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_DATABASE);
1412 return s;
1413 }
1414
1415 const std::string key = DatabaseNameKey::Encode(origin_identifier_, name);
1416 transaction->Remove(key);
1417
1418 bool need_cleanup = false;
1419 if (active_blob_registry()->MarkDeletedCheckIfUsed(
1420 metadata.id, DatabaseMetaDataKey::kAllBlobsKey)) {
1421 s = MergeDatabaseIntoLiveBlobJournal(transaction.get(), metadata.id);
1422 if (!s.ok())
1423 return s;
1424 } else {
1425 UpdateBlobJournalWithDatabase(transaction.get(), metadata.id);
1426 need_cleanup = true;
1427 }
1428
1429 s = transaction->Commit();
1430 if (!s.ok()) {
1431 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_DATABASE);
1432 return s;
1433 }
1434
1435 if (need_cleanup)
1436 CleanUpBlobJournal(BlobJournalKey::Encode());
1437
1438 db_->Compact(start_key, stop_key);
1439 return s;
1440 }
1441
CheckObjectStoreAndMetaDataType(const LevelDBIterator * it,const std::string & stop_key,int64 object_store_id,int64 meta_data_type)1442 static bool CheckObjectStoreAndMetaDataType(const LevelDBIterator* it,
1443 const std::string& stop_key,
1444 int64 object_store_id,
1445 int64 meta_data_type) {
1446 if (!it->IsValid() || CompareKeys(it->Key(), stop_key) >= 0)
1447 return false;
1448
1449 StringPiece slice(it->Key());
1450 ObjectStoreMetaDataKey meta_data_key;
1451 bool ok =
1452 ObjectStoreMetaDataKey::Decode(&slice, &meta_data_key) && slice.empty();
1453 DCHECK(ok);
1454 if (meta_data_key.ObjectStoreId() != object_store_id)
1455 return false;
1456 if (meta_data_key.MetaDataType() != meta_data_type)
1457 return false;
1458 return ok;
1459 }
1460
1461 // TODO(jsbell): This should do some error handling rather than
1462 // plowing ahead when bad data is encountered.
GetObjectStores(int64 database_id,IndexedDBDatabaseMetadata::ObjectStoreMap * object_stores)1463 leveldb::Status IndexedDBBackingStore::GetObjectStores(
1464 int64 database_id,
1465 IndexedDBDatabaseMetadata::ObjectStoreMap* object_stores) {
1466 IDB_TRACE("IndexedDBBackingStore::GetObjectStores");
1467 if (!KeyPrefix::IsValidDatabaseId(database_id))
1468 return InvalidDBKeyStatus();
1469 const std::string start_key =
1470 ObjectStoreMetaDataKey::Encode(database_id, 1, 0);
1471 const std::string stop_key =
1472 ObjectStoreMetaDataKey::EncodeMaxKey(database_id);
1473
1474 DCHECK(object_stores->empty());
1475
1476 scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
1477 leveldb::Status s = it->Seek(start_key);
1478 while (s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0) {
1479 StringPiece slice(it->Key());
1480 ObjectStoreMetaDataKey meta_data_key;
1481 bool ok =
1482 ObjectStoreMetaDataKey::Decode(&slice, &meta_data_key) && slice.empty();
1483 DCHECK(ok);
1484 if (!ok || meta_data_key.MetaDataType() != ObjectStoreMetaDataKey::NAME) {
1485 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1486 // Possible stale metadata, but don't fail the load.
1487 s = it->Next();
1488 if (!s.ok())
1489 break;
1490 continue;
1491 }
1492
1493 int64 object_store_id = meta_data_key.ObjectStoreId();
1494
1495 // TODO(jsbell): Do this by direct key lookup rather than iteration, to
1496 // simplify.
1497 base::string16 object_store_name;
1498 {
1499 StringPiece slice(it->Value());
1500 if (!DecodeString(&slice, &object_store_name) || !slice.empty())
1501 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1502 }
1503
1504 s = it->Next();
1505 if (!s.ok())
1506 break;
1507 if (!CheckObjectStoreAndMetaDataType(it.get(),
1508 stop_key,
1509 object_store_id,
1510 ObjectStoreMetaDataKey::KEY_PATH)) {
1511 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1512 break;
1513 }
1514 IndexedDBKeyPath key_path;
1515 {
1516 StringPiece slice(it->Value());
1517 if (!DecodeIDBKeyPath(&slice, &key_path) || !slice.empty())
1518 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1519 }
1520
1521 s = it->Next();
1522 if (!s.ok())
1523 break;
1524 if (!CheckObjectStoreAndMetaDataType(
1525 it.get(),
1526 stop_key,
1527 object_store_id,
1528 ObjectStoreMetaDataKey::AUTO_INCREMENT)) {
1529 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1530 break;
1531 }
1532 bool auto_increment;
1533 {
1534 StringPiece slice(it->Value());
1535 if (!DecodeBool(&slice, &auto_increment) || !slice.empty())
1536 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1537 }
1538
1539 s = it->Next(); // Is evictable.
1540 if (!s.ok())
1541 break;
1542 if (!CheckObjectStoreAndMetaDataType(it.get(),
1543 stop_key,
1544 object_store_id,
1545 ObjectStoreMetaDataKey::EVICTABLE)) {
1546 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1547 break;
1548 }
1549
1550 s = it->Next(); // Last version.
1551 if (!s.ok())
1552 break;
1553 if (!CheckObjectStoreAndMetaDataType(
1554 it.get(),
1555 stop_key,
1556 object_store_id,
1557 ObjectStoreMetaDataKey::LAST_VERSION)) {
1558 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1559 break;
1560 }
1561
1562 s = it->Next(); // Maximum index id allocated.
1563 if (!s.ok())
1564 break;
1565 if (!CheckObjectStoreAndMetaDataType(
1566 it.get(),
1567 stop_key,
1568 object_store_id,
1569 ObjectStoreMetaDataKey::MAX_INDEX_ID)) {
1570 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1571 break;
1572 }
1573 int64 max_index_id;
1574 {
1575 StringPiece slice(it->Value());
1576 if (!DecodeInt(&slice, &max_index_id) || !slice.empty())
1577 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1578 }
1579
1580 s = it->Next(); // [optional] has key path (is not null)
1581 if (!s.ok())
1582 break;
1583 if (CheckObjectStoreAndMetaDataType(it.get(),
1584 stop_key,
1585 object_store_id,
1586 ObjectStoreMetaDataKey::HAS_KEY_PATH)) {
1587 bool has_key_path;
1588 {
1589 StringPiece slice(it->Value());
1590 if (!DecodeBool(&slice, &has_key_path))
1591 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1592 }
1593 // This check accounts for two layers of legacy coding:
1594 // (1) Initially, has_key_path was added to distinguish null vs. string.
1595 // (2) Later, null vs. string vs. array was stored in the key_path itself.
1596 // So this check is only relevant for string-type key_paths.
1597 if (!has_key_path &&
1598 (key_path.type() == blink::WebIDBKeyPathTypeString &&
1599 !key_path.string().empty())) {
1600 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1601 break;
1602 }
1603 if (!has_key_path)
1604 key_path = IndexedDBKeyPath();
1605 s = it->Next();
1606 if (!s.ok())
1607 break;
1608 }
1609
1610 int64 key_generator_current_number = -1;
1611 if (CheckObjectStoreAndMetaDataType(
1612 it.get(),
1613 stop_key,
1614 object_store_id,
1615 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER)) {
1616 StringPiece slice(it->Value());
1617 if (!DecodeInt(&slice, &key_generator_current_number) || !slice.empty())
1618 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1619
1620 // TODO(jsbell): Return key_generator_current_number, cache in
1621 // object store, and write lazily to backing store. For now,
1622 // just assert that if it was written it was valid.
1623 DCHECK_GE(key_generator_current_number, kKeyGeneratorInitialNumber);
1624 s = it->Next();
1625 if (!s.ok())
1626 break;
1627 }
1628
1629 IndexedDBObjectStoreMetadata metadata(object_store_name,
1630 object_store_id,
1631 key_path,
1632 auto_increment,
1633 max_index_id);
1634 s = GetIndexes(database_id, object_store_id, &metadata.indexes);
1635 if (!s.ok())
1636 break;
1637 (*object_stores)[object_store_id] = metadata;
1638 }
1639
1640 if (!s.ok())
1641 INTERNAL_READ_ERROR_UNTESTED(GET_OBJECT_STORES);
1642
1643 return s;
1644 }
1645
SetMaxObjectStoreId(LevelDBTransaction * transaction,int64 database_id,int64 object_store_id)1646 WARN_UNUSED_RESULT static leveldb::Status SetMaxObjectStoreId(
1647 LevelDBTransaction* transaction,
1648 int64 database_id,
1649 int64 object_store_id) {
1650 const std::string max_object_store_id_key = DatabaseMetaDataKey::Encode(
1651 database_id, DatabaseMetaDataKey::MAX_OBJECT_STORE_ID);
1652 int64 max_object_store_id = -1;
1653 leveldb::Status s = GetMaxObjectStoreId(
1654 transaction, max_object_store_id_key, &max_object_store_id);
1655 if (!s.ok()) {
1656 INTERNAL_READ_ERROR_UNTESTED(SET_MAX_OBJECT_STORE_ID);
1657 return s;
1658 }
1659
1660 if (object_store_id <= max_object_store_id) {
1661 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_MAX_OBJECT_STORE_ID);
1662 return InternalInconsistencyStatus();
1663 }
1664 PutInt(transaction, max_object_store_id_key, object_store_id);
1665 return s;
1666 }
1667
Compact()1668 void IndexedDBBackingStore::Compact() { db_->CompactAll(); }
1669
CreateObjectStore(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id,const base::string16 & name,const IndexedDBKeyPath & key_path,bool auto_increment)1670 leveldb::Status IndexedDBBackingStore::CreateObjectStore(
1671 IndexedDBBackingStore::Transaction* transaction,
1672 int64 database_id,
1673 int64 object_store_id,
1674 const base::string16& name,
1675 const IndexedDBKeyPath& key_path,
1676 bool auto_increment) {
1677 IDB_TRACE("IndexedDBBackingStore::CreateObjectStore");
1678 if (!KeyPrefix::ValidIds(database_id, object_store_id))
1679 return InvalidDBKeyStatus();
1680 LevelDBTransaction* leveldb_transaction = transaction->transaction();
1681 leveldb::Status s =
1682 SetMaxObjectStoreId(leveldb_transaction, database_id, object_store_id);
1683 if (!s.ok())
1684 return s;
1685
1686 const std::string name_key = ObjectStoreMetaDataKey::Encode(
1687 database_id, object_store_id, ObjectStoreMetaDataKey::NAME);
1688 const std::string key_path_key = ObjectStoreMetaDataKey::Encode(
1689 database_id, object_store_id, ObjectStoreMetaDataKey::KEY_PATH);
1690 const std::string auto_increment_key = ObjectStoreMetaDataKey::Encode(
1691 database_id, object_store_id, ObjectStoreMetaDataKey::AUTO_INCREMENT);
1692 const std::string evictable_key = ObjectStoreMetaDataKey::Encode(
1693 database_id, object_store_id, ObjectStoreMetaDataKey::EVICTABLE);
1694 const std::string last_version_key = ObjectStoreMetaDataKey::Encode(
1695 database_id, object_store_id, ObjectStoreMetaDataKey::LAST_VERSION);
1696 const std::string max_index_id_key = ObjectStoreMetaDataKey::Encode(
1697 database_id, object_store_id, ObjectStoreMetaDataKey::MAX_INDEX_ID);
1698 const std::string has_key_path_key = ObjectStoreMetaDataKey::Encode(
1699 database_id, object_store_id, ObjectStoreMetaDataKey::HAS_KEY_PATH);
1700 const std::string key_generator_current_number_key =
1701 ObjectStoreMetaDataKey::Encode(
1702 database_id,
1703 object_store_id,
1704 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER);
1705 const std::string names_key = ObjectStoreNamesKey::Encode(database_id, name);
1706
1707 PutString(leveldb_transaction, name_key, name);
1708 PutIDBKeyPath(leveldb_transaction, key_path_key, key_path);
1709 PutInt(leveldb_transaction, auto_increment_key, auto_increment);
1710 PutInt(leveldb_transaction, evictable_key, false);
1711 PutInt(leveldb_transaction, last_version_key, 1);
1712 PutInt(leveldb_transaction, max_index_id_key, kMinimumIndexId);
1713 PutBool(leveldb_transaction, has_key_path_key, !key_path.IsNull());
1714 PutInt(leveldb_transaction,
1715 key_generator_current_number_key,
1716 kKeyGeneratorInitialNumber);
1717 PutInt(leveldb_transaction, names_key, object_store_id);
1718 return s;
1719 }
1720
DeleteObjectStore(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id)1721 leveldb::Status IndexedDBBackingStore::DeleteObjectStore(
1722 IndexedDBBackingStore::Transaction* transaction,
1723 int64 database_id,
1724 int64 object_store_id) {
1725 IDB_TRACE("IndexedDBBackingStore::DeleteObjectStore");
1726 if (!KeyPrefix::ValidIds(database_id, object_store_id))
1727 return InvalidDBKeyStatus();
1728 LevelDBTransaction* leveldb_transaction = transaction->transaction();
1729
1730 base::string16 object_store_name;
1731 bool found = false;
1732 leveldb::Status s =
1733 GetString(leveldb_transaction,
1734 ObjectStoreMetaDataKey::Encode(
1735 database_id, object_store_id, ObjectStoreMetaDataKey::NAME),
1736 &object_store_name,
1737 &found);
1738 if (!s.ok()) {
1739 INTERNAL_READ_ERROR_UNTESTED(DELETE_OBJECT_STORE);
1740 return s;
1741 }
1742 if (!found) {
1743 INTERNAL_CONSISTENCY_ERROR_UNTESTED(DELETE_OBJECT_STORE);
1744 return InternalInconsistencyStatus();
1745 }
1746
1747 s = DeleteBlobsInObjectStore(transaction, database_id, object_store_id);
1748 if (!s.ok()) {
1749 INTERNAL_CONSISTENCY_ERROR_UNTESTED(DELETE_OBJECT_STORE);
1750 return s;
1751 }
1752
1753 s = DeleteRangeBasic(
1754 leveldb_transaction,
1755 ObjectStoreMetaDataKey::Encode(database_id, object_store_id, 0),
1756 ObjectStoreMetaDataKey::EncodeMaxKey(database_id, object_store_id),
1757 true);
1758
1759 if (s.ok()) {
1760 leveldb_transaction->Remove(
1761 ObjectStoreNamesKey::Encode(database_id, object_store_name));
1762
1763 s = DeleteRangeBasic(
1764 leveldb_transaction,
1765 IndexFreeListKey::Encode(database_id, object_store_id, 0),
1766 IndexFreeListKey::EncodeMaxKey(database_id, object_store_id),
1767 true);
1768 }
1769
1770 if (s.ok()) {
1771 s = DeleteRangeBasic(
1772 leveldb_transaction,
1773 IndexMetaDataKey::Encode(database_id, object_store_id, 0, 0),
1774 IndexMetaDataKey::EncodeMaxKey(database_id, object_store_id),
1775 true);
1776 }
1777
1778 if (!s.ok()) {
1779 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_OBJECT_STORE);
1780 return s;
1781 }
1782
1783 return ClearObjectStore(transaction, database_id, object_store_id);
1784 }
1785
GetRecord(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id,const IndexedDBKey & key,IndexedDBValue * record)1786 leveldb::Status IndexedDBBackingStore::GetRecord(
1787 IndexedDBBackingStore::Transaction* transaction,
1788 int64 database_id,
1789 int64 object_store_id,
1790 const IndexedDBKey& key,
1791 IndexedDBValue* record) {
1792 IDB_TRACE("IndexedDBBackingStore::GetRecord");
1793 if (!KeyPrefix::ValidIds(database_id, object_store_id))
1794 return InvalidDBKeyStatus();
1795 LevelDBTransaction* leveldb_transaction = transaction->transaction();
1796
1797 const std::string leveldb_key =
1798 ObjectStoreDataKey::Encode(database_id, object_store_id, key);
1799 std::string data;
1800
1801 record->clear();
1802
1803 bool found = false;
1804 leveldb::Status s = leveldb_transaction->Get(leveldb_key, &data, &found);
1805 if (!s.ok()) {
1806 INTERNAL_READ_ERROR(GET_RECORD);
1807 return s;
1808 }
1809 if (!found)
1810 return s;
1811 if (data.empty()) {
1812 INTERNAL_READ_ERROR_UNTESTED(GET_RECORD);
1813 return leveldb::Status::NotFound("Record contained no data");
1814 }
1815
1816 int64 version;
1817 StringPiece slice(data);
1818 if (!DecodeVarInt(&slice, &version)) {
1819 INTERNAL_READ_ERROR_UNTESTED(GET_RECORD);
1820 return InternalInconsistencyStatus();
1821 }
1822
1823 record->bits = slice.as_string();
1824 return transaction->GetBlobInfoForRecord(database_id, leveldb_key, record);
1825 }
1826
GetNewVersionNumber(LevelDBTransaction * transaction,int64 database_id,int64 object_store_id,int64 * new_version_number)1827 WARN_UNUSED_RESULT static leveldb::Status GetNewVersionNumber(
1828 LevelDBTransaction* transaction,
1829 int64 database_id,
1830 int64 object_store_id,
1831 int64* new_version_number) {
1832 const std::string last_version_key = ObjectStoreMetaDataKey::Encode(
1833 database_id, object_store_id, ObjectStoreMetaDataKey::LAST_VERSION);
1834
1835 *new_version_number = -1;
1836 int64 last_version = -1;
1837 bool found = false;
1838 leveldb::Status s =
1839 GetInt(transaction, last_version_key, &last_version, &found);
1840 if (!s.ok()) {
1841 INTERNAL_READ_ERROR_UNTESTED(GET_NEW_VERSION_NUMBER);
1842 return s;
1843 }
1844 if (!found)
1845 last_version = 0;
1846
1847 DCHECK_GE(last_version, 0);
1848
1849 int64 version = last_version + 1;
1850 PutInt(transaction, last_version_key, version);
1851
1852 // TODO(jsbell): Think about how we want to handle the overflow scenario.
1853 DCHECK(version > last_version);
1854
1855 *new_version_number = version;
1856 return s;
1857 }
1858
PutRecord(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id,const IndexedDBKey & key,IndexedDBValue & value,ScopedVector<webkit_blob::BlobDataHandle> * handles,RecordIdentifier * record_identifier)1859 leveldb::Status IndexedDBBackingStore::PutRecord(
1860 IndexedDBBackingStore::Transaction* transaction,
1861 int64 database_id,
1862 int64 object_store_id,
1863 const IndexedDBKey& key,
1864 IndexedDBValue& value,
1865 ScopedVector<webkit_blob::BlobDataHandle>* handles,
1866 RecordIdentifier* record_identifier) {
1867 IDB_TRACE("IndexedDBBackingStore::PutRecord");
1868 if (!KeyPrefix::ValidIds(database_id, object_store_id))
1869 return InvalidDBKeyStatus();
1870 DCHECK(key.IsValid());
1871
1872 LevelDBTransaction* leveldb_transaction = transaction->transaction();
1873 int64 version = -1;
1874 leveldb::Status s = GetNewVersionNumber(
1875 leveldb_transaction, database_id, object_store_id, &version);
1876 if (!s.ok())
1877 return s;
1878 DCHECK_GE(version, 0);
1879 const std::string object_store_data_key =
1880 ObjectStoreDataKey::Encode(database_id, object_store_id, key);
1881
1882 std::string v;
1883 EncodeVarInt(version, &v);
1884 v.append(value.bits);
1885
1886 leveldb_transaction->Put(object_store_data_key, &v);
1887 s = transaction->PutBlobInfoIfNeeded(database_id,
1888 object_store_id,
1889 object_store_data_key,
1890 &value.blob_info,
1891 handles);
1892 if (!s.ok())
1893 return s;
1894 DCHECK(!handles->size());
1895
1896 const std::string exists_entry_key =
1897 ExistsEntryKey::Encode(database_id, object_store_id, key);
1898 std::string version_encoded;
1899 EncodeInt(version, &version_encoded);
1900 leveldb_transaction->Put(exists_entry_key, &version_encoded);
1901
1902 std::string key_encoded;
1903 EncodeIDBKey(key, &key_encoded);
1904 record_identifier->Reset(key_encoded, version);
1905 return s;
1906 }
1907
ClearObjectStore(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id)1908 leveldb::Status IndexedDBBackingStore::ClearObjectStore(
1909 IndexedDBBackingStore::Transaction* transaction,
1910 int64 database_id,
1911 int64 object_store_id) {
1912 IDB_TRACE("IndexedDBBackingStore::ClearObjectStore");
1913 if (!KeyPrefix::ValidIds(database_id, object_store_id))
1914 return InvalidDBKeyStatus();
1915 const std::string start_key =
1916 KeyPrefix(database_id, object_store_id).Encode();
1917 const std::string stop_key =
1918 KeyPrefix(database_id, object_store_id + 1).Encode();
1919
1920 leveldb::Status s =
1921 DeleteRangeBasic(transaction->transaction(), start_key, stop_key, true);
1922 if (!s.ok()) {
1923 INTERNAL_WRITE_ERROR(CLEAR_OBJECT_STORE);
1924 return s;
1925 }
1926 return DeleteBlobsInObjectStore(transaction, database_id, object_store_id);
1927 }
1928
DeleteRecord(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id,const RecordIdentifier & record_identifier)1929 leveldb::Status IndexedDBBackingStore::DeleteRecord(
1930 IndexedDBBackingStore::Transaction* transaction,
1931 int64 database_id,
1932 int64 object_store_id,
1933 const RecordIdentifier& record_identifier) {
1934 IDB_TRACE("IndexedDBBackingStore::DeleteRecord");
1935 if (!KeyPrefix::ValidIds(database_id, object_store_id))
1936 return InvalidDBKeyStatus();
1937 LevelDBTransaction* leveldb_transaction = transaction->transaction();
1938
1939 const std::string object_store_data_key = ObjectStoreDataKey::Encode(
1940 database_id, object_store_id, record_identifier.primary_key());
1941 leveldb_transaction->Remove(object_store_data_key);
1942 leveldb::Status s = transaction->PutBlobInfoIfNeeded(
1943 database_id, object_store_id, object_store_data_key, NULL, NULL);
1944 if (!s.ok())
1945 return s;
1946
1947 const std::string exists_entry_key = ExistsEntryKey::Encode(
1948 database_id, object_store_id, record_identifier.primary_key());
1949 leveldb_transaction->Remove(exists_entry_key);
1950 return leveldb::Status::OK();
1951 }
1952
DeleteRange(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id,const IndexedDBKeyRange & key_range)1953 leveldb::Status IndexedDBBackingStore::DeleteRange(
1954 IndexedDBBackingStore::Transaction* transaction,
1955 int64 database_id,
1956 int64 object_store_id,
1957 const IndexedDBKeyRange& key_range) {
1958 leveldb::Status s;
1959 scoped_ptr<IndexedDBBackingStore::Cursor> start_cursor =
1960 OpenObjectStoreCursor(transaction,
1961 database_id,
1962 object_store_id,
1963 key_range,
1964 indexed_db::CURSOR_NEXT,
1965 &s);
1966 if (!s.ok())
1967 return s;
1968 if (!start_cursor)
1969 return leveldb::Status::OK(); // Empty range == delete success.
1970
1971 scoped_ptr<IndexedDBBackingStore::Cursor> end_cursor =
1972 OpenObjectStoreCursor(transaction,
1973 database_id,
1974 object_store_id,
1975 key_range,
1976 indexed_db::CURSOR_PREV,
1977 &s);
1978
1979 if (!s.ok())
1980 return s;
1981 if (!end_cursor)
1982 return leveldb::Status::OK(); // Empty range == delete success.
1983
1984 BlobEntryKey start_blob_key, end_blob_key;
1985
1986 std::string start_key = ObjectStoreDataKey::Encode(
1987 database_id, object_store_id, start_cursor->key());
1988 base::StringPiece start_key_piece(start_key);
1989 if (!BlobEntryKey::FromObjectStoreDataKey(&start_key_piece, &start_blob_key))
1990 return InternalInconsistencyStatus();
1991 std::string stop_key = ObjectStoreDataKey::Encode(
1992 database_id, object_store_id, end_cursor->key());
1993 base::StringPiece stop_key_piece(stop_key);
1994 if (!BlobEntryKey::FromObjectStoreDataKey(&stop_key_piece, &end_blob_key))
1995 return InternalInconsistencyStatus();
1996
1997 s = DeleteBlobsInRange(transaction,
1998 database_id,
1999 object_store_id,
2000 start_blob_key.Encode(),
2001 end_blob_key.Encode(),
2002 false);
2003 if (!s.ok())
2004 return s;
2005 s = DeleteRangeBasic(transaction->transaction(), start_key, stop_key, false);
2006 if (!s.ok())
2007 return s;
2008 start_key =
2009 ExistsEntryKey::Encode(database_id, object_store_id, start_cursor->key());
2010 stop_key =
2011 ExistsEntryKey::Encode(database_id, object_store_id, end_cursor->key());
2012 return DeleteRangeBasic(
2013 transaction->transaction(), start_key, stop_key, false);
2014 }
2015
GetKeyGeneratorCurrentNumber(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id,int64 * key_generator_current_number)2016 leveldb::Status IndexedDBBackingStore::GetKeyGeneratorCurrentNumber(
2017 IndexedDBBackingStore::Transaction* transaction,
2018 int64 database_id,
2019 int64 object_store_id,
2020 int64* key_generator_current_number) {
2021 if (!KeyPrefix::ValidIds(database_id, object_store_id))
2022 return InvalidDBKeyStatus();
2023 LevelDBTransaction* leveldb_transaction = transaction->transaction();
2024
2025 const std::string key_generator_current_number_key =
2026 ObjectStoreMetaDataKey::Encode(
2027 database_id,
2028 object_store_id,
2029 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER);
2030
2031 *key_generator_current_number = -1;
2032 std::string data;
2033
2034 bool found = false;
2035 leveldb::Status s =
2036 leveldb_transaction->Get(key_generator_current_number_key, &data, &found);
2037 if (!s.ok()) {
2038 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER);
2039 return s;
2040 }
2041 if (found && !data.empty()) {
2042 StringPiece slice(data);
2043 if (!DecodeInt(&slice, key_generator_current_number) || !slice.empty()) {
2044 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER);
2045 return InternalInconsistencyStatus();
2046 }
2047 return s;
2048 }
2049
2050 // Previously, the key generator state was not stored explicitly
2051 // but derived from the maximum numeric key present in existing
2052 // data. This violates the spec as the data may be cleared but the
2053 // key generator state must be preserved.
2054 // TODO(jsbell): Fix this for all stores on database open?
2055 const std::string start_key =
2056 ObjectStoreDataKey::Encode(database_id, object_store_id, MinIDBKey());
2057 const std::string stop_key =
2058 ObjectStoreDataKey::Encode(database_id, object_store_id, MaxIDBKey());
2059
2060 scoped_ptr<LevelDBIterator> it = leveldb_transaction->CreateIterator();
2061 int64 max_numeric_key = 0;
2062
2063 for (s = it->Seek(start_key);
2064 s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0;
2065 s = it->Next()) {
2066 StringPiece slice(it->Key());
2067 ObjectStoreDataKey data_key;
2068 if (!ObjectStoreDataKey::Decode(&slice, &data_key) || !slice.empty()) {
2069 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER);
2070 return InternalInconsistencyStatus();
2071 }
2072 scoped_ptr<IndexedDBKey> user_key = data_key.user_key();
2073 if (user_key->type() == blink::WebIDBKeyTypeNumber) {
2074 int64 n = static_cast<int64>(user_key->number());
2075 if (n > max_numeric_key)
2076 max_numeric_key = n;
2077 }
2078 }
2079
2080 if (s.ok())
2081 *key_generator_current_number = max_numeric_key + 1;
2082 else
2083 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER);
2084
2085 return s;
2086 }
2087
MaybeUpdateKeyGeneratorCurrentNumber(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id,int64 new_number,bool check_current)2088 leveldb::Status IndexedDBBackingStore::MaybeUpdateKeyGeneratorCurrentNumber(
2089 IndexedDBBackingStore::Transaction* transaction,
2090 int64 database_id,
2091 int64 object_store_id,
2092 int64 new_number,
2093 bool check_current) {
2094 if (!KeyPrefix::ValidIds(database_id, object_store_id))
2095 return InvalidDBKeyStatus();
2096
2097 if (check_current) {
2098 int64 current_number;
2099 leveldb::Status s = GetKeyGeneratorCurrentNumber(
2100 transaction, database_id, object_store_id, ¤t_number);
2101 if (!s.ok())
2102 return s;
2103 if (new_number <= current_number)
2104 return s;
2105 }
2106
2107 const std::string key_generator_current_number_key =
2108 ObjectStoreMetaDataKey::Encode(
2109 database_id,
2110 object_store_id,
2111 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER);
2112 PutInt(
2113 transaction->transaction(), key_generator_current_number_key, new_number);
2114 return leveldb::Status::OK();
2115 }
2116
KeyExistsInObjectStore(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id,const IndexedDBKey & key,RecordIdentifier * found_record_identifier,bool * found)2117 leveldb::Status IndexedDBBackingStore::KeyExistsInObjectStore(
2118 IndexedDBBackingStore::Transaction* transaction,
2119 int64 database_id,
2120 int64 object_store_id,
2121 const IndexedDBKey& key,
2122 RecordIdentifier* found_record_identifier,
2123 bool* found) {
2124 IDB_TRACE("IndexedDBBackingStore::KeyExistsInObjectStore");
2125 if (!KeyPrefix::ValidIds(database_id, object_store_id))
2126 return InvalidDBKeyStatus();
2127 *found = false;
2128 const std::string leveldb_key =
2129 ObjectStoreDataKey::Encode(database_id, object_store_id, key);
2130 std::string data;
2131
2132 leveldb::Status s =
2133 transaction->transaction()->Get(leveldb_key, &data, found);
2134 if (!s.ok()) {
2135 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_OBJECT_STORE);
2136 return s;
2137 }
2138 if (!*found)
2139 return leveldb::Status::OK();
2140 if (!data.size()) {
2141 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_OBJECT_STORE);
2142 return InternalInconsistencyStatus();
2143 }
2144
2145 int64 version;
2146 StringPiece slice(data);
2147 if (!DecodeVarInt(&slice, &version))
2148 return InternalInconsistencyStatus();
2149
2150 std::string encoded_key;
2151 EncodeIDBKey(key, &encoded_key);
2152 found_record_identifier->Reset(encoded_key, version);
2153 return s;
2154 }
2155
2156 class IndexedDBBackingStore::Transaction::ChainedBlobWriterImpl
2157 : public IndexedDBBackingStore::Transaction::ChainedBlobWriter {
2158 public:
2159 typedef IndexedDBBackingStore::Transaction::WriteDescriptorVec
2160 WriteDescriptorVec;
ChainedBlobWriterImpl(int64 database_id,IndexedDBBackingStore * backing_store,WriteDescriptorVec & blobs,scoped_refptr<IndexedDBBackingStore::BlobWriteCallback> callback)2161 ChainedBlobWriterImpl(
2162 int64 database_id,
2163 IndexedDBBackingStore* backing_store,
2164 WriteDescriptorVec& blobs,
2165 scoped_refptr<IndexedDBBackingStore::BlobWriteCallback> callback)
2166 : waiting_for_callback_(false),
2167 database_id_(database_id),
2168 backing_store_(backing_store),
2169 callback_(callback),
2170 aborted_(false) {
2171 blobs_.swap(blobs);
2172 iter_ = blobs_.begin();
2173 backing_store->task_runner()->PostTask(
2174 FROM_HERE, base::Bind(&ChainedBlobWriterImpl::WriteNextFile, this));
2175 }
2176
set_delegate(scoped_ptr<FileWriterDelegate> delegate)2177 virtual void set_delegate(scoped_ptr<FileWriterDelegate> delegate) OVERRIDE {
2178 delegate_.reset(delegate.release());
2179 }
2180
ReportWriteCompletion(bool succeeded,int64 bytes_written)2181 virtual void ReportWriteCompletion(bool succeeded,
2182 int64 bytes_written) OVERRIDE {
2183 DCHECK(waiting_for_callback_);
2184 DCHECK(!succeeded || bytes_written >= 0);
2185 waiting_for_callback_ = false;
2186 if (delegate_.get()) // Only present for Blob, not File.
2187 content::BrowserThread::DeleteSoon(
2188 content::BrowserThread::IO, FROM_HERE, delegate_.release());
2189 if (aborted_) {
2190 self_ref_ = NULL;
2191 return;
2192 }
2193 if (iter_->size() != -1 && iter_->size() != bytes_written)
2194 succeeded = false;
2195 if (succeeded) {
2196 ++iter_;
2197 WriteNextFile();
2198 } else {
2199 callback_->Run(false);
2200 }
2201 }
2202
Abort()2203 virtual void Abort() OVERRIDE {
2204 if (!waiting_for_callback_)
2205 return;
2206 self_ref_ = this;
2207 aborted_ = true;
2208 }
2209
2210 private:
~ChainedBlobWriterImpl()2211 virtual ~ChainedBlobWriterImpl() {}
2212
WriteNextFile()2213 void WriteNextFile() {
2214 DCHECK(!waiting_for_callback_);
2215 DCHECK(!aborted_);
2216 if (iter_ == blobs_.end()) {
2217 DCHECK(!self_ref_);
2218 callback_->Run(true);
2219 return;
2220 } else {
2221 if (!backing_store_->WriteBlobFile(database_id_, *iter_, this)) {
2222 callback_->Run(false);
2223 return;
2224 }
2225 waiting_for_callback_ = true;
2226 }
2227 }
2228
2229 bool waiting_for_callback_;
2230 scoped_refptr<ChainedBlobWriterImpl> self_ref_;
2231 WriteDescriptorVec blobs_;
2232 WriteDescriptorVec::const_iterator iter_;
2233 int64 database_id_;
2234 IndexedDBBackingStore* backing_store_;
2235 scoped_refptr<IndexedDBBackingStore::BlobWriteCallback> callback_;
2236 scoped_ptr<FileWriterDelegate> delegate_;
2237 bool aborted_;
2238
2239 DISALLOW_COPY_AND_ASSIGN(ChainedBlobWriterImpl);
2240 };
2241
2242 class LocalWriteClosure : public FileWriterDelegate::DelegateWriteCallback,
2243 public base::RefCounted<LocalWriteClosure> {
2244 public:
LocalWriteClosure(IndexedDBBackingStore::Transaction::ChainedBlobWriter * chained_blob_writer,base::TaskRunner * task_runner)2245 LocalWriteClosure(IndexedDBBackingStore::Transaction::ChainedBlobWriter*
2246 chained_blob_writer,
2247 base::TaskRunner* task_runner)
2248 : chained_blob_writer_(chained_blob_writer),
2249 task_runner_(task_runner),
2250 bytes_written_(0) {}
2251
Run(base::File::Error rv,int64 bytes,FileWriterDelegate::WriteProgressStatus write_status)2252 void Run(base::File::Error rv,
2253 int64 bytes,
2254 FileWriterDelegate::WriteProgressStatus write_status) {
2255 DCHECK_GE(bytes, 0);
2256 bytes_written_ += bytes;
2257 if (write_status == FileWriterDelegate::SUCCESS_IO_PENDING)
2258 return; // We don't care about progress events.
2259 if (rv == base::File::FILE_OK) {
2260 DCHECK_EQ(write_status, FileWriterDelegate::SUCCESS_COMPLETED);
2261 } else {
2262 DCHECK(write_status == FileWriterDelegate::ERROR_WRITE_STARTED ||
2263 write_status == FileWriterDelegate::ERROR_WRITE_NOT_STARTED);
2264 }
2265 task_runner_->PostTask(
2266 FROM_HERE,
2267 base::Bind(&LocalWriteClosure::callBlobCallbackOnIDBTaskRunner,
2268 this,
2269 write_status == FileWriterDelegate::SUCCESS_COMPLETED));
2270 }
2271
writeBlobToFileOnIOThread(const FilePath & file_path,const GURL & blob_url,net::URLRequestContext * request_context)2272 void writeBlobToFileOnIOThread(const FilePath& file_path,
2273 const GURL& blob_url,
2274 net::URLRequestContext* request_context) {
2275 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
2276 scoped_ptr<fileapi::FileStreamWriter> writer(
2277 fileapi::FileStreamWriter::CreateForLocalFile(
2278 task_runner_, file_path, 0,
2279 fileapi::FileStreamWriter::CREATE_NEW_FILE));
2280 scoped_ptr<FileWriterDelegate> delegate(
2281 new FileWriterDelegate(writer.Pass(),
2282 FileWriterDelegate::FLUSH_ON_COMPLETION));
2283
2284 DCHECK(blob_url.is_valid());
2285 scoped_ptr<net::URLRequest> blob_request(request_context->CreateRequest(
2286 blob_url, net::DEFAULT_PRIORITY, delegate.get(), NULL));
2287
2288 delegate->Start(blob_request.Pass(),
2289 base::Bind(&LocalWriteClosure::Run, this));
2290 chained_blob_writer_->set_delegate(delegate.Pass());
2291 }
2292
2293 private:
~LocalWriteClosure()2294 virtual ~LocalWriteClosure() {}
2295 friend class base::RefCounted<LocalWriteClosure>;
2296
callBlobCallbackOnIDBTaskRunner(bool succeeded)2297 void callBlobCallbackOnIDBTaskRunner(bool succeeded) {
2298 DCHECK(task_runner_->RunsTasksOnCurrentThread());
2299 chained_blob_writer_->ReportWriteCompletion(succeeded, bytes_written_);
2300 }
2301
2302 IndexedDBBackingStore::Transaction::ChainedBlobWriter* chained_blob_writer_;
2303 base::TaskRunner* task_runner_;
2304 int64 bytes_written_;
2305
2306 DISALLOW_COPY_AND_ASSIGN(LocalWriteClosure);
2307 };
2308
WriteBlobFile(int64 database_id,const Transaction::WriteDescriptor & descriptor,Transaction::ChainedBlobWriter * chained_blob_writer)2309 bool IndexedDBBackingStore::WriteBlobFile(
2310 int64 database_id,
2311 const Transaction::WriteDescriptor& descriptor,
2312 Transaction::ChainedBlobWriter* chained_blob_writer) {
2313
2314 if (!MakeIDBBlobDirectory(blob_path_, database_id, descriptor.key()))
2315 return false;
2316
2317 FilePath path = GetBlobFileName(database_id, descriptor.key());
2318
2319 if (descriptor.is_file()) {
2320 DCHECK(!descriptor.file_path().empty());
2321 if (!base::CopyFile(descriptor.file_path(), path))
2322 return false;
2323
2324 base::File::Info info;
2325 if (base::GetFileInfo(descriptor.file_path(), &info)) {
2326 if (descriptor.size() != -1) {
2327 if (descriptor.size() != info.size)
2328 return false;
2329 // The round-trip can be lossy; round to nearest millisecond.
2330 int64 delta = (descriptor.last_modified() -
2331 info.last_modified).InMilliseconds();
2332 if (std::abs(delta) > 1)
2333 return false;
2334 }
2335 if (!base::TouchFile(path, info.last_accessed, info.last_modified)) {
2336 // TODO(ericu): Complain quietly; timestamp's probably not vital.
2337 }
2338 } else {
2339 // TODO(ericu): Complain quietly; timestamp's probably not vital.
2340 }
2341
2342 task_runner_->PostTask(
2343 FROM_HERE,
2344 base::Bind(&Transaction::ChainedBlobWriter::ReportWriteCompletion,
2345 chained_blob_writer,
2346 true,
2347 info.size));
2348 } else {
2349 DCHECK(descriptor.url().is_valid());
2350 scoped_refptr<LocalWriteClosure> write_closure(
2351 new LocalWriteClosure(chained_blob_writer, task_runner_));
2352 content::BrowserThread::PostTask(
2353 content::BrowserThread::IO,
2354 FROM_HERE,
2355 base::Bind(&LocalWriteClosure::writeBlobToFileOnIOThread,
2356 write_closure.get(),
2357 path,
2358 descriptor.url(),
2359 request_context_));
2360 }
2361 return true;
2362 }
2363
ReportBlobUnused(int64 database_id,int64 blob_key)2364 void IndexedDBBackingStore::ReportBlobUnused(int64 database_id,
2365 int64 blob_key) {
2366 DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
2367 bool all_blobs = blob_key == DatabaseMetaDataKey::kAllBlobsKey;
2368 DCHECK(all_blobs || DatabaseMetaDataKey::IsValidBlobKey(blob_key));
2369 scoped_refptr<LevelDBTransaction> transaction =
2370 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_.get());
2371
2372 std::string live_blob_key = LiveBlobJournalKey::Encode();
2373 BlobJournalType live_blob_journal;
2374 if (!GetBlobJournal(live_blob_key, transaction.get(), &live_blob_journal)
2375 .ok())
2376 return;
2377 DCHECK(live_blob_journal.size());
2378
2379 std::string primary_key = BlobJournalKey::Encode();
2380 BlobJournalType primary_journal;
2381 if (!GetBlobJournal(primary_key, transaction.get(), &primary_journal).ok())
2382 return;
2383
2384 // There are several cases to handle. If blob_key is kAllBlobsKey, we want to
2385 // remove all entries with database_id from the live_blob journal and add only
2386 // kAllBlobsKey to the primary journal. Otherwise if IsValidBlobKey(blob_key)
2387 // and we hit kAllBlobsKey for the right database_id in the journal, we leave
2388 // the kAllBlobsKey entry in the live_blob journal but add the specific blob
2389 // to the primary. Otherwise if IsValidBlobKey(blob_key) and we find a
2390 // matching (database_id, blob_key) tuple, we should move it to the primary
2391 // journal.
2392 BlobJournalType new_live_blob_journal;
2393 for (BlobJournalType::iterator journal_iter = live_blob_journal.begin();
2394 journal_iter != live_blob_journal.end();
2395 ++journal_iter) {
2396 int64 current_database_id = journal_iter->first;
2397 int64 current_blob_key = journal_iter->second;
2398 bool current_all_blobs =
2399 current_blob_key == DatabaseMetaDataKey::kAllBlobsKey;
2400 DCHECK(KeyPrefix::IsValidDatabaseId(current_database_id) ||
2401 current_all_blobs);
2402 if (current_database_id == database_id &&
2403 (all_blobs || current_all_blobs || blob_key == current_blob_key)) {
2404 if (!all_blobs) {
2405 primary_journal.push_back(
2406 std::make_pair(database_id, current_blob_key));
2407 if (current_all_blobs)
2408 new_live_blob_journal.push_back(*journal_iter);
2409 new_live_blob_journal.insert(new_live_blob_journal.end(),
2410 ++journal_iter,
2411 live_blob_journal.end()); // All the rest.
2412 break;
2413 }
2414 } else {
2415 new_live_blob_journal.push_back(*journal_iter);
2416 }
2417 }
2418 if (all_blobs) {
2419 primary_journal.push_back(
2420 std::make_pair(database_id, DatabaseMetaDataKey::kAllBlobsKey));
2421 }
2422 UpdatePrimaryJournalWithBlobList(transaction.get(), primary_journal);
2423 UpdateLiveBlobJournalWithBlobList(transaction.get(), new_live_blob_journal);
2424 transaction->Commit();
2425 // We could just do the deletions/cleaning here, but if there are a lot of
2426 // blobs about to be garbage collected, it'd be better to wait and do them all
2427 // at once.
2428 StartJournalCleaningTimer();
2429 }
2430
2431 // The this reference is a raw pointer that's declared Unretained inside the
2432 // timer code, so this won't confuse IndexedDBFactory's check for
2433 // HasLastBackingStoreReference. It's safe because if the backing store is
2434 // deleted, the timer will automatically be canceled on destruction.
StartJournalCleaningTimer()2435 void IndexedDBBackingStore::StartJournalCleaningTimer() {
2436 journal_cleaning_timer_.Start(
2437 FROM_HERE,
2438 base::TimeDelta::FromSeconds(5),
2439 this,
2440 &IndexedDBBackingStore::CleanPrimaryJournalIgnoreReturn);
2441 }
2442
2443 // This assumes a file path of dbId/second-to-LSB-of-counter/counter.
GetBlobFileName(int64 database_id,int64 key)2444 FilePath IndexedDBBackingStore::GetBlobFileName(int64 database_id, int64 key) {
2445 return GetBlobFileNameForKey(blob_path_, database_id, key);
2446 }
2447
CheckIndexAndMetaDataKey(const LevelDBIterator * it,const std::string & stop_key,int64 index_id,unsigned char meta_data_type)2448 static bool CheckIndexAndMetaDataKey(const LevelDBIterator* it,
2449 const std::string& stop_key,
2450 int64 index_id,
2451 unsigned char meta_data_type) {
2452 if (!it->IsValid() || CompareKeys(it->Key(), stop_key) >= 0)
2453 return false;
2454
2455 StringPiece slice(it->Key());
2456 IndexMetaDataKey meta_data_key;
2457 bool ok = IndexMetaDataKey::Decode(&slice, &meta_data_key);
2458 DCHECK(ok);
2459 if (meta_data_key.IndexId() != index_id)
2460 return false;
2461 if (meta_data_key.meta_data_type() != meta_data_type)
2462 return false;
2463 return true;
2464 }
2465
2466 // TODO(jsbell): This should do some error handling rather than plowing ahead
2467 // when bad data is encountered.
GetIndexes(int64 database_id,int64 object_store_id,IndexedDBObjectStoreMetadata::IndexMap * indexes)2468 leveldb::Status IndexedDBBackingStore::GetIndexes(
2469 int64 database_id,
2470 int64 object_store_id,
2471 IndexedDBObjectStoreMetadata::IndexMap* indexes) {
2472 IDB_TRACE("IndexedDBBackingStore::GetIndexes");
2473 if (!KeyPrefix::ValidIds(database_id, object_store_id))
2474 return InvalidDBKeyStatus();
2475 const std::string start_key =
2476 IndexMetaDataKey::Encode(database_id, object_store_id, 0, 0);
2477 const std::string stop_key =
2478 IndexMetaDataKey::Encode(database_id, object_store_id + 1, 0, 0);
2479
2480 DCHECK(indexes->empty());
2481
2482 scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
2483 leveldb::Status s = it->Seek(start_key);
2484 while (s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0) {
2485 StringPiece slice(it->Key());
2486 IndexMetaDataKey meta_data_key;
2487 bool ok = IndexMetaDataKey::Decode(&slice, &meta_data_key);
2488 DCHECK(ok);
2489 if (meta_data_key.meta_data_type() != IndexMetaDataKey::NAME) {
2490 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2491 // Possible stale metadata due to http://webkit.org/b/85557 but don't fail
2492 // the load.
2493 s = it->Next();
2494 if (!s.ok())
2495 break;
2496 continue;
2497 }
2498
2499 // TODO(jsbell): Do this by direct key lookup rather than iteration, to
2500 // simplify.
2501 int64 index_id = meta_data_key.IndexId();
2502 base::string16 index_name;
2503 {
2504 StringPiece slice(it->Value());
2505 if (!DecodeString(&slice, &index_name) || !slice.empty())
2506 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2507 }
2508
2509 s = it->Next(); // unique flag
2510 if (!s.ok())
2511 break;
2512 if (!CheckIndexAndMetaDataKey(
2513 it.get(), stop_key, index_id, IndexMetaDataKey::UNIQUE)) {
2514 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2515 break;
2516 }
2517 bool index_unique;
2518 {
2519 StringPiece slice(it->Value());
2520 if (!DecodeBool(&slice, &index_unique) || !slice.empty())
2521 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2522 }
2523
2524 s = it->Next(); // key_path
2525 if (!s.ok())
2526 break;
2527 if (!CheckIndexAndMetaDataKey(
2528 it.get(), stop_key, index_id, IndexMetaDataKey::KEY_PATH)) {
2529 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2530 break;
2531 }
2532 IndexedDBKeyPath key_path;
2533 {
2534 StringPiece slice(it->Value());
2535 if (!DecodeIDBKeyPath(&slice, &key_path) || !slice.empty())
2536 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2537 }
2538
2539 s = it->Next(); // [optional] multi_entry flag
2540 if (!s.ok())
2541 break;
2542 bool index_multi_entry = false;
2543 if (CheckIndexAndMetaDataKey(
2544 it.get(), stop_key, index_id, IndexMetaDataKey::MULTI_ENTRY)) {
2545 StringPiece slice(it->Value());
2546 if (!DecodeBool(&slice, &index_multi_entry) || !slice.empty())
2547 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2548
2549 s = it->Next();
2550 if (!s.ok())
2551 break;
2552 }
2553
2554 (*indexes)[index_id] = IndexedDBIndexMetadata(
2555 index_name, index_id, key_path, index_unique, index_multi_entry);
2556 }
2557
2558 if (!s.ok())
2559 INTERNAL_READ_ERROR_UNTESTED(GET_INDEXES);
2560
2561 return s;
2562 }
2563
RemoveBlobFile(int64 database_id,int64 key)2564 bool IndexedDBBackingStore::RemoveBlobFile(int64 database_id, int64 key) {
2565 FilePath fileName = GetBlobFileName(database_id, key);
2566 return base::DeleteFile(fileName, false);
2567 }
2568
RemoveBlobDirectory(int64 database_id)2569 bool IndexedDBBackingStore::RemoveBlobDirectory(int64 database_id) {
2570 FilePath dirName = GetBlobDirectoryName(blob_path_, database_id);
2571 return base::DeleteFile(dirName, true);
2572 }
2573
CleanUpBlobJournal(const std::string & level_db_key)2574 leveldb::Status IndexedDBBackingStore::CleanUpBlobJournal(
2575 const std::string& level_db_key) {
2576 scoped_refptr<LevelDBTransaction> journal_transaction =
2577 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_.get());
2578 BlobJournalType journal;
2579 leveldb::Status s =
2580 GetBlobJournal(level_db_key, journal_transaction.get(), &journal);
2581 if (!s.ok())
2582 return s;
2583 if (!journal.size())
2584 return leveldb::Status::OK();
2585 BlobJournalType::iterator journal_iter;
2586 for (journal_iter = journal.begin(); journal_iter != journal.end();
2587 ++journal_iter) {
2588 int64 database_id = journal_iter->first;
2589 int64 blob_key = journal_iter->second;
2590 DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
2591 if (blob_key == DatabaseMetaDataKey::kAllBlobsKey) {
2592 if (!RemoveBlobDirectory(database_id))
2593 return IOErrorStatus();
2594 } else {
2595 DCHECK(DatabaseMetaDataKey::IsValidBlobKey(blob_key));
2596 if (!RemoveBlobFile(database_id, blob_key))
2597 return IOErrorStatus();
2598 }
2599 }
2600 ClearBlobJournal(journal_transaction.get(), level_db_key);
2601 return journal_transaction->Commit();
2602 }
2603
GetBlobInfoForRecord(int64 database_id,const std::string & object_store_data_key,IndexedDBValue * value)2604 leveldb::Status IndexedDBBackingStore::Transaction::GetBlobInfoForRecord(
2605 int64 database_id,
2606 const std::string& object_store_data_key,
2607 IndexedDBValue* value) {
2608 BlobChangeRecord* change_record = NULL;
2609 BlobChangeMap::const_iterator blob_iter =
2610 blob_change_map_.find(object_store_data_key);
2611 if (blob_iter != blob_change_map_.end()) {
2612 change_record = blob_iter->second;
2613 } else {
2614 blob_iter = incognito_blob_map_.find(object_store_data_key);
2615 if (blob_iter != incognito_blob_map_.end())
2616 change_record = blob_iter->second;
2617 }
2618 if (change_record) {
2619 // Either we haven't written the blob to disk yet or we're in incognito
2620 // mode, so we have to send back the one they sent us. This change record
2621 // includes the original UUID.
2622 value->blob_info = change_record->blob_info();
2623 return leveldb::Status::OK();
2624 }
2625
2626 BlobEntryKey blob_entry_key;
2627 StringPiece leveldb_key_piece(object_store_data_key);
2628 if (!BlobEntryKey::FromObjectStoreDataKey(&leveldb_key_piece,
2629 &blob_entry_key)) {
2630 NOTREACHED();
2631 return InternalInconsistencyStatus();
2632 }
2633 scoped_ptr<LevelDBIterator> it = transaction()->CreateIterator();
2634 std::string encoded_key = blob_entry_key.Encode();
2635 leveldb::Status s = it->Seek(encoded_key);
2636 if (!s.ok())
2637 return s;
2638 if (it->IsValid() && CompareKeys(it->Key(), encoded_key) == 0) {
2639 if (!DecodeBlobData(it->Value().as_string(), &value->blob_info)) {
2640 INTERNAL_READ_ERROR(GET_BLOB_INFO_FOR_RECORD);
2641 return InternalInconsistencyStatus();
2642 }
2643 std::vector<IndexedDBBlobInfo>::iterator iter;
2644 for (iter = value->blob_info.begin(); iter != value->blob_info.end();
2645 ++iter) {
2646 iter->set_file_path(
2647 backing_store_->GetBlobFileName(database_id, iter->key()));
2648 iter->set_mark_used_callback(
2649 backing_store_->active_blob_registry()->GetAddBlobRefCallback(
2650 database_id, iter->key()));
2651 iter->set_release_callback(
2652 backing_store_->active_blob_registry()->GetFinalReleaseCallback(
2653 database_id, iter->key()));
2654 if (iter->is_file()) {
2655 base::File::Info info;
2656 if (base::GetFileInfo(iter->file_path(), &info)) {
2657 // This should always work, but it isn't fatal if it doesn't; it just
2658 // means a potential slow synchronous call from the renderer later.
2659 iter->set_last_modified(info.last_modified);
2660 iter->set_size(info.size);
2661 }
2662 }
2663 }
2664 }
2665 return leveldb::Status::OK();
2666 }
2667
CleanPrimaryJournalIgnoreReturn()2668 void IndexedDBBackingStore::CleanPrimaryJournalIgnoreReturn() {
2669 CleanUpBlobJournal(BlobJournalKey::Encode());
2670 }
2671
SetMaxIndexId(LevelDBTransaction * transaction,int64 database_id,int64 object_store_id,int64 index_id)2672 WARN_UNUSED_RESULT static leveldb::Status SetMaxIndexId(
2673 LevelDBTransaction* transaction,
2674 int64 database_id,
2675 int64 object_store_id,
2676 int64 index_id) {
2677 int64 max_index_id = -1;
2678 const std::string max_index_id_key = ObjectStoreMetaDataKey::Encode(
2679 database_id, object_store_id, ObjectStoreMetaDataKey::MAX_INDEX_ID);
2680 bool found = false;
2681 leveldb::Status s =
2682 GetInt(transaction, max_index_id_key, &max_index_id, &found);
2683 if (!s.ok()) {
2684 INTERNAL_READ_ERROR_UNTESTED(SET_MAX_INDEX_ID);
2685 return s;
2686 }
2687 if (!found)
2688 max_index_id = kMinimumIndexId;
2689
2690 if (index_id <= max_index_id) {
2691 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_MAX_INDEX_ID);
2692 return InternalInconsistencyStatus();
2693 }
2694
2695 PutInt(transaction, max_index_id_key, index_id);
2696 return s;
2697 }
2698
CreateIndex(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id,int64 index_id,const base::string16 & name,const IndexedDBKeyPath & key_path,bool is_unique,bool is_multi_entry)2699 leveldb::Status IndexedDBBackingStore::CreateIndex(
2700 IndexedDBBackingStore::Transaction* transaction,
2701 int64 database_id,
2702 int64 object_store_id,
2703 int64 index_id,
2704 const base::string16& name,
2705 const IndexedDBKeyPath& key_path,
2706 bool is_unique,
2707 bool is_multi_entry) {
2708 IDB_TRACE("IndexedDBBackingStore::CreateIndex");
2709 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
2710 return InvalidDBKeyStatus();
2711 LevelDBTransaction* leveldb_transaction = transaction->transaction();
2712 leveldb::Status s = SetMaxIndexId(
2713 leveldb_transaction, database_id, object_store_id, index_id);
2714
2715 if (!s.ok())
2716 return s;
2717
2718 const std::string name_key = IndexMetaDataKey::Encode(
2719 database_id, object_store_id, index_id, IndexMetaDataKey::NAME);
2720 const std::string unique_key = IndexMetaDataKey::Encode(
2721 database_id, object_store_id, index_id, IndexMetaDataKey::UNIQUE);
2722 const std::string key_path_key = IndexMetaDataKey::Encode(
2723 database_id, object_store_id, index_id, IndexMetaDataKey::KEY_PATH);
2724 const std::string multi_entry_key = IndexMetaDataKey::Encode(
2725 database_id, object_store_id, index_id, IndexMetaDataKey::MULTI_ENTRY);
2726
2727 PutString(leveldb_transaction, name_key, name);
2728 PutBool(leveldb_transaction, unique_key, is_unique);
2729 PutIDBKeyPath(leveldb_transaction, key_path_key, key_path);
2730 PutBool(leveldb_transaction, multi_entry_key, is_multi_entry);
2731 return s;
2732 }
2733
DeleteIndex(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id,int64 index_id)2734 leveldb::Status IndexedDBBackingStore::DeleteIndex(
2735 IndexedDBBackingStore::Transaction* transaction,
2736 int64 database_id,
2737 int64 object_store_id,
2738 int64 index_id) {
2739 IDB_TRACE("IndexedDBBackingStore::DeleteIndex");
2740 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
2741 return InvalidDBKeyStatus();
2742 LevelDBTransaction* leveldb_transaction = transaction->transaction();
2743
2744 const std::string index_meta_data_start =
2745 IndexMetaDataKey::Encode(database_id, object_store_id, index_id, 0);
2746 const std::string index_meta_data_end =
2747 IndexMetaDataKey::EncodeMaxKey(database_id, object_store_id, index_id);
2748 leveldb::Status s = DeleteRangeBasic(
2749 leveldb_transaction, index_meta_data_start, index_meta_data_end, true);
2750
2751 if (s.ok()) {
2752 const std::string index_data_start =
2753 IndexDataKey::EncodeMinKey(database_id, object_store_id, index_id);
2754 const std::string index_data_end =
2755 IndexDataKey::EncodeMaxKey(database_id, object_store_id, index_id);
2756 s = DeleteRangeBasic(
2757 leveldb_transaction, index_data_start, index_data_end, true);
2758 }
2759
2760 if (!s.ok())
2761 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_INDEX);
2762
2763 return s;
2764 }
2765
PutIndexDataForRecord(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id,int64 index_id,const IndexedDBKey & key,const RecordIdentifier & record_identifier)2766 leveldb::Status IndexedDBBackingStore::PutIndexDataForRecord(
2767 IndexedDBBackingStore::Transaction* transaction,
2768 int64 database_id,
2769 int64 object_store_id,
2770 int64 index_id,
2771 const IndexedDBKey& key,
2772 const RecordIdentifier& record_identifier) {
2773 IDB_TRACE("IndexedDBBackingStore::PutIndexDataForRecord");
2774 DCHECK(key.IsValid());
2775 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
2776 return InvalidDBKeyStatus();
2777
2778 std::string encoded_key;
2779 EncodeIDBKey(key, &encoded_key);
2780
2781 const std::string index_data_key =
2782 IndexDataKey::Encode(database_id,
2783 object_store_id,
2784 index_id,
2785 encoded_key,
2786 record_identifier.primary_key(),
2787 0);
2788
2789 std::string data;
2790 EncodeVarInt(record_identifier.version(), &data);
2791 data.append(record_identifier.primary_key());
2792
2793 transaction->transaction()->Put(index_data_key, &data);
2794 return leveldb::Status::OK();
2795 }
2796
FindGreatestKeyLessThanOrEqual(LevelDBTransaction * transaction,const std::string & target,std::string * found_key,leveldb::Status & s)2797 static bool FindGreatestKeyLessThanOrEqual(LevelDBTransaction* transaction,
2798 const std::string& target,
2799 std::string* found_key,
2800 leveldb::Status& s) {
2801 scoped_ptr<LevelDBIterator> it = transaction->CreateIterator();
2802 s = it->Seek(target);
2803 if (!s.ok())
2804 return false;
2805
2806 if (!it->IsValid()) {
2807 s = it->SeekToLast();
2808 if (!s.ok() || !it->IsValid())
2809 return false;
2810 }
2811
2812 while (CompareIndexKeys(it->Key(), target) > 0) {
2813 s = it->Prev();
2814 if (!s.ok() || !it->IsValid())
2815 return false;
2816 }
2817
2818 do {
2819 *found_key = it->Key().as_string();
2820
2821 // There can be several index keys that compare equal. We want the last one.
2822 s = it->Next();
2823 } while (s.ok() && it->IsValid() && !CompareIndexKeys(it->Key(), target));
2824
2825 return true;
2826 }
2827
VersionExists(LevelDBTransaction * transaction,int64 database_id,int64 object_store_id,int64 version,const std::string & encoded_primary_key,bool * exists)2828 static leveldb::Status VersionExists(LevelDBTransaction* transaction,
2829 int64 database_id,
2830 int64 object_store_id,
2831 int64 version,
2832 const std::string& encoded_primary_key,
2833 bool* exists) {
2834 const std::string key =
2835 ExistsEntryKey::Encode(database_id, object_store_id, encoded_primary_key);
2836 std::string data;
2837
2838 leveldb::Status s = transaction->Get(key, &data, exists);
2839 if (!s.ok()) {
2840 INTERNAL_READ_ERROR_UNTESTED(VERSION_EXISTS);
2841 return s;
2842 }
2843 if (!*exists)
2844 return s;
2845
2846 StringPiece slice(data);
2847 int64 decoded;
2848 if (!DecodeInt(&slice, &decoded) || !slice.empty())
2849 return InternalInconsistencyStatus();
2850 *exists = (decoded == version);
2851 return s;
2852 }
2853
FindKeyInIndex(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id,int64 index_id,const IndexedDBKey & key,std::string * found_encoded_primary_key,bool * found)2854 leveldb::Status IndexedDBBackingStore::FindKeyInIndex(
2855 IndexedDBBackingStore::Transaction* transaction,
2856 int64 database_id,
2857 int64 object_store_id,
2858 int64 index_id,
2859 const IndexedDBKey& key,
2860 std::string* found_encoded_primary_key,
2861 bool* found) {
2862 IDB_TRACE("IndexedDBBackingStore::FindKeyInIndex");
2863 DCHECK(KeyPrefix::ValidIds(database_id, object_store_id, index_id));
2864
2865 DCHECK(found_encoded_primary_key->empty());
2866 *found = false;
2867
2868 LevelDBTransaction* leveldb_transaction = transaction->transaction();
2869 const std::string leveldb_key =
2870 IndexDataKey::Encode(database_id, object_store_id, index_id, key);
2871 scoped_ptr<LevelDBIterator> it = leveldb_transaction->CreateIterator();
2872 leveldb::Status s = it->Seek(leveldb_key);
2873 if (!s.ok()) {
2874 INTERNAL_READ_ERROR_UNTESTED(FIND_KEY_IN_INDEX);
2875 return s;
2876 }
2877
2878 for (;;) {
2879 if (!it->IsValid())
2880 return leveldb::Status::OK();
2881 if (CompareIndexKeys(it->Key(), leveldb_key) > 0)
2882 return leveldb::Status::OK();
2883
2884 StringPiece slice(it->Value());
2885
2886 int64 version;
2887 if (!DecodeVarInt(&slice, &version)) {
2888 INTERNAL_READ_ERROR_UNTESTED(FIND_KEY_IN_INDEX);
2889 return InternalInconsistencyStatus();
2890 }
2891 *found_encoded_primary_key = slice.as_string();
2892
2893 bool exists = false;
2894 s = VersionExists(leveldb_transaction,
2895 database_id,
2896 object_store_id,
2897 version,
2898 *found_encoded_primary_key,
2899 &exists);
2900 if (!s.ok())
2901 return s;
2902 if (!exists) {
2903 // Delete stale index data entry and continue.
2904 leveldb_transaction->Remove(it->Key());
2905 s = it->Next();
2906 continue;
2907 }
2908 *found = true;
2909 return s;
2910 }
2911 }
2912
GetPrimaryKeyViaIndex(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id,int64 index_id,const IndexedDBKey & key,scoped_ptr<IndexedDBKey> * primary_key)2913 leveldb::Status IndexedDBBackingStore::GetPrimaryKeyViaIndex(
2914 IndexedDBBackingStore::Transaction* transaction,
2915 int64 database_id,
2916 int64 object_store_id,
2917 int64 index_id,
2918 const IndexedDBKey& key,
2919 scoped_ptr<IndexedDBKey>* primary_key) {
2920 IDB_TRACE("IndexedDBBackingStore::GetPrimaryKeyViaIndex");
2921 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
2922 return InvalidDBKeyStatus();
2923
2924 bool found = false;
2925 std::string found_encoded_primary_key;
2926 leveldb::Status s = FindKeyInIndex(transaction,
2927 database_id,
2928 object_store_id,
2929 index_id,
2930 key,
2931 &found_encoded_primary_key,
2932 &found);
2933 if (!s.ok()) {
2934 INTERNAL_READ_ERROR_UNTESTED(GET_PRIMARY_KEY_VIA_INDEX);
2935 return s;
2936 }
2937 if (!found)
2938 return s;
2939 if (!found_encoded_primary_key.size()) {
2940 INTERNAL_READ_ERROR_UNTESTED(GET_PRIMARY_KEY_VIA_INDEX);
2941 return InvalidDBKeyStatus();
2942 }
2943
2944 StringPiece slice(found_encoded_primary_key);
2945 if (DecodeIDBKey(&slice, primary_key) && slice.empty())
2946 return s;
2947 else
2948 return InvalidDBKeyStatus();
2949 }
2950
KeyExistsInIndex(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id,int64 index_id,const IndexedDBKey & index_key,scoped_ptr<IndexedDBKey> * found_primary_key,bool * exists)2951 leveldb::Status IndexedDBBackingStore::KeyExistsInIndex(
2952 IndexedDBBackingStore::Transaction* transaction,
2953 int64 database_id,
2954 int64 object_store_id,
2955 int64 index_id,
2956 const IndexedDBKey& index_key,
2957 scoped_ptr<IndexedDBKey>* found_primary_key,
2958 bool* exists) {
2959 IDB_TRACE("IndexedDBBackingStore::KeyExistsInIndex");
2960 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
2961 return InvalidDBKeyStatus();
2962
2963 *exists = false;
2964 std::string found_encoded_primary_key;
2965 leveldb::Status s = FindKeyInIndex(transaction,
2966 database_id,
2967 object_store_id,
2968 index_id,
2969 index_key,
2970 &found_encoded_primary_key,
2971 exists);
2972 if (!s.ok()) {
2973 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_INDEX);
2974 return s;
2975 }
2976 if (!*exists)
2977 return leveldb::Status::OK();
2978 if (found_encoded_primary_key.empty()) {
2979 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_INDEX);
2980 return InvalidDBKeyStatus();
2981 }
2982
2983 StringPiece slice(found_encoded_primary_key);
2984 if (DecodeIDBKey(&slice, found_primary_key) && slice.empty())
2985 return s;
2986 else
2987 return InvalidDBKeyStatus();
2988 }
2989
Cursor(const IndexedDBBackingStore::Cursor * other)2990 IndexedDBBackingStore::Cursor::Cursor(
2991 const IndexedDBBackingStore::Cursor* other)
2992 : backing_store_(other->backing_store_),
2993 transaction_(other->transaction_),
2994 database_id_(other->database_id_),
2995 cursor_options_(other->cursor_options_),
2996 current_key_(new IndexedDBKey(*other->current_key_)) {
2997 if (other->iterator_) {
2998 iterator_ = transaction_->transaction()->CreateIterator();
2999
3000 if (other->iterator_->IsValid()) {
3001 leveldb::Status s = iterator_->Seek(other->iterator_->Key());
3002 // TODO(cmumford): Handle this error (crbug.com/363397)
3003 DCHECK(iterator_->IsValid());
3004 }
3005 }
3006 }
3007
Cursor(scoped_refptr<IndexedDBBackingStore> backing_store,IndexedDBBackingStore::Transaction * transaction,int64 database_id,const CursorOptions & cursor_options)3008 IndexedDBBackingStore::Cursor::Cursor(
3009 scoped_refptr<IndexedDBBackingStore> backing_store,
3010 IndexedDBBackingStore::Transaction* transaction,
3011 int64 database_id,
3012 const CursorOptions& cursor_options)
3013 : backing_store_(backing_store),
3014 transaction_(transaction),
3015 database_id_(database_id),
3016 cursor_options_(cursor_options) {
3017 }
~Cursor()3018 IndexedDBBackingStore::Cursor::~Cursor() {}
3019
FirstSeek(leveldb::Status * s)3020 bool IndexedDBBackingStore::Cursor::FirstSeek(leveldb::Status* s) {
3021 iterator_ = transaction_->transaction()->CreateIterator();
3022 if (cursor_options_.forward)
3023 *s = iterator_->Seek(cursor_options_.low_key);
3024 else
3025 *s = iterator_->Seek(cursor_options_.high_key);
3026 if (!s->ok())
3027 return false;
3028
3029 return Continue(0, READY, s);
3030 }
3031
Advance(uint32 count,leveldb::Status * s)3032 bool IndexedDBBackingStore::Cursor::Advance(uint32 count, leveldb::Status* s) {
3033 *s = leveldb::Status::OK();
3034 while (count--) {
3035 if (!Continue(s))
3036 return false;
3037 }
3038 return true;
3039 }
3040
Continue(const IndexedDBKey * key,const IndexedDBKey * primary_key,IteratorState next_state,leveldb::Status * s)3041 bool IndexedDBBackingStore::Cursor::Continue(const IndexedDBKey* key,
3042 const IndexedDBKey* primary_key,
3043 IteratorState next_state,
3044 leveldb::Status* s) {
3045 DCHECK(!key || key->IsValid());
3046 DCHECK(!primary_key || primary_key->IsValid());
3047 *s = leveldb::Status::OK();
3048
3049 // TODO(alecflett): avoid a copy here?
3050 IndexedDBKey previous_key = current_key_ ? *current_key_ : IndexedDBKey();
3051
3052 bool first_iteration = true;
3053
3054 // When iterating with PrevNoDuplicate, spec requires that the
3055 // value we yield for each key is the first duplicate in forwards
3056 // order.
3057 IndexedDBKey last_duplicate_key;
3058
3059 bool forward = cursor_options_.forward;
3060
3061 for (;;) {
3062 if (next_state == SEEK) {
3063 // TODO(jsbell): Optimize seeking for reverse cursors as well.
3064 if (first_iteration && key && forward) {
3065 std::string leveldb_key;
3066 if (primary_key) {
3067 leveldb_key = EncodeKey(*key, *primary_key);
3068 } else {
3069 leveldb_key = EncodeKey(*key);
3070 }
3071 *s = iterator_->Seek(leveldb_key);
3072 first_iteration = false;
3073 } else if (forward) {
3074 *s = iterator_->Next();
3075 } else {
3076 *s = iterator_->Prev();
3077 }
3078 if (!s->ok())
3079 return false;
3080 } else {
3081 next_state = SEEK; // for subsequent iterations
3082 }
3083
3084 if (!iterator_->IsValid()) {
3085 if (!forward && last_duplicate_key.IsValid()) {
3086 // We need to walk forward because we hit the end of
3087 // the data.
3088 forward = true;
3089 continue;
3090 }
3091
3092 return false;
3093 }
3094
3095 if (IsPastBounds()) {
3096 if (!forward && last_duplicate_key.IsValid()) {
3097 // We need to walk forward because now we're beyond the
3098 // bounds defined by the cursor.
3099 forward = true;
3100 continue;
3101 }
3102
3103 return false;
3104 }
3105
3106 if (!HaveEnteredRange())
3107 continue;
3108
3109 // The row may not load because there's a stale entry in the
3110 // index. This is not fatal.
3111 if (!LoadCurrentRow())
3112 continue;
3113
3114 if (key) {
3115 if (forward) {
3116 if (primary_key && current_key_->Equals(*key) &&
3117 this->primary_key().IsLessThan(*primary_key))
3118 continue;
3119 if (current_key_->IsLessThan(*key))
3120 continue;
3121 } else {
3122 if (primary_key && key->Equals(*current_key_) &&
3123 primary_key->IsLessThan(this->primary_key()))
3124 continue;
3125 if (key->IsLessThan(*current_key_))
3126 continue;
3127 }
3128 }
3129
3130 if (cursor_options_.unique) {
3131 if (previous_key.IsValid() && current_key_->Equals(previous_key)) {
3132 // We should never be able to walk forward all the way
3133 // to the previous key.
3134 DCHECK(!last_duplicate_key.IsValid());
3135 continue;
3136 }
3137
3138 if (!forward) {
3139 if (!last_duplicate_key.IsValid()) {
3140 last_duplicate_key = *current_key_;
3141 continue;
3142 }
3143
3144 // We need to walk forward because we hit the boundary
3145 // between key ranges.
3146 if (!last_duplicate_key.Equals(*current_key_)) {
3147 forward = true;
3148 continue;
3149 }
3150
3151 continue;
3152 }
3153 }
3154 break;
3155 }
3156
3157 DCHECK(!last_duplicate_key.IsValid() ||
3158 (forward && last_duplicate_key.Equals(*current_key_)));
3159 return true;
3160 }
3161
HaveEnteredRange() const3162 bool IndexedDBBackingStore::Cursor::HaveEnteredRange() const {
3163 if (cursor_options_.forward) {
3164 int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.low_key);
3165 if (cursor_options_.low_open) {
3166 return compare > 0;
3167 }
3168 return compare >= 0;
3169 }
3170 int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.high_key);
3171 if (cursor_options_.high_open) {
3172 return compare < 0;
3173 }
3174 return compare <= 0;
3175 }
3176
IsPastBounds() const3177 bool IndexedDBBackingStore::Cursor::IsPastBounds() const {
3178 if (cursor_options_.forward) {
3179 int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.high_key);
3180 if (cursor_options_.high_open) {
3181 return compare >= 0;
3182 }
3183 return compare > 0;
3184 }
3185 int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.low_key);
3186 if (cursor_options_.low_open) {
3187 return compare <= 0;
3188 }
3189 return compare < 0;
3190 }
3191
primary_key() const3192 const IndexedDBKey& IndexedDBBackingStore::Cursor::primary_key() const {
3193 return *current_key_;
3194 }
3195
3196 const IndexedDBBackingStore::RecordIdentifier&
record_identifier() const3197 IndexedDBBackingStore::Cursor::record_identifier() const {
3198 return record_identifier_;
3199 }
3200
3201 class ObjectStoreKeyCursorImpl : public IndexedDBBackingStore::Cursor {
3202 public:
ObjectStoreKeyCursorImpl(scoped_refptr<IndexedDBBackingStore> backing_store,IndexedDBBackingStore::Transaction * transaction,int64 database_id,const IndexedDBBackingStore::Cursor::CursorOptions & cursor_options)3203 ObjectStoreKeyCursorImpl(
3204 scoped_refptr<IndexedDBBackingStore> backing_store,
3205 IndexedDBBackingStore::Transaction* transaction,
3206 int64 database_id,
3207 const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
3208 : IndexedDBBackingStore::Cursor(backing_store,
3209 transaction,
3210 database_id,
3211 cursor_options) {}
3212
Clone()3213 virtual Cursor* Clone() OVERRIDE {
3214 return new ObjectStoreKeyCursorImpl(this);
3215 }
3216
3217 // IndexedDBBackingStore::Cursor
value()3218 virtual IndexedDBValue* value() OVERRIDE {
3219 NOTREACHED();
3220 return NULL;
3221 }
3222 virtual bool LoadCurrentRow() OVERRIDE;
3223
3224 protected:
EncodeKey(const IndexedDBKey & key)3225 virtual std::string EncodeKey(const IndexedDBKey& key) OVERRIDE {
3226 return ObjectStoreDataKey::Encode(
3227 cursor_options_.database_id, cursor_options_.object_store_id, key);
3228 }
EncodeKey(const IndexedDBKey & key,const IndexedDBKey & primary_key)3229 virtual std::string EncodeKey(const IndexedDBKey& key,
3230 const IndexedDBKey& primary_key) OVERRIDE {
3231 NOTREACHED();
3232 return std::string();
3233 }
3234
3235 private:
ObjectStoreKeyCursorImpl(const ObjectStoreKeyCursorImpl * other)3236 explicit ObjectStoreKeyCursorImpl(const ObjectStoreKeyCursorImpl* other)
3237 : IndexedDBBackingStore::Cursor(other) {}
3238
3239 DISALLOW_COPY_AND_ASSIGN(ObjectStoreKeyCursorImpl);
3240 };
3241
LoadCurrentRow()3242 bool ObjectStoreKeyCursorImpl::LoadCurrentRow() {
3243 StringPiece slice(iterator_->Key());
3244 ObjectStoreDataKey object_store_data_key;
3245 if (!ObjectStoreDataKey::Decode(&slice, &object_store_data_key)) {
3246 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3247 return false;
3248 }
3249
3250 current_key_ = object_store_data_key.user_key();
3251
3252 int64 version;
3253 slice = StringPiece(iterator_->Value());
3254 if (!DecodeVarInt(&slice, &version)) {
3255 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3256 return false;
3257 }
3258
3259 // TODO(jsbell): This re-encodes what was just decoded; try and optimize.
3260 std::string encoded_key;
3261 EncodeIDBKey(*current_key_, &encoded_key);
3262 record_identifier_.Reset(encoded_key, version);
3263
3264 return true;
3265 }
3266
3267 class ObjectStoreCursorImpl : public IndexedDBBackingStore::Cursor {
3268 public:
ObjectStoreCursorImpl(scoped_refptr<IndexedDBBackingStore> backing_store,IndexedDBBackingStore::Transaction * transaction,int64 database_id,const IndexedDBBackingStore::Cursor::CursorOptions & cursor_options)3269 ObjectStoreCursorImpl(
3270 scoped_refptr<IndexedDBBackingStore> backing_store,
3271 IndexedDBBackingStore::Transaction* transaction,
3272 int64 database_id,
3273 const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
3274 : IndexedDBBackingStore::Cursor(backing_store,
3275 transaction,
3276 database_id,
3277 cursor_options) {}
3278
Clone()3279 virtual Cursor* Clone() OVERRIDE { return new ObjectStoreCursorImpl(this); }
3280
3281 // IndexedDBBackingStore::Cursor
value()3282 virtual IndexedDBValue* value() OVERRIDE { return ¤t_value_; }
3283 virtual bool LoadCurrentRow() OVERRIDE;
3284
3285 protected:
EncodeKey(const IndexedDBKey & key)3286 virtual std::string EncodeKey(const IndexedDBKey& key) OVERRIDE {
3287 return ObjectStoreDataKey::Encode(
3288 cursor_options_.database_id, cursor_options_.object_store_id, key);
3289 }
EncodeKey(const IndexedDBKey & key,const IndexedDBKey & primary_key)3290 virtual std::string EncodeKey(const IndexedDBKey& key,
3291 const IndexedDBKey& primary_key) OVERRIDE {
3292 NOTREACHED();
3293 return std::string();
3294 }
3295
3296 private:
ObjectStoreCursorImpl(const ObjectStoreCursorImpl * other)3297 explicit ObjectStoreCursorImpl(const ObjectStoreCursorImpl* other)
3298 : IndexedDBBackingStore::Cursor(other),
3299 current_value_(other->current_value_) {}
3300
3301 IndexedDBValue current_value_;
3302
3303 DISALLOW_COPY_AND_ASSIGN(ObjectStoreCursorImpl);
3304 };
3305
LoadCurrentRow()3306 bool ObjectStoreCursorImpl::LoadCurrentRow() {
3307 StringPiece key_slice(iterator_->Key());
3308 ObjectStoreDataKey object_store_data_key;
3309 if (!ObjectStoreDataKey::Decode(&key_slice, &object_store_data_key)) {
3310 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3311 return false;
3312 }
3313
3314 current_key_ = object_store_data_key.user_key();
3315
3316 int64 version;
3317 StringPiece value_slice = StringPiece(iterator_->Value());
3318 if (!DecodeVarInt(&value_slice, &version)) {
3319 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3320 return false;
3321 }
3322
3323 // TODO(jsbell): This re-encodes what was just decoded; try and optimize.
3324 std::string encoded_key;
3325 EncodeIDBKey(*current_key_, &encoded_key);
3326 record_identifier_.Reset(encoded_key, version);
3327
3328 if (!transaction_->GetBlobInfoForRecord(database_id_,
3329 iterator_->Key().as_string(),
3330 ¤t_value_).ok()) {
3331 return false;
3332 }
3333 current_value_.bits = value_slice.as_string();
3334 return true;
3335 }
3336
3337 class IndexKeyCursorImpl : public IndexedDBBackingStore::Cursor {
3338 public:
IndexKeyCursorImpl(scoped_refptr<IndexedDBBackingStore> backing_store,IndexedDBBackingStore::Transaction * transaction,int64 database_id,const IndexedDBBackingStore::Cursor::CursorOptions & cursor_options)3339 IndexKeyCursorImpl(
3340 scoped_refptr<IndexedDBBackingStore> backing_store,
3341 IndexedDBBackingStore::Transaction* transaction,
3342 int64 database_id,
3343 const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
3344 : IndexedDBBackingStore::Cursor(backing_store,
3345 transaction,
3346 database_id,
3347 cursor_options) {}
3348
Clone()3349 virtual Cursor* Clone() OVERRIDE { return new IndexKeyCursorImpl(this); }
3350
3351 // IndexedDBBackingStore::Cursor
value()3352 virtual IndexedDBValue* value() OVERRIDE {
3353 NOTREACHED();
3354 return NULL;
3355 }
primary_key() const3356 virtual const IndexedDBKey& primary_key() const OVERRIDE {
3357 return *primary_key_;
3358 }
record_identifier() const3359 virtual const IndexedDBBackingStore::RecordIdentifier& record_identifier()
3360 const OVERRIDE {
3361 NOTREACHED();
3362 return record_identifier_;
3363 }
3364 virtual bool LoadCurrentRow() OVERRIDE;
3365
3366 protected:
EncodeKey(const IndexedDBKey & key)3367 virtual std::string EncodeKey(const IndexedDBKey& key) OVERRIDE {
3368 return IndexDataKey::Encode(cursor_options_.database_id,
3369 cursor_options_.object_store_id,
3370 cursor_options_.index_id,
3371 key);
3372 }
EncodeKey(const IndexedDBKey & key,const IndexedDBKey & primary_key)3373 virtual std::string EncodeKey(const IndexedDBKey& key,
3374 const IndexedDBKey& primary_key) OVERRIDE {
3375 return IndexDataKey::Encode(cursor_options_.database_id,
3376 cursor_options_.object_store_id,
3377 cursor_options_.index_id,
3378 key,
3379 primary_key);
3380 }
3381
3382 private:
IndexKeyCursorImpl(const IndexKeyCursorImpl * other)3383 explicit IndexKeyCursorImpl(const IndexKeyCursorImpl* other)
3384 : IndexedDBBackingStore::Cursor(other),
3385 primary_key_(new IndexedDBKey(*other->primary_key_)) {}
3386
3387 scoped_ptr<IndexedDBKey> primary_key_;
3388
3389 DISALLOW_COPY_AND_ASSIGN(IndexKeyCursorImpl);
3390 };
3391
LoadCurrentRow()3392 bool IndexKeyCursorImpl::LoadCurrentRow() {
3393 StringPiece slice(iterator_->Key());
3394 IndexDataKey index_data_key;
3395 if (!IndexDataKey::Decode(&slice, &index_data_key)) {
3396 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3397 return false;
3398 }
3399
3400 current_key_ = index_data_key.user_key();
3401 DCHECK(current_key_);
3402
3403 slice = StringPiece(iterator_->Value());
3404 int64 index_data_version;
3405 if (!DecodeVarInt(&slice, &index_data_version)) {
3406 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3407 return false;
3408 }
3409
3410 if (!DecodeIDBKey(&slice, &primary_key_) || !slice.empty()) {
3411 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3412 return false;
3413 }
3414
3415 std::string primary_leveldb_key =
3416 ObjectStoreDataKey::Encode(index_data_key.DatabaseId(),
3417 index_data_key.ObjectStoreId(),
3418 *primary_key_);
3419
3420 std::string result;
3421 bool found = false;
3422 leveldb::Status s =
3423 transaction_->transaction()->Get(primary_leveldb_key, &result, &found);
3424 if (!s.ok()) {
3425 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3426 return false;
3427 }
3428 if (!found) {
3429 transaction_->transaction()->Remove(iterator_->Key());
3430 return false;
3431 }
3432 if (!result.size()) {
3433 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3434 return false;
3435 }
3436
3437 int64 object_store_data_version;
3438 slice = StringPiece(result);
3439 if (!DecodeVarInt(&slice, &object_store_data_version)) {
3440 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3441 return false;
3442 }
3443
3444 if (object_store_data_version != index_data_version) {
3445 transaction_->transaction()->Remove(iterator_->Key());
3446 return false;
3447 }
3448
3449 return true;
3450 }
3451
3452 class IndexCursorImpl : public IndexedDBBackingStore::Cursor {
3453 public:
IndexCursorImpl(scoped_refptr<IndexedDBBackingStore> backing_store,IndexedDBBackingStore::Transaction * transaction,int64 database_id,const IndexedDBBackingStore::Cursor::CursorOptions & cursor_options)3454 IndexCursorImpl(
3455 scoped_refptr<IndexedDBBackingStore> backing_store,
3456 IndexedDBBackingStore::Transaction* transaction,
3457 int64 database_id,
3458 const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
3459 : IndexedDBBackingStore::Cursor(backing_store,
3460 transaction,
3461 database_id,
3462 cursor_options) {}
3463
Clone()3464 virtual Cursor* Clone() OVERRIDE { return new IndexCursorImpl(this); }
3465
3466 // IndexedDBBackingStore::Cursor
value()3467 virtual IndexedDBValue* value() OVERRIDE { return ¤t_value_; }
primary_key() const3468 virtual const IndexedDBKey& primary_key() const OVERRIDE {
3469 return *primary_key_;
3470 }
record_identifier() const3471 virtual const IndexedDBBackingStore::RecordIdentifier& record_identifier()
3472 const OVERRIDE {
3473 NOTREACHED();
3474 return record_identifier_;
3475 }
3476 virtual bool LoadCurrentRow() OVERRIDE;
3477
3478 protected:
EncodeKey(const IndexedDBKey & key)3479 virtual std::string EncodeKey(const IndexedDBKey& key) OVERRIDE {
3480 return IndexDataKey::Encode(cursor_options_.database_id,
3481 cursor_options_.object_store_id,
3482 cursor_options_.index_id,
3483 key);
3484 }
EncodeKey(const IndexedDBKey & key,const IndexedDBKey & primary_key)3485 virtual std::string EncodeKey(const IndexedDBKey& key,
3486 const IndexedDBKey& primary_key) OVERRIDE {
3487 return IndexDataKey::Encode(cursor_options_.database_id,
3488 cursor_options_.object_store_id,
3489 cursor_options_.index_id,
3490 key,
3491 primary_key);
3492 }
3493
3494 private:
IndexCursorImpl(const IndexCursorImpl * other)3495 explicit IndexCursorImpl(const IndexCursorImpl* other)
3496 : IndexedDBBackingStore::Cursor(other),
3497 primary_key_(new IndexedDBKey(*other->primary_key_)),
3498 current_value_(other->current_value_),
3499 primary_leveldb_key_(other->primary_leveldb_key_) {}
3500
3501 scoped_ptr<IndexedDBKey> primary_key_;
3502 IndexedDBValue current_value_;
3503 std::string primary_leveldb_key_;
3504
3505 DISALLOW_COPY_AND_ASSIGN(IndexCursorImpl);
3506 };
3507
LoadCurrentRow()3508 bool IndexCursorImpl::LoadCurrentRow() {
3509 StringPiece slice(iterator_->Key());
3510 IndexDataKey index_data_key;
3511 if (!IndexDataKey::Decode(&slice, &index_data_key)) {
3512 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3513 return false;
3514 }
3515
3516 current_key_ = index_data_key.user_key();
3517 DCHECK(current_key_);
3518
3519 slice = StringPiece(iterator_->Value());
3520 int64 index_data_version;
3521 if (!DecodeVarInt(&slice, &index_data_version)) {
3522 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3523 return false;
3524 }
3525 if (!DecodeIDBKey(&slice, &primary_key_)) {
3526 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3527 return false;
3528 }
3529
3530 DCHECK_EQ(index_data_key.DatabaseId(), database_id_);
3531 primary_leveldb_key_ =
3532 ObjectStoreDataKey::Encode(index_data_key.DatabaseId(),
3533 index_data_key.ObjectStoreId(),
3534 *primary_key_);
3535
3536 std::string result;
3537 bool found = false;
3538 leveldb::Status s =
3539 transaction_->transaction()->Get(primary_leveldb_key_, &result, &found);
3540 if (!s.ok()) {
3541 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3542 return false;
3543 }
3544 if (!found) {
3545 transaction_->transaction()->Remove(iterator_->Key());
3546 return false;
3547 }
3548 if (!result.size()) {
3549 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3550 return false;
3551 }
3552
3553 int64 object_store_data_version;
3554 slice = StringPiece(result);
3555 if (!DecodeVarInt(&slice, &object_store_data_version)) {
3556 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3557 return false;
3558 }
3559
3560 if (object_store_data_version != index_data_version) {
3561 transaction_->transaction()->Remove(iterator_->Key());
3562 return false;
3563 }
3564
3565 current_value_.bits = slice.as_string();
3566 return transaction_->GetBlobInfoForRecord(database_id_,
3567 primary_leveldb_key_,
3568 ¤t_value_).ok();
3569 }
3570
ObjectStoreCursorOptions(LevelDBTransaction * transaction,int64 database_id,int64 object_store_id,const IndexedDBKeyRange & range,indexed_db::CursorDirection direction,IndexedDBBackingStore::Cursor::CursorOptions * cursor_options)3571 bool ObjectStoreCursorOptions(
3572 LevelDBTransaction* transaction,
3573 int64 database_id,
3574 int64 object_store_id,
3575 const IndexedDBKeyRange& range,
3576 indexed_db::CursorDirection direction,
3577 IndexedDBBackingStore::Cursor::CursorOptions* cursor_options) {
3578 cursor_options->database_id = database_id;
3579 cursor_options->object_store_id = object_store_id;
3580
3581 bool lower_bound = range.lower().IsValid();
3582 bool upper_bound = range.upper().IsValid();
3583 cursor_options->forward =
3584 (direction == indexed_db::CURSOR_NEXT_NO_DUPLICATE ||
3585 direction == indexed_db::CURSOR_NEXT);
3586 cursor_options->unique = (direction == indexed_db::CURSOR_NEXT_NO_DUPLICATE ||
3587 direction == indexed_db::CURSOR_PREV_NO_DUPLICATE);
3588
3589 if (!lower_bound) {
3590 cursor_options->low_key =
3591 ObjectStoreDataKey::Encode(database_id, object_store_id, MinIDBKey());
3592 cursor_options->low_open = true; // Not included.
3593 } else {
3594 cursor_options->low_key =
3595 ObjectStoreDataKey::Encode(database_id, object_store_id, range.lower());
3596 cursor_options->low_open = range.lowerOpen();
3597 }
3598
3599 leveldb::Status s;
3600
3601 if (!upper_bound) {
3602 cursor_options->high_key =
3603 ObjectStoreDataKey::Encode(database_id, object_store_id, MaxIDBKey());
3604
3605 if (cursor_options->forward) {
3606 cursor_options->high_open = true; // Not included.
3607 } else {
3608 // We need a key that exists.
3609 // TODO(cmumford): Handle this error (crbug.com/363397)
3610 if (!FindGreatestKeyLessThanOrEqual(transaction,
3611 cursor_options->high_key,
3612 &cursor_options->high_key,
3613 s))
3614 return false;
3615 cursor_options->high_open = false;
3616 }
3617 } else {
3618 cursor_options->high_key =
3619 ObjectStoreDataKey::Encode(database_id, object_store_id, range.upper());
3620 cursor_options->high_open = range.upperOpen();
3621
3622 if (!cursor_options->forward) {
3623 // For reverse cursors, we need a key that exists.
3624 std::string found_high_key;
3625 // TODO(cmumford): Handle this error (crbug.com/363397)
3626 if (!FindGreatestKeyLessThanOrEqual(
3627 transaction, cursor_options->high_key, &found_high_key, s))
3628 return false;
3629
3630 // If the target key should not be included, but we end up with a smaller
3631 // key, we should include that.
3632 if (cursor_options->high_open &&
3633 CompareIndexKeys(found_high_key, cursor_options->high_key) < 0)
3634 cursor_options->high_open = false;
3635
3636 cursor_options->high_key = found_high_key;
3637 }
3638 }
3639
3640 return true;
3641 }
3642
IndexCursorOptions(LevelDBTransaction * transaction,int64 database_id,int64 object_store_id,int64 index_id,const IndexedDBKeyRange & range,indexed_db::CursorDirection direction,IndexedDBBackingStore::Cursor::CursorOptions * cursor_options)3643 bool IndexCursorOptions(
3644 LevelDBTransaction* transaction,
3645 int64 database_id,
3646 int64 object_store_id,
3647 int64 index_id,
3648 const IndexedDBKeyRange& range,
3649 indexed_db::CursorDirection direction,
3650 IndexedDBBackingStore::Cursor::CursorOptions* cursor_options) {
3651 DCHECK(transaction);
3652 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
3653 return false;
3654
3655 cursor_options->database_id = database_id;
3656 cursor_options->object_store_id = object_store_id;
3657 cursor_options->index_id = index_id;
3658
3659 bool lower_bound = range.lower().IsValid();
3660 bool upper_bound = range.upper().IsValid();
3661 cursor_options->forward =
3662 (direction == indexed_db::CURSOR_NEXT_NO_DUPLICATE ||
3663 direction == indexed_db::CURSOR_NEXT);
3664 cursor_options->unique = (direction == indexed_db::CURSOR_NEXT_NO_DUPLICATE ||
3665 direction == indexed_db::CURSOR_PREV_NO_DUPLICATE);
3666
3667 if (!lower_bound) {
3668 cursor_options->low_key =
3669 IndexDataKey::EncodeMinKey(database_id, object_store_id, index_id);
3670 cursor_options->low_open = false; // Included.
3671 } else {
3672 cursor_options->low_key = IndexDataKey::Encode(
3673 database_id, object_store_id, index_id, range.lower());
3674 cursor_options->low_open = range.lowerOpen();
3675 }
3676
3677 leveldb::Status s;
3678
3679 if (!upper_bound) {
3680 cursor_options->high_key =
3681 IndexDataKey::EncodeMaxKey(database_id, object_store_id, index_id);
3682 cursor_options->high_open = false; // Included.
3683
3684 if (!cursor_options->forward) { // We need a key that exists.
3685 if (!FindGreatestKeyLessThanOrEqual(transaction,
3686 cursor_options->high_key,
3687 &cursor_options->high_key,
3688 s))
3689 return false;
3690 cursor_options->high_open = false;
3691 }
3692 } else {
3693 cursor_options->high_key = IndexDataKey::Encode(
3694 database_id, object_store_id, index_id, range.upper());
3695 cursor_options->high_open = range.upperOpen();
3696
3697 std::string found_high_key;
3698 // Seek to the *last* key in the set of non-unique keys
3699 // TODO(cmumford): Handle this error (crbug.com/363397)
3700 if (!FindGreatestKeyLessThanOrEqual(
3701 transaction, cursor_options->high_key, &found_high_key, s))
3702 return false;
3703
3704 // If the target key should not be included, but we end up with a smaller
3705 // key, we should include that.
3706 if (cursor_options->high_open &&
3707 CompareIndexKeys(found_high_key, cursor_options->high_key) < 0)
3708 cursor_options->high_open = false;
3709
3710 cursor_options->high_key = found_high_key;
3711 }
3712
3713 return true;
3714 }
3715
3716 scoped_ptr<IndexedDBBackingStore::Cursor>
OpenObjectStoreCursor(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id,const IndexedDBKeyRange & range,indexed_db::CursorDirection direction,leveldb::Status * s)3717 IndexedDBBackingStore::OpenObjectStoreCursor(
3718 IndexedDBBackingStore::Transaction* transaction,
3719 int64 database_id,
3720 int64 object_store_id,
3721 const IndexedDBKeyRange& range,
3722 indexed_db::CursorDirection direction,
3723 leveldb::Status* s) {
3724 IDB_TRACE("IndexedDBBackingStore::OpenObjectStoreCursor");
3725 *s = leveldb::Status::OK();
3726 LevelDBTransaction* leveldb_transaction = transaction->transaction();
3727 IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
3728 if (!ObjectStoreCursorOptions(leveldb_transaction,
3729 database_id,
3730 object_store_id,
3731 range,
3732 direction,
3733 &cursor_options))
3734 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3735 scoped_ptr<ObjectStoreCursorImpl> cursor(new ObjectStoreCursorImpl(
3736 this, transaction, database_id, cursor_options));
3737 if (!cursor->FirstSeek(s))
3738 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3739
3740 return cursor.PassAs<IndexedDBBackingStore::Cursor>();
3741 }
3742
3743 scoped_ptr<IndexedDBBackingStore::Cursor>
OpenObjectStoreKeyCursor(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id,const IndexedDBKeyRange & range,indexed_db::CursorDirection direction,leveldb::Status * s)3744 IndexedDBBackingStore::OpenObjectStoreKeyCursor(
3745 IndexedDBBackingStore::Transaction* transaction,
3746 int64 database_id,
3747 int64 object_store_id,
3748 const IndexedDBKeyRange& range,
3749 indexed_db::CursorDirection direction,
3750 leveldb::Status* s) {
3751 IDB_TRACE("IndexedDBBackingStore::OpenObjectStoreKeyCursor");
3752 *s = leveldb::Status::OK();
3753 LevelDBTransaction* leveldb_transaction = transaction->transaction();
3754 IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
3755 if (!ObjectStoreCursorOptions(leveldb_transaction,
3756 database_id,
3757 object_store_id,
3758 range,
3759 direction,
3760 &cursor_options))
3761 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3762 scoped_ptr<ObjectStoreKeyCursorImpl> cursor(new ObjectStoreKeyCursorImpl(
3763 this, transaction, database_id, cursor_options));
3764 if (!cursor->FirstSeek(s))
3765 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3766
3767 return cursor.PassAs<IndexedDBBackingStore::Cursor>();
3768 }
3769
3770 scoped_ptr<IndexedDBBackingStore::Cursor>
OpenIndexKeyCursor(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id,int64 index_id,const IndexedDBKeyRange & range,indexed_db::CursorDirection direction,leveldb::Status * s)3771 IndexedDBBackingStore::OpenIndexKeyCursor(
3772 IndexedDBBackingStore::Transaction* transaction,
3773 int64 database_id,
3774 int64 object_store_id,
3775 int64 index_id,
3776 const IndexedDBKeyRange& range,
3777 indexed_db::CursorDirection direction,
3778 leveldb::Status* s) {
3779 IDB_TRACE("IndexedDBBackingStore::OpenIndexKeyCursor");
3780 *s = leveldb::Status::OK();
3781 LevelDBTransaction* leveldb_transaction = transaction->transaction();
3782 IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
3783 if (!IndexCursorOptions(leveldb_transaction,
3784 database_id,
3785 object_store_id,
3786 index_id,
3787 range,
3788 direction,
3789 &cursor_options))
3790 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3791 scoped_ptr<IndexKeyCursorImpl> cursor(
3792 new IndexKeyCursorImpl(this, transaction, database_id, cursor_options));
3793 if (!cursor->FirstSeek(s))
3794 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3795
3796 return cursor.PassAs<IndexedDBBackingStore::Cursor>();
3797 }
3798
3799 scoped_ptr<IndexedDBBackingStore::Cursor>
OpenIndexCursor(IndexedDBBackingStore::Transaction * transaction,int64 database_id,int64 object_store_id,int64 index_id,const IndexedDBKeyRange & range,indexed_db::CursorDirection direction,leveldb::Status * s)3800 IndexedDBBackingStore::OpenIndexCursor(
3801 IndexedDBBackingStore::Transaction* transaction,
3802 int64 database_id,
3803 int64 object_store_id,
3804 int64 index_id,
3805 const IndexedDBKeyRange& range,
3806 indexed_db::CursorDirection direction,
3807 leveldb::Status* s) {
3808 IDB_TRACE("IndexedDBBackingStore::OpenIndexCursor");
3809 LevelDBTransaction* leveldb_transaction = transaction->transaction();
3810 IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
3811 if (!IndexCursorOptions(leveldb_transaction,
3812 database_id,
3813 object_store_id,
3814 index_id,
3815 range,
3816 direction,
3817 &cursor_options))
3818 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3819 scoped_ptr<IndexCursorImpl> cursor(
3820 new IndexCursorImpl(this, transaction, database_id, cursor_options));
3821 if (!cursor->FirstSeek(s))
3822 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3823
3824 return cursor.PassAs<IndexedDBBackingStore::Cursor>();
3825 }
3826
Transaction(IndexedDBBackingStore * backing_store)3827 IndexedDBBackingStore::Transaction::Transaction(
3828 IndexedDBBackingStore* backing_store)
3829 : backing_store_(backing_store), database_id_(-1) {
3830 }
3831
~Transaction()3832 IndexedDBBackingStore::Transaction::~Transaction() {
3833 STLDeleteContainerPairSecondPointers(
3834 blob_change_map_.begin(), blob_change_map_.end());
3835 STLDeleteContainerPairSecondPointers(incognito_blob_map_.begin(),
3836 incognito_blob_map_.end());
3837 }
3838
Begin()3839 void IndexedDBBackingStore::Transaction::Begin() {
3840 IDB_TRACE("IndexedDBBackingStore::Transaction::Begin");
3841 DCHECK(!transaction_.get());
3842 transaction_ = IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
3843 backing_store_->db_.get());
3844
3845 // If incognito, this snapshots blobs just as the above transaction_
3846 // constructor snapshots the leveldb.
3847 BlobChangeMap::const_iterator iter;
3848 for (iter = backing_store_->incognito_blob_map_.begin();
3849 iter != backing_store_->incognito_blob_map_.end();
3850 ++iter)
3851 incognito_blob_map_[iter->first] = iter->second->Clone().release();
3852 }
3853
getURLFromUUID(const string & uuid)3854 static GURL getURLFromUUID(const string& uuid) {
3855 return GURL("blob:uuid/" + uuid);
3856 }
3857
HandleBlobPreTransaction(BlobEntryKeyValuePairVec * new_blob_entries,WriteDescriptorVec * new_files_to_write)3858 leveldb::Status IndexedDBBackingStore::Transaction::HandleBlobPreTransaction(
3859 BlobEntryKeyValuePairVec* new_blob_entries,
3860 WriteDescriptorVec* new_files_to_write) {
3861 if (backing_store_->is_incognito())
3862 return leveldb::Status::OK();
3863
3864 BlobChangeMap::iterator iter = blob_change_map_.begin();
3865 new_blob_entries->clear();
3866 new_files_to_write->clear();
3867 if (iter != blob_change_map_.end()) {
3868 // Create LevelDBTransaction for the name generator seed and add-journal.
3869 scoped_refptr<LevelDBTransaction> pre_transaction =
3870 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
3871 backing_store_->db_.get());
3872 BlobJournalType journal;
3873 for (; iter != blob_change_map_.end(); ++iter) {
3874 std::vector<IndexedDBBlobInfo>::iterator info_iter;
3875 std::vector<IndexedDBBlobInfo*> new_blob_keys;
3876 for (info_iter = iter->second->mutable_blob_info().begin();
3877 info_iter != iter->second->mutable_blob_info().end();
3878 ++info_iter) {
3879 int64 next_blob_key = -1;
3880 bool result = GetBlobKeyGeneratorCurrentNumber(
3881 pre_transaction.get(), database_id_, &next_blob_key);
3882 if (!result || next_blob_key < 0)
3883 return InternalInconsistencyStatus();
3884 BlobJournalEntryType journal_entry =
3885 std::make_pair(database_id_, next_blob_key);
3886 journal.push_back(journal_entry);
3887 if (info_iter->is_file()) {
3888 new_files_to_write->push_back(
3889 WriteDescriptor(info_iter->file_path(),
3890 next_blob_key,
3891 info_iter->size(),
3892 info_iter->last_modified()));
3893 } else {
3894 new_files_to_write->push_back(
3895 WriteDescriptor(getURLFromUUID(info_iter->uuid()),
3896 next_blob_key,
3897 info_iter->size()));
3898 }
3899 info_iter->set_key(next_blob_key);
3900 new_blob_keys.push_back(&*info_iter);
3901 result = UpdateBlobKeyGeneratorCurrentNumber(
3902 pre_transaction.get(), database_id_, next_blob_key + 1);
3903 if (!result)
3904 return InternalInconsistencyStatus();
3905 }
3906 BlobEntryKey blob_entry_key;
3907 StringPiece key_piece(iter->second->key());
3908 if (!BlobEntryKey::FromObjectStoreDataKey(&key_piece, &blob_entry_key)) {
3909 NOTREACHED();
3910 return InternalInconsistencyStatus();
3911 }
3912 new_blob_entries->push_back(
3913 std::make_pair(blob_entry_key, EncodeBlobData(new_blob_keys)));
3914 }
3915 UpdatePrimaryJournalWithBlobList(pre_transaction.get(), journal);
3916 leveldb::Status s = pre_transaction->Commit();
3917 if (!s.ok())
3918 return InternalInconsistencyStatus();
3919 }
3920 return leveldb::Status::OK();
3921 }
3922
CollectBlobFilesToRemove()3923 bool IndexedDBBackingStore::Transaction::CollectBlobFilesToRemove() {
3924 if (backing_store_->is_incognito())
3925 return true;
3926
3927 BlobChangeMap::const_iterator iter = blob_change_map_.begin();
3928 // Look up all old files to remove as part of the transaction, store their
3929 // names in blobs_to_remove_, and remove their old blob data entries.
3930 if (iter != blob_change_map_.end()) {
3931 scoped_ptr<LevelDBIterator> db_iter = transaction_->CreateIterator();
3932 for (; iter != blob_change_map_.end(); ++iter) {
3933 BlobEntryKey blob_entry_key;
3934 StringPiece key_piece(iter->second->key());
3935 if (!BlobEntryKey::FromObjectStoreDataKey(&key_piece, &blob_entry_key)) {
3936 NOTREACHED();
3937 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
3938 transaction_ = NULL;
3939 return false;
3940 }
3941 if (database_id_ < 0)
3942 database_id_ = blob_entry_key.database_id();
3943 else
3944 DCHECK_EQ(database_id_, blob_entry_key.database_id());
3945 std::string blob_entry_key_bytes = blob_entry_key.Encode();
3946 db_iter->Seek(blob_entry_key_bytes);
3947 if (db_iter->IsValid() &&
3948 !CompareKeys(db_iter->Key(), blob_entry_key_bytes)) {
3949 std::vector<IndexedDBBlobInfo> blob_info;
3950 if (!DecodeBlobData(db_iter->Value().as_string(), &blob_info)) {
3951 INTERNAL_READ_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
3952 transaction_ = NULL;
3953 return false;
3954 }
3955 std::vector<IndexedDBBlobInfo>::iterator blob_info_iter;
3956 for (blob_info_iter = blob_info.begin();
3957 blob_info_iter != blob_info.end();
3958 ++blob_info_iter)
3959 blobs_to_remove_.push_back(
3960 std::make_pair(database_id_, blob_info_iter->key()));
3961 transaction_->Remove(blob_entry_key_bytes);
3962 }
3963 }
3964 }
3965 return true;
3966 }
3967
SortBlobsToRemove()3968 leveldb::Status IndexedDBBackingStore::Transaction::SortBlobsToRemove() {
3969 IndexedDBActiveBlobRegistry* registry =
3970 backing_store_->active_blob_registry();
3971 BlobJournalType::iterator iter;
3972 BlobJournalType primary_journal, live_blob_journal;
3973 for (iter = blobs_to_remove_.begin(); iter != blobs_to_remove_.end();
3974 ++iter) {
3975 if (registry->MarkDeletedCheckIfUsed(iter->first, iter->second))
3976 live_blob_journal.push_back(*iter);
3977 else
3978 primary_journal.push_back(*iter);
3979 }
3980 UpdatePrimaryJournalWithBlobList(transaction_.get(), primary_journal);
3981 leveldb::Status s =
3982 MergeBlobsIntoLiveBlobJournal(transaction_.get(), live_blob_journal);
3983 if (!s.ok())
3984 return s;
3985 // To signal how many blobs need attention right now.
3986 blobs_to_remove_.swap(primary_journal);
3987 return leveldb::Status::OK();
3988 }
3989
CommitPhaseOne(scoped_refptr<BlobWriteCallback> callback)3990 leveldb::Status IndexedDBBackingStore::Transaction::CommitPhaseOne(
3991 scoped_refptr<BlobWriteCallback> callback) {
3992 IDB_TRACE("IndexedDBBackingStore::Transaction::CommitPhaseOne");
3993 DCHECK(transaction_);
3994 DCHECK(backing_store_->task_runner()->RunsTasksOnCurrentThread());
3995
3996 leveldb::Status s;
3997
3998 s = backing_store_->CleanUpBlobJournal(BlobJournalKey::Encode());
3999 if (!s.ok()) {
4000 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
4001 transaction_ = NULL;
4002 return s;
4003 }
4004
4005 BlobEntryKeyValuePairVec new_blob_entries;
4006 WriteDescriptorVec new_files_to_write;
4007 s = HandleBlobPreTransaction(&new_blob_entries, &new_files_to_write);
4008 if (!s.ok()) {
4009 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
4010 transaction_ = NULL;
4011 return s;
4012 }
4013
4014 DCHECK(!new_files_to_write.size() ||
4015 KeyPrefix::IsValidDatabaseId(database_id_));
4016 if (!CollectBlobFilesToRemove()) {
4017 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
4018 transaction_ = NULL;
4019 return InternalInconsistencyStatus();
4020 }
4021
4022 if (new_files_to_write.size()) {
4023 // This kicks off the writes of the new blobs, if any.
4024 // This call will zero out new_blob_entries and new_files_to_write.
4025 WriteNewBlobs(new_blob_entries, new_files_to_write, callback);
4026 // Remove the add journal, if any; once the blobs are written, and we
4027 // commit, this will do the cleanup.
4028 ClearBlobJournal(transaction_.get(), BlobJournalKey::Encode());
4029 } else {
4030 callback->Run(true);
4031 }
4032
4033 return leveldb::Status::OK();
4034 }
4035
CommitPhaseTwo()4036 leveldb::Status IndexedDBBackingStore::Transaction::CommitPhaseTwo() {
4037 IDB_TRACE("IndexedDBBackingStore::Transaction::CommitPhaseTwo");
4038 leveldb::Status s;
4039 if (blobs_to_remove_.size()) {
4040 s = SortBlobsToRemove();
4041 if (!s.ok()) {
4042 INTERNAL_READ_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
4043 transaction_ = NULL;
4044 return s;
4045 }
4046 }
4047
4048 s = transaction_->Commit();
4049 transaction_ = NULL;
4050
4051 if (s.ok() && backing_store_->is_incognito() && !blob_change_map_.empty()) {
4052 BlobChangeMap& target_map = backing_store_->incognito_blob_map_;
4053 BlobChangeMap::iterator iter;
4054 for (iter = blob_change_map_.begin(); iter != blob_change_map_.end();
4055 ++iter) {
4056 BlobChangeMap::iterator target_record = target_map.find(iter->first);
4057 if (target_record != target_map.end()) {
4058 delete target_record->second;
4059 target_map.erase(target_record);
4060 }
4061 if (iter->second) {
4062 target_map[iter->first] = iter->second;
4063 iter->second = NULL;
4064 }
4065 }
4066 }
4067 if (!s.ok())
4068 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
4069 else if (blobs_to_remove_.size())
4070 s = backing_store_->CleanUpBlobJournal(BlobJournalKey::Encode());
4071
4072 return s;
4073 }
4074
4075
4076 class IndexedDBBackingStore::Transaction::BlobWriteCallbackWrapper
4077 : public IndexedDBBackingStore::BlobWriteCallback {
4078 public:
BlobWriteCallbackWrapper(IndexedDBBackingStore::Transaction * transaction,scoped_refptr<BlobWriteCallback> callback)4079 BlobWriteCallbackWrapper(IndexedDBBackingStore::Transaction* transaction,
4080 scoped_refptr<BlobWriteCallback> callback)
4081 : transaction_(transaction), callback_(callback) {}
Run(bool succeeded)4082 virtual void Run(bool succeeded) OVERRIDE {
4083 callback_->Run(succeeded);
4084 if (succeeded) // Else it's already been deleted during rollback.
4085 transaction_->chained_blob_writer_ = NULL;
4086 }
4087
4088 private:
~BlobWriteCallbackWrapper()4089 virtual ~BlobWriteCallbackWrapper() {}
4090 friend class base::RefCounted<IndexedDBBackingStore::BlobWriteCallback>;
4091
4092 IndexedDBBackingStore::Transaction* transaction_;
4093 scoped_refptr<BlobWriteCallback> callback_;
4094
4095 DISALLOW_COPY_AND_ASSIGN(BlobWriteCallbackWrapper);
4096 };
4097
WriteNewBlobs(BlobEntryKeyValuePairVec & new_blob_entries,WriteDescriptorVec & new_files_to_write,scoped_refptr<BlobWriteCallback> callback)4098 void IndexedDBBackingStore::Transaction::WriteNewBlobs(
4099 BlobEntryKeyValuePairVec& new_blob_entries,
4100 WriteDescriptorVec& new_files_to_write,
4101 scoped_refptr<BlobWriteCallback> callback) {
4102 DCHECK_GT(new_files_to_write.size(), 0UL);
4103 DCHECK_GT(database_id_, 0);
4104 BlobEntryKeyValuePairVec::iterator blob_entry_iter;
4105 for (blob_entry_iter = new_blob_entries.begin();
4106 blob_entry_iter != new_blob_entries.end();
4107 ++blob_entry_iter) {
4108 // Add the new blob-table entry for each blob to the main transaction, or
4109 // remove any entry that may exist if there's no new one.
4110 if (!blob_entry_iter->second.size())
4111 transaction_->Remove(blob_entry_iter->first.Encode());
4112 else
4113 transaction_->Put(blob_entry_iter->first.Encode(),
4114 &blob_entry_iter->second);
4115 }
4116 // Creating the writer will start it going asynchronously.
4117 chained_blob_writer_ =
4118 new ChainedBlobWriterImpl(database_id_,
4119 backing_store_,
4120 new_files_to_write,
4121 new BlobWriteCallbackWrapper(this, callback));
4122 }
4123
Rollback()4124 void IndexedDBBackingStore::Transaction::Rollback() {
4125 IDB_TRACE("IndexedDBBackingStore::Transaction::Rollback");
4126 DCHECK(transaction_.get());
4127 if (chained_blob_writer_) {
4128 chained_blob_writer_->Abort();
4129 chained_blob_writer_ = NULL;
4130 }
4131 transaction_->Rollback();
4132 transaction_ = NULL;
4133 }
4134
BlobChangeRecord(const std::string & key,int64 object_store_id)4135 IndexedDBBackingStore::BlobChangeRecord::BlobChangeRecord(
4136 const std::string& key,
4137 int64 object_store_id)
4138 : key_(key), object_store_id_(object_store_id) {
4139 }
4140
~BlobChangeRecord()4141 IndexedDBBackingStore::BlobChangeRecord::~BlobChangeRecord() {
4142 }
4143
SetBlobInfo(std::vector<IndexedDBBlobInfo> * blob_info)4144 void IndexedDBBackingStore::BlobChangeRecord::SetBlobInfo(
4145 std::vector<IndexedDBBlobInfo>* blob_info) {
4146 blob_info_.clear();
4147 if (blob_info)
4148 blob_info_.swap(*blob_info);
4149 }
4150
SetHandles(ScopedVector<webkit_blob::BlobDataHandle> * handles)4151 void IndexedDBBackingStore::BlobChangeRecord::SetHandles(
4152 ScopedVector<webkit_blob::BlobDataHandle>* handles) {
4153 handles_.clear();
4154 if (handles)
4155 handles_.swap(*handles);
4156 }
4157
4158 scoped_ptr<IndexedDBBackingStore::BlobChangeRecord>
Clone() const4159 IndexedDBBackingStore::BlobChangeRecord::Clone() const {
4160 scoped_ptr<IndexedDBBackingStore::BlobChangeRecord> record(
4161 new BlobChangeRecord(key_, object_store_id_));
4162 record->blob_info_ = blob_info_;
4163
4164 ScopedVector<webkit_blob::BlobDataHandle>::const_iterator iter;
4165 for (iter = handles_.begin(); iter != handles_.end(); ++iter)
4166 record->handles_.push_back(new webkit_blob::BlobDataHandle(**iter));
4167 return record.Pass();
4168 }
4169
PutBlobInfoIfNeeded(int64 database_id,int64 object_store_id,const std::string & object_store_data_key,std::vector<IndexedDBBlobInfo> * blob_info,ScopedVector<webkit_blob::BlobDataHandle> * handles)4170 leveldb::Status IndexedDBBackingStore::Transaction::PutBlobInfoIfNeeded(
4171 int64 database_id,
4172 int64 object_store_id,
4173 const std::string& object_store_data_key,
4174 std::vector<IndexedDBBlobInfo>* blob_info,
4175 ScopedVector<webkit_blob::BlobDataHandle>* handles) {
4176 if (!blob_info || blob_info->empty()) {
4177 blob_change_map_.erase(object_store_data_key);
4178 incognito_blob_map_.erase(object_store_data_key);
4179
4180 BlobEntryKey blob_entry_key;
4181 StringPiece leveldb_key_piece(object_store_data_key);
4182 if (!BlobEntryKey::FromObjectStoreDataKey(&leveldb_key_piece,
4183 &blob_entry_key)) {
4184 NOTREACHED();
4185 return InternalInconsistencyStatus();
4186 }
4187 std::string value;
4188 bool found = false;
4189 leveldb::Status s =
4190 transaction()->Get(blob_entry_key.Encode(), &value, &found);
4191 if (!s.ok())
4192 return s;
4193 if (!found)
4194 return leveldb::Status::OK();
4195 }
4196 PutBlobInfo(
4197 database_id, object_store_id, object_store_data_key, blob_info, handles);
4198 return leveldb::Status::OK();
4199 }
4200
4201 // This is storing an info, even if empty, even if the previous key had no blob
4202 // info that we know of. It duplicates a bunch of information stored in the
4203 // leveldb transaction, but only w.r.t. the user keys altered--we don't keep the
4204 // changes to exists or index keys here.
PutBlobInfo(int64 database_id,int64 object_store_id,const std::string & object_store_data_key,std::vector<IndexedDBBlobInfo> * blob_info,ScopedVector<webkit_blob::BlobDataHandle> * handles)4205 void IndexedDBBackingStore::Transaction::PutBlobInfo(
4206 int64 database_id,
4207 int64 object_store_id,
4208 const std::string& object_store_data_key,
4209 std::vector<IndexedDBBlobInfo>* blob_info,
4210 ScopedVector<webkit_blob::BlobDataHandle>* handles) {
4211 DCHECK_GT(object_store_data_key.size(), 0UL);
4212 if (database_id_ < 0)
4213 database_id_ = database_id;
4214 DCHECK_EQ(database_id_, database_id);
4215
4216 BlobChangeMap::iterator it = blob_change_map_.find(object_store_data_key);
4217 BlobChangeRecord* record = NULL;
4218 if (it == blob_change_map_.end()) {
4219 record = new BlobChangeRecord(object_store_data_key, object_store_id);
4220 blob_change_map_[object_store_data_key] = record;
4221 } else {
4222 record = it->second;
4223 }
4224 DCHECK_EQ(record->object_store_id(), object_store_id);
4225 record->SetBlobInfo(blob_info);
4226 record->SetHandles(handles);
4227 DCHECK(!handles || !handles->size());
4228 }
4229
WriteDescriptor(const GURL & url,int64_t key,int64_t size)4230 IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
4231 const GURL& url,
4232 int64_t key,
4233 int64_t size)
4234 : is_file_(false), url_(url), key_(key), size_(size) {
4235 }
4236
WriteDescriptor(const FilePath & file_path,int64_t key,int64_t size,base::Time last_modified)4237 IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
4238 const FilePath& file_path,
4239 int64_t key,
4240 int64_t size,
4241 base::Time last_modified)
4242 : is_file_(true),
4243 file_path_(file_path),
4244 key_(key),
4245 size_(size),
4246 last_modified_(last_modified) {
4247 }
4248
4249 } // namespace content
4250