Home | History | Annotate | Download | only in drive
      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