Home | History | Annotate | Download | only in drive
      1 // Copyright (c) 2012 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/file_cache_metadata.h"
      6 
      7 #include "base/callback.h"
      8 #include "base/file_util.h"
      9 #include "base/files/file_enumerator.h"
     10 #include "base/metrics/histogram.h"
     11 #include "base/sequenced_task_runner.h"
     12 #include "base/threading/thread_restrictions.h"
     13 #include "chrome/browser/chromeos/drive/drive.pb.h"
     14 #include "chrome/browser/chromeos/drive/file_system_util.h"
     15 #include "third_party/leveldatabase/src/include/leveldb/db.h"
     16 
     17 namespace drive {
     18 namespace internal {
     19 
     20 namespace {
     21 
     22 enum DBOpenStatus {
     23   DB_OPEN_SUCCESS,
     24   DB_OPEN_FAILURE_CORRUPTION,
     25   DB_OPEN_FAILURE_OTHER,
     26   DB_OPEN_FAILURE_UNRECOVERABLE,
     27   DB_OPEN_MAX_VALUE,
     28 };
     29 
     30 }  // namespace
     31 
     32 FileCacheMetadata::Iterator::Iterator(scoped_ptr<leveldb::Iterator> it)
     33     : it_(it.Pass()) {
     34   base::ThreadRestrictions::AssertIOAllowed();
     35   DCHECK(it_);
     36 
     37   it_->SeekToFirst();
     38   AdvanceInternal();
     39 }
     40 
     41 FileCacheMetadata::Iterator::~Iterator() {
     42   base::ThreadRestrictions::AssertIOAllowed();
     43 }
     44 
     45 bool FileCacheMetadata::Iterator::IsAtEnd() const {
     46   base::ThreadRestrictions::AssertIOAllowed();
     47   return !it_->Valid();
     48 }
     49 
     50 std::string FileCacheMetadata::Iterator::GetKey() const {
     51   base::ThreadRestrictions::AssertIOAllowed();
     52   DCHECK(!IsAtEnd());
     53   return it_->key().ToString();
     54 }
     55 
     56 const FileCacheEntry& FileCacheMetadata::Iterator::GetValue() const {
     57   base::ThreadRestrictions::AssertIOAllowed();
     58   DCHECK(!IsAtEnd());
     59   return entry_;
     60 }
     61 
     62 void FileCacheMetadata::Iterator::Advance() {
     63   base::ThreadRestrictions::AssertIOAllowed();
     64   DCHECK(!IsAtEnd());
     65 
     66   it_->Next();
     67   AdvanceInternal();
     68 }
     69 
     70 bool FileCacheMetadata::Iterator::HasError() const {
     71   base::ThreadRestrictions::AssertIOAllowed();
     72   return !it_->status().ok();
     73 }
     74 
     75 void FileCacheMetadata::Iterator::AdvanceInternal() {
     76   for (; it_->Valid(); it_->Next()) {
     77     // Skip unparsable broken entries.
     78     // TODO(hashimoto): Broken entries should be cleaned up at some point.
     79     if (entry_.ParseFromArray(it_->value().data(), it_->value().size()))
     80       break;
     81   }
     82 }
     83 
     84 FileCacheMetadata::FileCacheMetadata(
     85     base::SequencedTaskRunner* blocking_task_runner)
     86     : blocking_task_runner_(blocking_task_runner) {
     87   AssertOnSequencedWorkerPool();
     88 }
     89 
     90 FileCacheMetadata::~FileCacheMetadata() {
     91   AssertOnSequencedWorkerPool();
     92 }
     93 
     94 FileCacheMetadata::InitializeResult FileCacheMetadata::Initialize(
     95     const base::FilePath& db_path) {
     96   AssertOnSequencedWorkerPool();
     97 
     98   bool created = !base::PathExists(db_path);
     99 
    100   leveldb::DB* level_db = NULL;
    101   leveldb::Options options;
    102   options.create_if_missing = true;
    103   options.max_open_files = 64;  // Use minimum.
    104   leveldb::Status db_status = leveldb::DB::Open(options, db_path.AsUTF8Unsafe(),
    105                                                 &level_db);
    106 
    107   // Delete the db and scan the physical cache. This will fix a corrupt db, but
    108   // perhaps not other causes of failed DB::Open.
    109   DBOpenStatus uma_status = DB_OPEN_SUCCESS;
    110   if (!db_status.ok()) {
    111     LOG(WARNING) << "Cache db failed to open: " << db_status.ToString();
    112     uma_status = db_status.IsCorruption() ?
    113         DB_OPEN_FAILURE_CORRUPTION : DB_OPEN_FAILURE_OTHER;
    114     const bool deleted = base::DeleteFile(db_path, true);
    115     DCHECK(deleted);
    116     db_status = leveldb::DB::Open(options, db_path.value(), &level_db);
    117     if (!db_status.ok()) {
    118       LOG(WARNING) << "Still failed to open: " << db_status.ToString();
    119       UMA_HISTOGRAM_ENUMERATION("Drive.CacheDBOpenStatus",
    120                                 DB_OPEN_FAILURE_UNRECOVERABLE,
    121                                 DB_OPEN_MAX_VALUE);
    122       return INITIALIZE_FAILED;
    123     }
    124 
    125     created = true;
    126   }
    127   UMA_HISTOGRAM_ENUMERATION("Drive.CacheDBOpenStatus", uma_status,
    128                             DB_OPEN_MAX_VALUE);
    129   DCHECK(level_db);
    130   level_db_.reset(level_db);
    131 
    132   return created ? INITIALIZE_CREATED : INITIALIZE_OPENED;
    133 }
    134 
    135 void FileCacheMetadata::AddOrUpdateCacheEntry(
    136     const std::string& resource_id,
    137     const FileCacheEntry& cache_entry) {
    138   AssertOnSequencedWorkerPool();
    139 
    140   DVLOG(1) << "AddOrUpdateCacheEntry, resource_id=" << resource_id;
    141   std::string serialized;
    142   const bool ok = cache_entry.SerializeToString(&serialized);
    143   if (ok)
    144     level_db_->Put(leveldb::WriteOptions(),
    145                    leveldb::Slice(resource_id),
    146                    leveldb::Slice(serialized));
    147 }
    148 
    149 void FileCacheMetadata::RemoveCacheEntry(const std::string& resource_id) {
    150   AssertOnSequencedWorkerPool();
    151 
    152   DVLOG(1) << "RemoveCacheEntry, resource_id=" << resource_id;
    153   level_db_->Delete(leveldb::WriteOptions(), leveldb::Slice(resource_id));
    154 }
    155 
    156 bool FileCacheMetadata::GetCacheEntry(const std::string& resource_id,
    157                                       FileCacheEntry* entry) {
    158   DCHECK(entry);
    159   AssertOnSequencedWorkerPool();
    160 
    161   std::string serialized;
    162   const leveldb::Status status = level_db_->Get(
    163       leveldb::ReadOptions(),
    164       leveldb::Slice(resource_id), &serialized);
    165   if (!status.ok()) {
    166     DVLOG(1) << "Can't find " << resource_id << " in cache db";
    167     return false;
    168   }
    169 
    170   if (!entry->ParseFromString(serialized)) {
    171     LOG(ERROR) << "Failed to parse " << serialized;
    172     return false;
    173   }
    174   return true;
    175 }
    176 
    177 scoped_ptr<FileCacheMetadata::Iterator> FileCacheMetadata::GetIterator() {
    178   AssertOnSequencedWorkerPool();
    179 
    180   scoped_ptr<leveldb::Iterator> iter(level_db_->NewIterator(
    181       leveldb::ReadOptions()));
    182   return make_scoped_ptr(new Iterator(iter.Pass()));
    183 }
    184 
    185 void FileCacheMetadata::AssertOnSequencedWorkerPool() {
    186   DCHECK(!blocking_task_runner_.get() ||
    187          blocking_task_runner_->RunsTasksOnCurrentThread());
    188 }
    189 
    190 }  // namespace internal
    191 }  // namespace drive
    192