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 51 FilePath GetBlobDirectoryName(const FilePath& pathBase, int64 database_id) { 52 return pathBase.AppendASCII(base::StringPrintf("%" PRIx64, database_id)); 53 } 54 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 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 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 79 static std::string ComputeOriginIdentifier(const GURL& origin_url) { 80 return webkit_database::GetIdentifierFromOrigin(origin_url) + "@1"; 81 } 82 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 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 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 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 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. 192 static leveldb::Status InternalInconsistencyStatus() { 193 return leveldb::Status::Corruption("Internal inconsistency"); 194 } 195 196 static leveldb::Status InvalidDBKeyStatus() { 197 return leveldb::Status::InvalidArgument("Invalid database key ID"); 198 } 199 200 static leveldb::Status IOErrorStatus() { 201 return leveldb::Status::IOError("IO Error"); 202 } 203 204 template <typename DBOrTransaction> 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 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> 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 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> 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 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 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 290 static int CompareKeys(const StringPiece& a, const StringPiece& b) { 291 return Compare(a, b, false /*index_keys*/); 292 } 293 294 static int CompareIndexKeys(const StringPiece& a, const StringPiece& b) { 295 return Compare(a, b, true /*index_keys*/); 296 } 297 298 int IndexedDBBackingStore::Comparator::Compare(const StringPiece& a, 299 const StringPiece& b) const { 300 return content::Compare(a, b, false /*index_keys*/); 301 } 302 303 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; 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. 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> 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> 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: 489 DefaultLevelDBFactory() {} 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 } 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 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 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> 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 581 static void ClearBlobJournal(LevelDBTransaction* leveldb_transaction, 582 const std::string& level_db_key) { 583 leveldb_transaction->Remove(level_db_key); 584 } 585 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 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 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 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 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. 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 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 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 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 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 } 748 IndexedDBBackingStore::RecordIdentifier::RecordIdentifier() 749 : primary_key_(), version_(-1) {} 750 IndexedDBBackingStore::RecordIdentifier::~RecordIdentifier() {} 751 752 IndexedDBBackingStore::Cursor::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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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. 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 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 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 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 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. 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 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 1668 void IndexedDBBackingStore::Compact() { db_->CompactAll(); } 1669 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 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 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 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 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 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 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 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 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 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 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; 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 2177 virtual void set_delegate(scoped_ptr<FileWriterDelegate> delegate) OVERRIDE { 2178 delegate_.reset(delegate.release()); 2179 } 2180 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 2203 virtual void Abort() OVERRIDE { 2204 if (!waiting_for_callback_) 2205 return; 2206 self_ref_ = this; 2207 aborted_ = true; 2208 } 2209 2210 private: 2211 virtual ~ChainedBlobWriterImpl() {} 2212 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: 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 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 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: 2294 virtual ~LocalWriteClosure() {} 2295 friend class base::RefCounted<LocalWriteClosure>; 2296 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 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 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. 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. 2444 FilePath IndexedDBBackingStore::GetBlobFileName(int64 database_id, int64 key) { 2445 return GetBlobFileNameForKey(blob_path_, database_id, key); 2446 } 2447 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. 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 2564 bool IndexedDBBackingStore::RemoveBlobFile(int64 database_id, int64 key) { 2565 FilePath fileName = GetBlobFileName(database_id, key); 2566 return base::DeleteFile(fileName, false); 2567 } 2568 2569 bool IndexedDBBackingStore::RemoveBlobDirectory(int64 database_id) { 2570 FilePath dirName = GetBlobDirectoryName(blob_path_, database_id); 2571 return base::DeleteFile(dirName, true); 2572 } 2573 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 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 2668 void IndexedDBBackingStore::CleanPrimaryJournalIgnoreReturn() { 2669 CleanUpBlobJournal(BlobJournalKey::Encode()); 2670 } 2671 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 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 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 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 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 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 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 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 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 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 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 } 3018 IndexedDBBackingStore::Cursor::~Cursor() {} 3019 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 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 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 3162 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 3177 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 3192 const IndexedDBKey& IndexedDBBackingStore::Cursor::primary_key() const { 3193 return *current_key_; 3194 } 3195 3196 const IndexedDBBackingStore::RecordIdentifier& 3197 IndexedDBBackingStore::Cursor::record_identifier() const { 3198 return record_identifier_; 3199 } 3200 3201 class ObjectStoreKeyCursorImpl : public IndexedDBBackingStore::Cursor { 3202 public: 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 3213 virtual Cursor* Clone() OVERRIDE { 3214 return new ObjectStoreKeyCursorImpl(this); 3215 } 3216 3217 // IndexedDBBackingStore::Cursor 3218 virtual IndexedDBValue* value() OVERRIDE { 3219 NOTREACHED(); 3220 return NULL; 3221 } 3222 virtual bool LoadCurrentRow() OVERRIDE; 3223 3224 protected: 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 } 3229 virtual std::string EncodeKey(const IndexedDBKey& key, 3230 const IndexedDBKey& primary_key) OVERRIDE { 3231 NOTREACHED(); 3232 return std::string(); 3233 } 3234 3235 private: 3236 explicit ObjectStoreKeyCursorImpl(const ObjectStoreKeyCursorImpl* other) 3237 : IndexedDBBackingStore::Cursor(other) {} 3238 3239 DISALLOW_COPY_AND_ASSIGN(ObjectStoreKeyCursorImpl); 3240 }; 3241 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: 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 3279 virtual Cursor* Clone() OVERRIDE { return new ObjectStoreCursorImpl(this); } 3280 3281 // IndexedDBBackingStore::Cursor 3282 virtual IndexedDBValue* value() OVERRIDE { return ¤t_value_; } 3283 virtual bool LoadCurrentRow() OVERRIDE; 3284 3285 protected: 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 } 3290 virtual std::string EncodeKey(const IndexedDBKey& key, 3291 const IndexedDBKey& primary_key) OVERRIDE { 3292 NOTREACHED(); 3293 return std::string(); 3294 } 3295 3296 private: 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 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: 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 3349 virtual Cursor* Clone() OVERRIDE { return new IndexKeyCursorImpl(this); } 3350 3351 // IndexedDBBackingStore::Cursor 3352 virtual IndexedDBValue* value() OVERRIDE { 3353 NOTREACHED(); 3354 return NULL; 3355 } 3356 virtual const IndexedDBKey& primary_key() const OVERRIDE { 3357 return *primary_key_; 3358 } 3359 virtual const IndexedDBBackingStore::RecordIdentifier& record_identifier() 3360 const OVERRIDE { 3361 NOTREACHED(); 3362 return record_identifier_; 3363 } 3364 virtual bool LoadCurrentRow() OVERRIDE; 3365 3366 protected: 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 } 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: 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 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: 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 3464 virtual Cursor* Clone() OVERRIDE { return new IndexCursorImpl(this); } 3465 3466 // IndexedDBBackingStore::Cursor 3467 virtual IndexedDBValue* value() OVERRIDE { return ¤t_value_; } 3468 virtual const IndexedDBKey& primary_key() const OVERRIDE { 3469 return *primary_key_; 3470 } 3471 virtual const IndexedDBBackingStore::RecordIdentifier& record_identifier() 3472 const OVERRIDE { 3473 NOTREACHED(); 3474 return record_identifier_; 3475 } 3476 virtual bool LoadCurrentRow() OVERRIDE; 3477 3478 protected: 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 } 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: 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 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 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 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> 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> 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> 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> 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 3827 IndexedDBBackingStore::Transaction::Transaction( 3828 IndexedDBBackingStore* backing_store) 3829 : backing_store_(backing_store), database_id_(-1) { 3830 } 3831 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 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 3854 static GURL getURLFromUUID(const string& uuid) { 3855 return GURL("blob:uuid/" + uuid); 3856 } 3857 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 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 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 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 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: 4079 BlobWriteCallbackWrapper(IndexedDBBackingStore::Transaction* transaction, 4080 scoped_refptr<BlobWriteCallback> callback) 4081 : transaction_(transaction), callback_(callback) {} 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: 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 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 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 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 4141 IndexedDBBackingStore::BlobChangeRecord::~BlobChangeRecord() { 4142 } 4143 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 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> 4159 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 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. 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 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 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