1 // Copyright 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 "chrome/browser/chromeos/drive/resource_metadata_storage.h" 6 7 #include "base/bind.h" 8 #include "base/file_util.h" 9 #include "base/location.h" 10 #include "base/logging.h" 11 #include "base/metrics/histogram.h" 12 #include "base/metrics/sparse_histogram.h" 13 #include "base/sequenced_task_runner.h" 14 #include "base/threading/thread_restrictions.h" 15 #include "chrome/browser/chromeos/drive/drive.pb.h" 16 #include "third_party/leveldatabase/src/include/leveldb/db.h" 17 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h" 18 19 namespace drive { 20 namespace internal { 21 22 namespace { 23 24 // Enum to describe DB initialization status. 25 enum DBInitStatus { 26 DB_INIT_SUCCESS, 27 DB_INIT_NOT_FOUND, 28 DB_INIT_CORRUPTION, 29 DB_INIT_IO_ERROR, 30 DB_INIT_FAILED, 31 DB_INIT_INCOMPATIBLE, 32 DB_INIT_BROKEN, 33 DB_INIT_OPENED_EXISTING_DB, 34 DB_INIT_CREATED_NEW_DB, 35 DB_INIT_REPLACED_EXISTING_DB_WITH_NEW_DB, 36 DB_INIT_MAX_VALUE, 37 }; 38 39 // The name of the DB which stores the metadata. 40 const base::FilePath::CharType kResourceMapDBName[] = 41 FILE_PATH_LITERAL("resource_metadata_resource_map.db"); 42 43 // The name of the DB which couldn't be opened, but is preserved just in case. 44 const base::FilePath::CharType kPreservedResourceMapDBName[] = 45 FILE_PATH_LITERAL("resource_metadata_preserved_resource_map.db"); 46 47 // The name of the DB which couldn't be opened, and was replaced with a new one. 48 const base::FilePath::CharType kTrashedResourceMapDBName[] = 49 FILE_PATH_LITERAL("resource_metadata_trashed_resource_map.db"); 50 51 // Meant to be a character which never happen to be in real IDs. 52 const char kDBKeyDelimeter = '\0'; 53 54 // String used as a suffix of a key for a cache entry. 55 const char kCacheEntryKeySuffix[] = "CACHE"; 56 57 // String used as a prefix of a key for a resource-ID-to-local-ID entry. 58 const char kIdEntryKeyPrefix[] = "ID"; 59 60 // Returns a string to be used as the key for the header. 61 std::string GetHeaderDBKey() { 62 std::string key; 63 key.push_back(kDBKeyDelimeter); 64 key.append("HEADER"); 65 return key; 66 } 67 68 // Returns true if |key| is a key for a child entry. 69 bool IsChildEntryKey(const leveldb::Slice& key) { 70 return !key.empty() && key[key.size() - 1] == kDBKeyDelimeter; 71 } 72 73 // Returns a string to be used as a key for a cache entry. 74 std::string GetCacheEntryKey(const std::string& id) { 75 std::string key(id); 76 key.push_back(kDBKeyDelimeter); 77 key.append(kCacheEntryKeySuffix); 78 return key; 79 } 80 81 // Returns true if |key| is a key for a cache entry. 82 bool IsCacheEntryKey(const leveldb::Slice& key) { 83 // A cache entry key should end with |kDBKeyDelimeter + kCacheEntryKeySuffix|. 84 const leveldb::Slice expected_suffix(kCacheEntryKeySuffix, 85 arraysize(kCacheEntryKeySuffix) - 1); 86 if (key.size() < 1 + expected_suffix.size() || 87 key[key.size() - expected_suffix.size() - 1] != kDBKeyDelimeter) 88 return false; 89 90 const leveldb::Slice key_substring( 91 key.data() + key.size() - expected_suffix.size(), expected_suffix.size()); 92 return key_substring.compare(expected_suffix) == 0; 93 } 94 95 // Returns ID extracted from a cache entry key. 96 std::string GetIdFromCacheEntryKey(const leveldb::Slice& key) { 97 DCHECK(IsCacheEntryKey(key)); 98 // Drop the suffix |kDBKeyDelimeter + kCacheEntryKeySuffix| from the key. 99 const size_t kSuffixLength = arraysize(kCacheEntryKeySuffix) - 1; 100 const int id_length = key.size() - 1 - kSuffixLength; 101 return std::string(key.data(), id_length); 102 } 103 104 // Returns a string to be used as a key for a resource-ID-to-local-ID entry. 105 std::string GetIdEntryKey(const std::string& resource_id) { 106 std::string key; 107 key.push_back(kDBKeyDelimeter); 108 key.append(kIdEntryKeyPrefix); 109 key.push_back(kDBKeyDelimeter); 110 key.append(resource_id); 111 return key; 112 } 113 114 // Returns true if |key| is a key for a resource-ID-to-local-ID entry. 115 bool IsIdEntryKey(const leveldb::Slice& key) { 116 // A resource-ID-to-local-ID entry key should start with 117 // |kDBKeyDelimeter + kIdEntryKeyPrefix + kDBKeyDelimeter|. 118 const leveldb::Slice expected_prefix(kIdEntryKeyPrefix, 119 arraysize(kIdEntryKeyPrefix) - 1); 120 if (key.size() < 2 + expected_prefix.size()) 121 return false; 122 const leveldb::Slice key_substring(key.data() + 1, expected_prefix.size()); 123 return key[0] == kDBKeyDelimeter && 124 key_substring.compare(expected_prefix) == 0 && 125 key[expected_prefix.size() + 1] == kDBKeyDelimeter; 126 } 127 128 // Converts leveldb::Status to DBInitStatus. 129 DBInitStatus LevelDBStatusToDBInitStatus(const leveldb::Status status) { 130 if (status.ok()) 131 return DB_INIT_SUCCESS; 132 if (status.IsNotFound()) 133 return DB_INIT_NOT_FOUND; 134 if (status.IsCorruption()) 135 return DB_INIT_CORRUPTION; 136 if (status.IsIOError()) 137 return DB_INIT_IO_ERROR; 138 return DB_INIT_FAILED; 139 } 140 141 ResourceMetadataHeader GetDefaultHeaderEntry() { 142 ResourceMetadataHeader header; 143 header.set_version(ResourceMetadataStorage::kDBVersion); 144 return header; 145 } 146 147 bool MoveIfPossible(const base::FilePath& from, const base::FilePath& to) { 148 return !base::PathExists(from) || base::Move(from, to); 149 } 150 151 } // namespace 152 153 ResourceMetadataStorage::Iterator::Iterator(scoped_ptr<leveldb::Iterator> it) 154 : it_(it.Pass()) { 155 base::ThreadRestrictions::AssertIOAllowed(); 156 DCHECK(it_); 157 158 // Skip the header entry. 159 // Note: The header entry comes before all other entries because its key 160 // starts with kDBKeyDelimeter. (i.e. '\0') 161 it_->Seek(leveldb::Slice(GetHeaderDBKey())); 162 163 Advance(); 164 } 165 166 ResourceMetadataStorage::Iterator::~Iterator() { 167 base::ThreadRestrictions::AssertIOAllowed(); 168 } 169 170 bool ResourceMetadataStorage::Iterator::IsAtEnd() const { 171 base::ThreadRestrictions::AssertIOAllowed(); 172 return !it_->Valid(); 173 } 174 175 std::string ResourceMetadataStorage::Iterator::GetID() const { 176 return it_->key().ToString(); 177 } 178 179 const ResourceEntry& ResourceMetadataStorage::Iterator::GetValue() const { 180 base::ThreadRestrictions::AssertIOAllowed(); 181 DCHECK(!IsAtEnd()); 182 return entry_; 183 } 184 185 bool ResourceMetadataStorage::Iterator::GetCacheEntry( 186 FileCacheEntry* cache_entry) { 187 base::ThreadRestrictions::AssertIOAllowed(); 188 DCHECK(!IsAtEnd()); 189 190 // Try to seek to the cache entry. 191 std::string current_key = it_->key().ToString(); 192 std::string cache_entry_key = GetCacheEntryKey(current_key); 193 it_->Seek(leveldb::Slice(cache_entry_key)); 194 195 bool success = it_->Valid() && 196 it_->key().compare(cache_entry_key) == 0 && 197 cache_entry->ParseFromArray(it_->value().data(), it_->value().size()); 198 199 // Seek back to the original position. 200 it_->Seek(leveldb::Slice(current_key)); 201 DCHECK(!IsAtEnd()); 202 DCHECK_EQ(current_key, it_->key().ToString()); 203 204 return success; 205 } 206 207 void ResourceMetadataStorage::Iterator::Advance() { 208 base::ThreadRestrictions::AssertIOAllowed(); 209 DCHECK(!IsAtEnd()); 210 211 for (it_->Next() ; it_->Valid(); it_->Next()) { 212 if (!IsChildEntryKey(it_->key()) && 213 !IsCacheEntryKey(it_->key()) && 214 !IsIdEntryKey(it_->key()) && 215 entry_.ParseFromArray(it_->value().data(), it_->value().size())) 216 break; 217 } 218 } 219 220 bool ResourceMetadataStorage::Iterator::HasError() const { 221 base::ThreadRestrictions::AssertIOAllowed(); 222 return !it_->status().ok(); 223 } 224 225 ResourceMetadataStorage::CacheEntryIterator::CacheEntryIterator( 226 scoped_ptr<leveldb::Iterator> it) : it_(it.Pass()) { 227 base::ThreadRestrictions::AssertIOAllowed(); 228 DCHECK(it_); 229 230 it_->SeekToFirst(); 231 AdvanceInternal(); 232 } 233 234 ResourceMetadataStorage::CacheEntryIterator::~CacheEntryIterator() { 235 base::ThreadRestrictions::AssertIOAllowed(); 236 } 237 238 bool ResourceMetadataStorage::CacheEntryIterator::IsAtEnd() const { 239 base::ThreadRestrictions::AssertIOAllowed(); 240 return !it_->Valid(); 241 } 242 243 const std::string& ResourceMetadataStorage::CacheEntryIterator::GetID() const { 244 base::ThreadRestrictions::AssertIOAllowed(); 245 DCHECK(!IsAtEnd()); 246 return id_; 247 } 248 249 const FileCacheEntry& 250 ResourceMetadataStorage::CacheEntryIterator::GetValue() const { 251 base::ThreadRestrictions::AssertIOAllowed(); 252 DCHECK(!IsAtEnd()); 253 return entry_; 254 } 255 256 void ResourceMetadataStorage::CacheEntryIterator::Advance() { 257 base::ThreadRestrictions::AssertIOAllowed(); 258 DCHECK(!IsAtEnd()); 259 260 it_->Next(); 261 AdvanceInternal(); 262 } 263 264 bool ResourceMetadataStorage::CacheEntryIterator::HasError() const { 265 base::ThreadRestrictions::AssertIOAllowed(); 266 return !it_->status().ok(); 267 } 268 269 void ResourceMetadataStorage::CacheEntryIterator::AdvanceInternal() { 270 for (; it_->Valid(); it_->Next()) { 271 // Skip unparsable broken entries. 272 // TODO(hashimoto): Broken entries should be cleaned up at some point. 273 if (IsCacheEntryKey(it_->key()) && 274 entry_.ParseFromArray(it_->value().data(), it_->value().size())) { 275 id_ = GetIdFromCacheEntryKey(it_->key()); 276 break; 277 } 278 } 279 } 280 281 // static 282 bool ResourceMetadataStorage::UpgradeOldDB( 283 const base::FilePath& directory_path, 284 const ResourceIdCanonicalizer& id_canonicalizer) { 285 base::ThreadRestrictions::AssertIOAllowed(); 286 COMPILE_ASSERT( 287 kDBVersion == 12, 288 db_version_and_this_function_should_be_updated_at_the_same_time); 289 290 const base::FilePath resource_map_path = 291 directory_path.Append(kResourceMapDBName); 292 const base::FilePath preserved_resource_map_path = 293 directory_path.Append(kPreservedResourceMapDBName); 294 295 if (base::PathExists(preserved_resource_map_path)) { 296 // Preserved DB is found. The previous attempt to create a new DB should not 297 // be successful. Discard the imperfect new DB and restore the old DB. 298 if (!base::DeleteFile(resource_map_path, false /* recursive */) || 299 !base::Move(preserved_resource_map_path, resource_map_path)) 300 return false; 301 } 302 303 if (!base::PathExists(resource_map_path)) 304 return false; 305 306 // Open DB. 307 leveldb::DB* db = NULL; 308 leveldb::Options options; 309 options.max_open_files = 0; // Use minimum. 310 options.create_if_missing = false; 311 if (!leveldb::DB::Open(options, resource_map_path.AsUTF8Unsafe(), &db).ok()) 312 return false; 313 scoped_ptr<leveldb::DB> resource_map(db); 314 315 // Check DB version. 316 std::string serialized_header; 317 ResourceMetadataHeader header; 318 if (!resource_map->Get(leveldb::ReadOptions(), 319 leveldb::Slice(GetHeaderDBKey()), 320 &serialized_header).ok() || 321 !header.ParseFromString(serialized_header)) 322 return false; 323 UMA_HISTOGRAM_SPARSE_SLOWLY("Drive.MetadataDBVersionBeforeUpgradeCheck", 324 header.version()); 325 326 if (header.version() == kDBVersion) { // Nothing to do. 327 return true; 328 } else if (header.version() < 6) { // Too old, nothing can be done. 329 return false; 330 } else if (header.version() < 11) { // Cache entries can be reused. 331 leveldb::ReadOptions options; 332 options.verify_checksums = true; 333 scoped_ptr<leveldb::Iterator> it(resource_map->NewIterator(options)); 334 335 leveldb::WriteBatch batch; 336 for (it->SeekToFirst(); it->Valid(); it->Next()) { 337 if (IsCacheEntryKey(it->key())) { 338 // The resource ID might be in old WAPI format. We need to canonicalize 339 // to the format of API service currently in use. 340 const std::string& id = GetIdFromCacheEntryKey(it->key()); 341 const std::string& id_new = id_canonicalizer.Run(id); 342 if (id != id_new) { 343 batch.Delete(it->key()); 344 batch.Put(GetCacheEntryKey(id_new), it->value()); 345 } 346 // Before v11, resource ID was directly used as local ID. Such entries 347 // can be migrated by adding an identity ID mapping. 348 batch.Put(GetIdEntryKey(id_new), id_new); 349 } else { // Remove all entries except cache entries. 350 batch.Delete(it->key()); 351 } 352 } 353 if (!it->status().ok()) 354 return false; 355 356 // Put header with the latest version number. 357 std::string serialized_header; 358 if (!GetDefaultHeaderEntry().SerializeToString(&serialized_header)) 359 return false; 360 batch.Put(GetHeaderDBKey(), serialized_header); 361 362 return resource_map->Write(leveldb::WriteOptions(), &batch).ok(); 363 } else if (header.version() < 12) { // Cache and ID map entries are reusable. 364 leveldb::ReadOptions options; 365 options.verify_checksums = true; 366 scoped_ptr<leveldb::Iterator> it(resource_map->NewIterator(options)); 367 368 leveldb::WriteBatch batch; 369 for (it->SeekToFirst(); it->Valid(); it->Next()) { 370 if (!IsCacheEntryKey(it->key()) && !IsIdEntryKey(it->key())) 371 batch.Delete(it->key()); 372 } 373 if (!it->status().ok()) 374 return false; 375 376 // Put header with the latest version number. 377 std::string serialized_header; 378 if (!GetDefaultHeaderEntry().SerializeToString(&serialized_header)) 379 return false; 380 batch.Put(GetHeaderDBKey(), serialized_header); 381 382 return resource_map->Write(leveldb::WriteOptions(), &batch).ok(); 383 } 384 385 LOG(WARNING) << "Unexpected DB version: " << header.version(); 386 return false; 387 } 388 389 ResourceMetadataStorage::ResourceMetadataStorage( 390 const base::FilePath& directory_path, 391 base::SequencedTaskRunner* blocking_task_runner) 392 : directory_path_(directory_path), 393 cache_file_scan_is_needed_(true), 394 blocking_task_runner_(blocking_task_runner) { 395 } 396 397 void ResourceMetadataStorage::Destroy() { 398 blocking_task_runner_->PostTask( 399 FROM_HERE, 400 base::Bind(&ResourceMetadataStorage::DestroyOnBlockingPool, 401 base::Unretained(this))); 402 } 403 404 bool ResourceMetadataStorage::Initialize() { 405 base::ThreadRestrictions::AssertIOAllowed(); 406 407 resource_map_.reset(); 408 409 const base::FilePath resource_map_path = 410 directory_path_.Append(kResourceMapDBName); 411 const base::FilePath preserved_resource_map_path = 412 directory_path_.Append(kPreservedResourceMapDBName); 413 const base::FilePath trashed_resource_map_path = 414 directory_path_.Append(kTrashedResourceMapDBName); 415 416 // Discard unneeded DBs. 417 if (!base::DeleteFile(preserved_resource_map_path, true /* recursive */) || 418 !base::DeleteFile(trashed_resource_map_path, true /* recursive */)) { 419 LOG(ERROR) << "Failed to remove unneeded DBs."; 420 return false; 421 } 422 423 // Try to open the existing DB. 424 leveldb::DB* db = NULL; 425 leveldb::Options options; 426 options.max_open_files = 0; // Use minimum. 427 options.create_if_missing = false; 428 429 DBInitStatus open_existing_result = DB_INIT_NOT_FOUND; 430 leveldb::Status status; 431 if (base::PathExists(resource_map_path)) { 432 status = leveldb::DB::Open(options, resource_map_path.AsUTF8Unsafe(), &db); 433 open_existing_result = LevelDBStatusToDBInitStatus(status); 434 } 435 436 if (open_existing_result == DB_INIT_SUCCESS) { 437 resource_map_.reset(db); 438 439 // Check the validity of existing DB. 440 int db_version = -1; 441 ResourceMetadataHeader header; 442 if (GetHeader(&header)) 443 db_version = header.version(); 444 445 bool should_discard_db = true; 446 if (db_version != kDBVersion) { 447 open_existing_result = DB_INIT_INCOMPATIBLE; 448 DVLOG(1) << "Reject incompatible DB."; 449 } else if (!CheckValidity()) { 450 open_existing_result = DB_INIT_BROKEN; 451 LOG(ERROR) << "Reject invalid DB."; 452 } else { 453 should_discard_db = false; 454 } 455 456 if (should_discard_db) 457 resource_map_.reset(); 458 else 459 cache_file_scan_is_needed_ = false; 460 } 461 462 UMA_HISTOGRAM_ENUMERATION("Drive.MetadataDBOpenExistingResult", 463 open_existing_result, 464 DB_INIT_MAX_VALUE); 465 466 DBInitStatus init_result = DB_INIT_OPENED_EXISTING_DB; 467 468 // Failed to open the existing DB, create new DB. 469 if (!resource_map_) { 470 // Move the existing DB to the preservation path. The moved old DB is 471 // deleted once the new DB creation succeeds, or is restored later in 472 // UpgradeOldDB() when the creation fails. 473 MoveIfPossible(resource_map_path, preserved_resource_map_path); 474 475 // Create DB. 476 options.max_open_files = 0; // Use minimum. 477 options.create_if_missing = true; 478 options.error_if_exists = true; 479 480 status = leveldb::DB::Open(options, resource_map_path.AsUTF8Unsafe(), &db); 481 if (status.ok()) { 482 resource_map_.reset(db); 483 484 if (PutHeader(GetDefaultHeaderEntry()) && // Set up header. 485 MoveIfPossible(preserved_resource_map_path, // Trash the old DB. 486 trashed_resource_map_path)) { 487 init_result = open_existing_result == DB_INIT_NOT_FOUND ? 488 DB_INIT_CREATED_NEW_DB : DB_INIT_REPLACED_EXISTING_DB_WITH_NEW_DB; 489 } else { 490 init_result = DB_INIT_FAILED; 491 resource_map_.reset(); 492 } 493 } else { 494 LOG(ERROR) << "Failed to create resource map DB: " << status.ToString(); 495 init_result = LevelDBStatusToDBInitStatus(status); 496 } 497 } 498 499 UMA_HISTOGRAM_ENUMERATION("Drive.MetadataDBInitResult", 500 init_result, 501 DB_INIT_MAX_VALUE); 502 return resource_map_; 503 } 504 505 void ResourceMetadataStorage::RecoverCacheInfoFromTrashedResourceMap( 506 RecoveredCacheInfoMap* out_info) { 507 const base::FilePath trashed_resource_map_path = 508 directory_path_.Append(kTrashedResourceMapDBName); 509 510 if (!base::PathExists(trashed_resource_map_path)) 511 return; 512 513 leveldb::Options options; 514 options.max_open_files = 0; // Use minimum. 515 options.create_if_missing = false; 516 517 // Trashed DB may be broken, repair it first. 518 leveldb::Status status; 519 status = leveldb::RepairDB(trashed_resource_map_path.AsUTF8Unsafe(), options); 520 if (!status.ok()) { 521 LOG(ERROR) << "Failed to repair trashed DB: " << status.ToString(); 522 return; 523 } 524 525 // Open it. 526 leveldb::DB* db = NULL; 527 status = leveldb::DB::Open(options, trashed_resource_map_path.AsUTF8Unsafe(), 528 &db); 529 if (!status.ok()) { 530 LOG(ERROR) << "Failed to open trashed DB: " << status.ToString(); 531 return; 532 } 533 scoped_ptr<leveldb::DB> resource_map(db); 534 535 // Check DB version. 536 std::string serialized_header; 537 ResourceMetadataHeader header; 538 if (!resource_map->Get(leveldb::ReadOptions(), 539 leveldb::Slice(GetHeaderDBKey()), 540 &serialized_header).ok() || 541 !header.ParseFromString(serialized_header) || 542 header.version() != kDBVersion) { 543 LOG(ERROR) << "Incompatible DB version: " << header.version(); 544 return; 545 } 546 547 // Collect cache entries. 548 scoped_ptr<leveldb::Iterator> it( 549 resource_map->NewIterator(leveldb::ReadOptions())); 550 for (it->SeekToFirst(); it->Valid(); it->Next()) { 551 if (IsCacheEntryKey(it->key())) { 552 const std::string& id = GetIdFromCacheEntryKey(it->key()); 553 FileCacheEntry cache_entry; 554 if (cache_entry.ParseFromArray(it->value().data(), it->value().size())) { 555 RecoveredCacheInfo* info = &(*out_info)[id]; 556 info->is_dirty = cache_entry.is_dirty(); 557 info->md5 = cache_entry.md5(); 558 559 // Get title from ResourceEntry if available. 560 std::string serialized_entry; 561 ResourceEntry entry; 562 if (resource_map->Get(leveldb::ReadOptions(), 563 leveldb::Slice(id), 564 &serialized_entry).ok() && 565 entry.ParseFromString(serialized_entry)) 566 info->title = entry.title(); 567 } 568 } 569 } 570 } 571 572 bool ResourceMetadataStorage::SetLargestChangestamp( 573 int64 largest_changestamp) { 574 base::ThreadRestrictions::AssertIOAllowed(); 575 576 ResourceMetadataHeader header; 577 if (!GetHeader(&header)) { 578 DLOG(ERROR) << "Failed to get the header."; 579 return false; 580 } 581 header.set_largest_changestamp(largest_changestamp); 582 return PutHeader(header); 583 } 584 585 int64 ResourceMetadataStorage::GetLargestChangestamp() { 586 base::ThreadRestrictions::AssertIOAllowed(); 587 ResourceMetadataHeader header; 588 if (!GetHeader(&header)) { 589 DLOG(ERROR) << "Failed to get the header."; 590 return 0; 591 } 592 return header.largest_changestamp(); 593 } 594 595 bool ResourceMetadataStorage::PutEntry(const ResourceEntry& entry) { 596 base::ThreadRestrictions::AssertIOAllowed(); 597 598 const std::string& id = entry.local_id(); 599 DCHECK(!id.empty()); 600 601 // Try to get existing entry. 602 std::string serialized_entry; 603 leveldb::Status status = resource_map_->Get(leveldb::ReadOptions(), 604 leveldb::Slice(id), 605 &serialized_entry); 606 if (!status.ok() && !status.IsNotFound()) // Unexpected errors. 607 return false; 608 609 ResourceEntry old_entry; 610 if (status.ok() && !old_entry.ParseFromString(serialized_entry)) 611 return false; 612 613 // Construct write batch. 614 leveldb::WriteBatch batch; 615 616 // Remove from the old parent. 617 if (!old_entry.parent_local_id().empty()) { 618 batch.Delete(GetChildEntryKey(old_entry.parent_local_id(), 619 old_entry.base_name())); 620 } 621 // Add to the new parent. 622 if (!entry.parent_local_id().empty()) 623 batch.Put(GetChildEntryKey(entry.parent_local_id(), entry.base_name()), id); 624 625 // Refresh resource-ID-to-local-ID mapping entry. 626 if (old_entry.resource_id() != entry.resource_id()) { 627 // Resource ID should not change. 628 DCHECK(old_entry.resource_id().empty() || entry.resource_id().empty()); 629 630 if (!old_entry.resource_id().empty()) 631 batch.Delete(GetIdEntryKey(old_entry.resource_id())); 632 if (!entry.resource_id().empty()) 633 batch.Put(GetIdEntryKey(entry.resource_id()), id); 634 } 635 636 // Put the entry itself. 637 if (!entry.SerializeToString(&serialized_entry)) { 638 DLOG(ERROR) << "Failed to serialize the entry: " << id; 639 return false; 640 } 641 batch.Put(id, serialized_entry); 642 643 status = resource_map_->Write(leveldb::WriteOptions(), &batch); 644 return status.ok(); 645 } 646 647 bool ResourceMetadataStorage::GetEntry(const std::string& id, 648 ResourceEntry* out_entry) { 649 base::ThreadRestrictions::AssertIOAllowed(); 650 DCHECK(!id.empty()); 651 652 std::string serialized_entry; 653 const leveldb::Status status = resource_map_->Get(leveldb::ReadOptions(), 654 leveldb::Slice(id), 655 &serialized_entry); 656 return status.ok() && out_entry->ParseFromString(serialized_entry); 657 } 658 659 bool ResourceMetadataStorage::RemoveEntry(const std::string& id) { 660 base::ThreadRestrictions::AssertIOAllowed(); 661 DCHECK(!id.empty()); 662 663 ResourceEntry entry; 664 if (!GetEntry(id, &entry)) 665 return false; 666 667 leveldb::WriteBatch batch; 668 669 // Remove from the parent. 670 if (!entry.parent_local_id().empty()) 671 batch.Delete(GetChildEntryKey(entry.parent_local_id(), entry.base_name())); 672 673 // Remove resource ID-local ID mapping entry. 674 if (!entry.resource_id().empty()) 675 batch.Delete(GetIdEntryKey(entry.resource_id())); 676 677 // Remove the entry itself. 678 batch.Delete(id); 679 680 const leveldb::Status status = resource_map_->Write(leveldb::WriteOptions(), 681 &batch); 682 return status.ok(); 683 } 684 685 scoped_ptr<ResourceMetadataStorage::Iterator> 686 ResourceMetadataStorage::GetIterator() { 687 base::ThreadRestrictions::AssertIOAllowed(); 688 689 scoped_ptr<leveldb::Iterator> it( 690 resource_map_->NewIterator(leveldb::ReadOptions())); 691 return make_scoped_ptr(new Iterator(it.Pass())); 692 } 693 694 std::string ResourceMetadataStorage::GetChild(const std::string& parent_id, 695 const std::string& child_name) { 696 base::ThreadRestrictions::AssertIOAllowed(); 697 DCHECK(!parent_id.empty()); 698 DCHECK(!child_name.empty()); 699 700 std::string child_id; 701 resource_map_->Get(leveldb::ReadOptions(), 702 leveldb::Slice(GetChildEntryKey(parent_id, child_name)), 703 &child_id); 704 return child_id; 705 } 706 707 void ResourceMetadataStorage::GetChildren(const std::string& parent_id, 708 std::vector<std::string>* children) { 709 base::ThreadRestrictions::AssertIOAllowed(); 710 DCHECK(!parent_id.empty()); 711 712 // Iterate over all entries with keys starting with |parent_id|. 713 scoped_ptr<leveldb::Iterator> it( 714 resource_map_->NewIterator(leveldb::ReadOptions())); 715 for (it->Seek(parent_id); 716 it->Valid() && it->key().starts_with(leveldb::Slice(parent_id)); 717 it->Next()) { 718 if (IsChildEntryKey(it->key())) 719 children->push_back(it->value().ToString()); 720 } 721 DCHECK(it->status().ok()); 722 } 723 724 bool ResourceMetadataStorage::PutCacheEntry(const std::string& id, 725 const FileCacheEntry& entry) { 726 base::ThreadRestrictions::AssertIOAllowed(); 727 DCHECK(!id.empty()); 728 729 std::string serialized_entry; 730 if (!entry.SerializeToString(&serialized_entry)) { 731 DLOG(ERROR) << "Failed to serialize the entry."; 732 return false; 733 } 734 735 const leveldb::Status status = resource_map_->Put( 736 leveldb::WriteOptions(), 737 leveldb::Slice(GetCacheEntryKey(id)), 738 leveldb::Slice(serialized_entry)); 739 return status.ok(); 740 } 741 742 bool ResourceMetadataStorage::GetCacheEntry(const std::string& id, 743 FileCacheEntry* out_entry) { 744 base::ThreadRestrictions::AssertIOAllowed(); 745 DCHECK(!id.empty()); 746 747 std::string serialized_entry; 748 const leveldb::Status status = resource_map_->Get( 749 leveldb::ReadOptions(), 750 leveldb::Slice(GetCacheEntryKey(id)), 751 &serialized_entry); 752 return status.ok() && out_entry->ParseFromString(serialized_entry); 753 } 754 755 bool ResourceMetadataStorage::RemoveCacheEntry(const std::string& id) { 756 base::ThreadRestrictions::AssertIOAllowed(); 757 DCHECK(!id.empty()); 758 759 const leveldb::Status status = resource_map_->Delete( 760 leveldb::WriteOptions(), 761 leveldb::Slice(GetCacheEntryKey(id))); 762 return status.ok(); 763 } 764 765 scoped_ptr<ResourceMetadataStorage::CacheEntryIterator> 766 ResourceMetadataStorage::GetCacheEntryIterator() { 767 base::ThreadRestrictions::AssertIOAllowed(); 768 769 scoped_ptr<leveldb::Iterator> it( 770 resource_map_->NewIterator(leveldb::ReadOptions())); 771 return make_scoped_ptr(new CacheEntryIterator(it.Pass())); 772 } 773 774 ResourceMetadataStorage::RecoveredCacheInfo::RecoveredCacheInfo() 775 : is_dirty(false) {} 776 777 ResourceMetadataStorage::RecoveredCacheInfo::~RecoveredCacheInfo() {} 778 779 bool ResourceMetadataStorage::GetIdByResourceId( 780 const std::string& resource_id, 781 std::string* out_id) { 782 base::ThreadRestrictions::AssertIOAllowed(); 783 DCHECK(!resource_id.empty()); 784 785 const leveldb::Status status = resource_map_->Get( 786 leveldb::ReadOptions(), 787 leveldb::Slice(GetIdEntryKey(resource_id)), 788 out_id); 789 return status.ok(); 790 } 791 792 ResourceMetadataStorage::~ResourceMetadataStorage() { 793 base::ThreadRestrictions::AssertIOAllowed(); 794 } 795 796 void ResourceMetadataStorage::DestroyOnBlockingPool() { 797 delete this; 798 } 799 800 // static 801 std::string ResourceMetadataStorage::GetChildEntryKey( 802 const std::string& parent_id, 803 const std::string& child_name) { 804 DCHECK(!parent_id.empty()); 805 DCHECK(!child_name.empty()); 806 807 std::string key = parent_id; 808 key.push_back(kDBKeyDelimeter); 809 key.append(child_name); 810 key.push_back(kDBKeyDelimeter); 811 return key; 812 } 813 814 bool ResourceMetadataStorage::PutHeader( 815 const ResourceMetadataHeader& header) { 816 base::ThreadRestrictions::AssertIOAllowed(); 817 818 std::string serialized_header; 819 if (!header.SerializeToString(&serialized_header)) { 820 DLOG(ERROR) << "Failed to serialize the header"; 821 return false; 822 } 823 824 const leveldb::Status status = resource_map_->Put( 825 leveldb::WriteOptions(), 826 leveldb::Slice(GetHeaderDBKey()), 827 leveldb::Slice(serialized_header)); 828 return status.ok(); 829 } 830 831 bool ResourceMetadataStorage::GetHeader(ResourceMetadataHeader* header) { 832 base::ThreadRestrictions::AssertIOAllowed(); 833 834 std::string serialized_header; 835 const leveldb::Status status = resource_map_->Get( 836 leveldb::ReadOptions(), 837 leveldb::Slice(GetHeaderDBKey()), 838 &serialized_header); 839 return status.ok() && header->ParseFromString(serialized_header); 840 } 841 842 bool ResourceMetadataStorage::CheckValidity() { 843 base::ThreadRestrictions::AssertIOAllowed(); 844 845 // Perform read with checksums verification enalbed. 846 leveldb::ReadOptions options; 847 options.verify_checksums = true; 848 849 scoped_ptr<leveldb::Iterator> it(resource_map_->NewIterator(options)); 850 it->SeekToFirst(); 851 852 // DB is organized like this: 853 // 854 // <key> : <value> 855 // "\0HEADER" : ResourceMetadataHeader 856 // "\0ID\0|resource ID 1|" : Local ID associated to resource ID 1. 857 // "\0ID\0|resource ID 2|" : Local ID associated to resource ID 2. 858 // ... 859 // "|ID of A|" : ResourceEntry for entry A. 860 // "|ID of A|\0CACHE" : FileCacheEntry for entry A. 861 // "|ID of A|\0|child name 1|\0" : ID of the 1st child entry of entry A. 862 // "|ID of A|\0|child name 2|\0" : ID of the 2nd child entry of entry A. 863 // ... 864 // "|ID of A|\0|child name n|\0" : ID of the nth child entry of entry A. 865 // "|ID of B|" : ResourceEntry for entry B. 866 // "|ID of B|\0CACHE" : FileCacheEntry for entry B. 867 // ... 868 869 // Check the header. 870 ResourceMetadataHeader header; 871 if (!it->Valid() || 872 it->key() != GetHeaderDBKey() || // Header entry must come first. 873 !header.ParseFromArray(it->value().data(), it->value().size()) || 874 header.version() != kDBVersion) { 875 DLOG(ERROR) << "Invalid header detected. version = " << header.version(); 876 return false; 877 } 878 879 // Check all entries. 880 size_t num_entries_with_parent = 0; 881 size_t num_child_entries = 0; 882 ResourceEntry entry; 883 std::string serialized_entry; 884 std::string child_id; 885 for (it->Next(); it->Valid(); it->Next()) { 886 // Count child entries. 887 if (IsChildEntryKey(it->key())) { 888 ++num_child_entries; 889 continue; 890 } 891 892 // Ignore cache entries. 893 if (IsCacheEntryKey(it->key())) 894 continue; 895 896 // Check if resource-ID-to-local-ID mapping is stored correctly. 897 if (IsIdEntryKey(it->key())) { 898 leveldb::Status status = resource_map_->Get( 899 options, it->value(), &serialized_entry); 900 // Resource-ID-to-local-ID mapping without entry for the local ID is ok. 901 if (status.IsNotFound()) 902 continue; 903 // When the entry exists, its resource ID must be consistent. 904 const bool ok = status.ok() && 905 entry.ParseFromString(serialized_entry) && 906 !entry.resource_id().empty() && 907 leveldb::Slice(GetIdEntryKey(entry.resource_id())) == it->key(); 908 if (!ok) { 909 DLOG(ERROR) << "Broken ID entry. status = " << status.ToString(); 910 return false; 911 } 912 continue; 913 } 914 915 // Check if stored data is broken. 916 if (!entry.ParseFromArray(it->value().data(), it->value().size())) { 917 DLOG(ERROR) << "Broken entry detected"; 918 return false; 919 } 920 921 if (!entry.parent_local_id().empty()) { 922 // Check if the parent entry is stored. 923 leveldb::Status status = resource_map_->Get( 924 options, 925 leveldb::Slice(entry.parent_local_id()), 926 &serialized_entry); 927 if (!status.ok()) { 928 DLOG(ERROR) << "Can't get parent entry. status = " << status.ToString(); 929 return false; 930 } 931 932 // Check if parent-child relationship is stored correctly. 933 status = resource_map_->Get( 934 options, 935 leveldb::Slice(GetChildEntryKey(entry.parent_local_id(), 936 entry.base_name())), 937 &child_id); 938 if (!status.ok() || leveldb::Slice(child_id) != it->key()) { 939 DLOG(ERROR) << "Child map is broken. status = " << status.ToString(); 940 return false; 941 } 942 ++num_entries_with_parent; 943 } 944 } 945 if (!it->status().ok() || num_child_entries != num_entries_with_parent) { 946 DLOG(ERROR) << "Error during checking resource map. status = " 947 << it->status().ToString(); 948 return false; 949 } 950 return true; 951 } 952 953 } // namespace internal 954 } // namespace drive 955