| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/chromeos/drive/resource_metadata_storage.h" | 5 #include "chrome/browser/chromeos/drive/resource_metadata_storage.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/file_util.h" | 8 #include "base/file_util.h" |
| 9 #include "base/location.h" | 9 #include "base/location.h" |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| (...skipping 23 matching lines...) Expand all Loading... |
| 34 }; | 34 }; |
| 35 | 35 |
| 36 // The name of the DB which stores the metadata. | 36 // The name of the DB which stores the metadata. |
| 37 const base::FilePath::CharType kResourceMapDBName[] = | 37 const base::FilePath::CharType kResourceMapDBName[] = |
| 38 FILE_PATH_LITERAL("resource_metadata_resource_map.db"); | 38 FILE_PATH_LITERAL("resource_metadata_resource_map.db"); |
| 39 | 39 |
| 40 // The name of the DB which couldn't be opened, but is preserved just in case. | 40 // The name of the DB which couldn't be opened, but is preserved just in case. |
| 41 const base::FilePath::CharType kPreservedResourceMapDBName[] = | 41 const base::FilePath::CharType kPreservedResourceMapDBName[] = |
| 42 FILE_PATH_LITERAL("resource_metadata_preserved_resource_map.db"); | 42 FILE_PATH_LITERAL("resource_metadata_preserved_resource_map.db"); |
| 43 | 43 |
| 44 // The name of the DB which couldn't be opened, and was replaced with a new one. |
| 45 const base::FilePath::CharType kTrashedResourceMapDBName[] = |
| 46 FILE_PATH_LITERAL("resource_metadata_trashed_resource_map.db"); |
| 47 |
| 44 // Meant to be a character which never happen to be in real IDs. | 48 // Meant to be a character which never happen to be in real IDs. |
| 45 const char kDBKeyDelimeter = '\0'; | 49 const char kDBKeyDelimeter = '\0'; |
| 46 | 50 |
| 47 // String used as a suffix of a key for a cache entry. | 51 // String used as a suffix of a key for a cache entry. |
| 48 const char kCacheEntryKeySuffix[] = "CACHE"; | 52 const char kCacheEntryKeySuffix[] = "CACHE"; |
| 49 | 53 |
| 50 // String used as a prefix of a key for a resource-ID-to-local-ID entry. | 54 // String used as a prefix of a key for a resource-ID-to-local-ID entry. |
| 51 const char kIdEntryKeyPrefix[] = "ID"; | 55 const char kIdEntryKeyPrefix[] = "ID"; |
| 52 | 56 |
| 53 // Returns a string to be used as the key for the header. | 57 // Returns a string to be used as the key for the header. |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 130 return DB_INIT_IO_ERROR; | 134 return DB_INIT_IO_ERROR; |
| 131 return DB_INIT_FAILED; | 135 return DB_INIT_FAILED; |
| 132 } | 136 } |
| 133 | 137 |
| 134 ResourceMetadataHeader GetDefaultHeaderEntry() { | 138 ResourceMetadataHeader GetDefaultHeaderEntry() { |
| 135 ResourceMetadataHeader header; | 139 ResourceMetadataHeader header; |
| 136 header.set_version(ResourceMetadataStorage::kDBVersion); | 140 header.set_version(ResourceMetadataStorage::kDBVersion); |
| 137 return header; | 141 return header; |
| 138 } | 142 } |
| 139 | 143 |
| 144 bool MoveIfPossible(const base::FilePath& from, const base::FilePath& to) { |
| 145 return !base::PathExists(from) || base::Move(from, to); |
| 146 } |
| 147 |
| 140 } // namespace | 148 } // namespace |
| 141 | 149 |
| 142 ResourceMetadataStorage::Iterator::Iterator(scoped_ptr<leveldb::Iterator> it) | 150 ResourceMetadataStorage::Iterator::Iterator(scoped_ptr<leveldb::Iterator> it) |
| 143 : it_(it.Pass()) { | 151 : it_(it.Pass()) { |
| 144 base::ThreadRestrictions::AssertIOAllowed(); | 152 base::ThreadRestrictions::AssertIOAllowed(); |
| 145 DCHECK(it_); | 153 DCHECK(it_); |
| 146 | 154 |
| 147 // Skip the header entry. | 155 // Skip the header entry. |
| 148 // Note: The header entry comes before all other entries because its key | 156 // Note: The header entry comes before all other entries because its key |
| 149 // starts with kDBKeyDelimeter. (i.e. '\0') | 157 // starts with kDBKeyDelimeter. (i.e. '\0') |
| (...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 367 | 375 |
| 368 bool ResourceMetadataStorage::Initialize() { | 376 bool ResourceMetadataStorage::Initialize() { |
| 369 base::ThreadRestrictions::AssertIOAllowed(); | 377 base::ThreadRestrictions::AssertIOAllowed(); |
| 370 | 378 |
| 371 resource_map_.reset(); | 379 resource_map_.reset(); |
| 372 | 380 |
| 373 const base::FilePath resource_map_path = | 381 const base::FilePath resource_map_path = |
| 374 directory_path_.Append(kResourceMapDBName); | 382 directory_path_.Append(kResourceMapDBName); |
| 375 const base::FilePath preserved_resource_map_path = | 383 const base::FilePath preserved_resource_map_path = |
| 376 directory_path_.Append(kPreservedResourceMapDBName); | 384 directory_path_.Append(kPreservedResourceMapDBName); |
| 385 const base::FilePath trashed_resource_map_path = |
| 386 directory_path_.Append(kTrashedResourceMapDBName); |
| 387 |
| 388 // Discard unneeded DBs. |
| 389 if (!base::DeleteFile(preserved_resource_map_path, true /* recursive */) || |
| 390 !base::DeleteFile(trashed_resource_map_path, true /* recursive */)) { |
| 391 LOG(ERROR) << "Failed to remove unneeded DBs."; |
| 392 return false; |
| 393 } |
| 377 | 394 |
| 378 // Try to open the existing DB. | 395 // Try to open the existing DB. |
| 379 leveldb::DB* db = NULL; | 396 leveldb::DB* db = NULL; |
| 380 leveldb::Options options; | 397 leveldb::Options options; |
| 381 options.max_open_files = 0; // Use minimum. | 398 options.max_open_files = 0; // Use minimum. |
| 382 options.create_if_missing = false; | 399 options.create_if_missing = false; |
| 383 | 400 |
| 384 DBInitStatus open_existing_result = DB_INIT_NOT_FOUND; | 401 DBInitStatus open_existing_result = DB_INIT_NOT_FOUND; |
| 385 leveldb::Status status; | 402 leveldb::Status status; |
| 386 if (base::PathExists(resource_map_path)) { | 403 if (base::PathExists(resource_map_path)) { |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 418 open_existing_result, | 435 open_existing_result, |
| 419 DB_INIT_MAX_VALUE); | 436 DB_INIT_MAX_VALUE); |
| 420 | 437 |
| 421 DBInitStatus init_result = DB_INIT_SUCCESS; | 438 DBInitStatus init_result = DB_INIT_SUCCESS; |
| 422 | 439 |
| 423 // Failed to open the existing DB, create new DB. | 440 // Failed to open the existing DB, create new DB. |
| 424 if (!resource_map_) { | 441 if (!resource_map_) { |
| 425 // Move the existing DB to the preservation path. The moved old DB is | 442 // Move the existing DB to the preservation path. The moved old DB is |
| 426 // deleted once the new DB creation succeeds, or is restored later in | 443 // deleted once the new DB creation succeeds, or is restored later in |
| 427 // UpgradeOldDB() when the creation fails. | 444 // UpgradeOldDB() when the creation fails. |
| 428 if (base::PathExists(resource_map_path) && | 445 MoveIfPossible(resource_map_path, preserved_resource_map_path); |
| 429 base::DeleteFile(preserved_resource_map_path, true /* recursive */)) | |
| 430 base::Move(resource_map_path, preserved_resource_map_path); | |
| 431 | 446 |
| 432 // Create DB. | 447 // Create DB. |
| 433 options.max_open_files = 0; // Use minimum. | 448 options.max_open_files = 0; // Use minimum. |
| 434 options.create_if_missing = true; | 449 options.create_if_missing = true; |
| 435 options.error_if_exists = true; | 450 options.error_if_exists = true; |
| 436 | 451 |
| 437 status = leveldb::DB::Open(options, resource_map_path.AsUTF8Unsafe(), &db); | 452 status = leveldb::DB::Open(options, resource_map_path.AsUTF8Unsafe(), &db); |
| 438 if (status.ok()) { | 453 if (status.ok()) { |
| 439 resource_map_.reset(db); | 454 resource_map_.reset(db); |
| 440 | 455 |
| 441 if (!PutHeader(GetDefaultHeaderEntry()) || // Set up header. | 456 if (!PutHeader(GetDefaultHeaderEntry()) || // Set up header. |
| 442 !base::DeleteFile(preserved_resource_map_path, | 457 !MoveIfPossible(preserved_resource_map_path, // Trash the old DB. |
| 443 true /* recursive */)) { // Remove the old DB. | 458 trashed_resource_map_path)) { |
| 444 init_result = DB_INIT_FAILED; | 459 init_result = DB_INIT_FAILED; |
| 445 resource_map_.reset(); | 460 resource_map_.reset(); |
| 446 } | 461 } |
| 447 } else { | 462 } else { |
| 448 LOG(ERROR) << "Failed to create resource map DB: " << status.ToString(); | 463 LOG(ERROR) << "Failed to create resource map DB: " << status.ToString(); |
| 449 init_result = LevelDBStatusToDBInitStatus(status); | 464 init_result = LevelDBStatusToDBInitStatus(status); |
| 450 } | 465 } |
| 451 } | 466 } |
| 452 | 467 |
| 453 UMA_HISTOGRAM_ENUMERATION("Drive.MetadataDBInitResult", | 468 UMA_HISTOGRAM_ENUMERATION("Drive.MetadataDBInitResult", |
| 454 init_result, | 469 init_result, |
| 455 DB_INIT_MAX_VALUE); | 470 DB_INIT_MAX_VALUE); |
| 456 return resource_map_; | 471 return resource_map_; |
| 457 } | 472 } |
| 458 | 473 |
| 474 void ResourceMetadataStorage::RecoverCacheEntriesFromTrashedResourceMap( |
| 475 std::map<std::string, FileCacheEntry>* out_entries) { |
| 476 const base::FilePath trashed_resource_map_path = |
| 477 directory_path_.Append(kTrashedResourceMapDBName); |
| 478 |
| 479 if (!base::PathExists(trashed_resource_map_path)) |
| 480 return; |
| 481 |
| 482 leveldb::Options options; |
| 483 options.max_open_files = 0; // Use minimum. |
| 484 options.create_if_missing = false; |
| 485 |
| 486 // Trashed DB may be broken, repair it first. |
| 487 leveldb::Status status; |
| 488 status = leveldb::RepairDB(trashed_resource_map_path.AsUTF8Unsafe(), options); |
| 489 if (!status.ok()) { |
| 490 LOG(ERROR) << "Failed to repair trashed DB: " << status.ToString(); |
| 491 return; |
| 492 } |
| 493 |
| 494 // Open it. |
| 495 leveldb::DB* db = NULL; |
| 496 status = leveldb::DB::Open(options, trashed_resource_map_path.AsUTF8Unsafe(), |
| 497 &db); |
| 498 if (!status.ok()) { |
| 499 LOG(ERROR) << "Failed to open trashed DB: " << status.ToString(); |
| 500 return; |
| 501 } |
| 502 scoped_ptr<leveldb::DB> resource_map(db); |
| 503 |
| 504 // Check DB version. |
| 505 std::string serialized_header; |
| 506 ResourceMetadataHeader header; |
| 507 if (!resource_map->Get(leveldb::ReadOptions(), |
| 508 leveldb::Slice(GetHeaderDBKey()), |
| 509 &serialized_header).ok() || |
| 510 !header.ParseFromString(serialized_header) || |
| 511 header.version() != kDBVersion) { |
| 512 LOG(ERROR) << "Incompatible DB version: " << header.version(); |
| 513 return; |
| 514 } |
| 515 |
| 516 // Collect cache entries. |
| 517 scoped_ptr<leveldb::Iterator> it( |
| 518 resource_map->NewIterator(leveldb::ReadOptions())); |
| 519 for (it->SeekToFirst(); it->Valid(); it->Next()) { |
| 520 if (IsCacheEntryKey(it->key())) { |
| 521 const std::string& id = GetIdFromCacheEntryKey(it->key()); |
| 522 FileCacheEntry cache_entry; |
| 523 if (cache_entry.ParseFromArray(it->value().data(), it->value().size())) |
| 524 (*out_entries)[id] = cache_entry; |
| 525 } |
| 526 } |
| 527 } |
| 528 |
| 459 bool ResourceMetadataStorage::SetLargestChangestamp( | 529 bool ResourceMetadataStorage::SetLargestChangestamp( |
| 460 int64 largest_changestamp) { | 530 int64 largest_changestamp) { |
| 461 base::ThreadRestrictions::AssertIOAllowed(); | 531 base::ThreadRestrictions::AssertIOAllowed(); |
| 462 | 532 |
| 463 ResourceMetadataHeader header; | 533 ResourceMetadataHeader header; |
| 464 if (!GetHeader(&header)) { | 534 if (!GetHeader(&header)) { |
| 465 DLOG(ERROR) << "Failed to get the header."; | 535 DLOG(ERROR) << "Failed to get the header."; |
| 466 return false; | 536 return false; |
| 467 } | 537 } |
| 468 header.set_largest_changestamp(largest_changestamp); | 538 header.set_largest_changestamp(largest_changestamp); |
| (...skipping 358 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 827 if (!it->status().ok() || num_child_entries != num_entries_with_parent) { | 897 if (!it->status().ok() || num_child_entries != num_entries_with_parent) { |
| 828 DLOG(ERROR) << "Error during checking resource map. status = " | 898 DLOG(ERROR) << "Error during checking resource map. status = " |
| 829 << it->status().ToString(); | 899 << it->status().ToString(); |
| 830 return false; | 900 return false; |
| 831 } | 901 } |
| 832 return true; | 902 return true; |
| 833 } | 903 } |
| 834 | 904 |
| 835 } // namespace internal | 905 } // namespace internal |
| 836 } // namespace drive | 906 } // namespace drive |
| OLD | NEW |