Home | History | Annotate | Download | only in drive_backend
      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/sync_file_system/drive_backend/drive_metadata_store.h"
      6 
      7 #include <utility>
      8 #include <vector>
      9 
     10 #include "base/bind.h"
     11 #include "base/callback.h"
     12 #include "base/files/file_path.h"
     13 #include "base/location.h"
     14 #include "base/message_loop/message_loop_proxy.h"
     15 #include "base/sequenced_task_runner.h"
     16 #include "base/stl_util.h"
     17 #include "base/strings/string_number_conversions.h"
     18 #include "base/strings/string_util.h"
     19 #include "base/strings/stringprintf.h"
     20 #include "base/task_runner_util.h"
     21 #include "base/values.h"
     22 #include "chrome/browser/sync_file_system/drive_backend/drive_file_sync_service.h"
     23 #include "chrome/browser/sync_file_system/drive_backend/drive_file_sync_util.h"
     24 #include "chrome/browser/sync_file_system/drive_backend/metadata_db_migration_util.h"
     25 #include "chrome/browser/sync_file_system/logger.h"
     26 #include "chrome/browser/sync_file_system/sync_file_system.pb.h"
     27 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
     28 #include "third_party/leveldatabase/src/include/leveldb/db.h"
     29 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
     30 #include "url/gurl.h"
     31 #include "webkit/browser/fileapi/file_system_url.h"
     32 #include "webkit/common/fileapi/file_system_util.h"
     33 
     34 using fileapi::FileSystemURL;
     35 
     36 namespace sync_file_system {
     37 
     38 typedef DriveMetadataStore::MetadataMap MetadataMap;
     39 typedef DriveMetadataStore::OriginByResourceId OriginByResourceId;
     40 typedef DriveMetadataStore::PathToMetadata PathToMetadata;
     41 typedef DriveMetadataStore::ResourceIdByOrigin ResourceIdByOrigin;
     42 
     43 const base::FilePath::CharType DriveMetadataStore::kDatabaseName[] =
     44     FILE_PATH_LITERAL("DriveMetadata");
     45 
     46 struct DBContents {
     47   SyncStatusCode status;
     48   scoped_ptr<leveldb::DB> db;
     49   bool created;
     50 
     51   int64 largest_changestamp;
     52   DriveMetadataStore::MetadataMap metadata_map;
     53   std::string sync_root_directory_resource_id;
     54   ResourceIdByOrigin incremental_sync_origins;
     55   ResourceIdByOrigin disabled_origins;
     56 
     57   DBContents()
     58       : status(SYNC_STATUS_UNKNOWN),
     59         created(false),
     60         largest_changestamp(0) {
     61   }
     62 };
     63 
     64 namespace {
     65 
     66 const char kDatabaseVersionKey[] = "VERSION";
     67 const int64 kCurrentDatabaseVersion = 2;
     68 const char kChangeStampKey[] = "CHANGE_STAMP";
     69 const char kSyncRootDirectoryKey[] = "SYNC_ROOT_DIR";
     70 const char kDriveMetadataKeyPrefix[] = "METADATA: ";
     71 const char kMetadataKeySeparator = ' ';
     72 const char kDriveIncrementalSyncOriginKeyPrefix[] = "ISYNC_ORIGIN: ";
     73 const char kDriveDisabledOriginKeyPrefix[] = "DISABLED_ORIGIN: ";
     74 const size_t kDriveMetadataKeyPrefixLength = arraysize(kDriveMetadataKeyPrefix);
     75 
     76 enum OriginSyncType {
     77   INCREMENTAL_SYNC_ORIGIN,
     78   DISABLED_ORIGIN
     79 };
     80 
     81 std::string RemovePrefix(const std::string& str, const std::string& prefix) {
     82   if (StartsWithASCII(str, prefix, true))
     83     return str.substr(prefix.size());
     84   return str;
     85 }
     86 
     87 std::string OriginAndPathToMetadataKey(const GURL& origin,
     88                                        const base::FilePath& path) {
     89   return kDriveMetadataKeyPrefix + origin.spec() +
     90       kMetadataKeySeparator + path.AsUTF8Unsafe();
     91 }
     92 
     93 std::string FileSystemURLToMetadataKey(const FileSystemURL& url) {
     94   return OriginAndPathToMetadataKey(url.origin(), url.path());
     95 }
     96 
     97 void MetadataKeyToOriginAndPath(const std::string& metadata_key,
     98                                 GURL* origin,
     99                                 base::FilePath* path) {
    100   std::string key_body(RemovePrefix(metadata_key, kDriveMetadataKeyPrefix));
    101   size_t separator_position = key_body.find(kMetadataKeySeparator);
    102   *origin = GURL(key_body.substr(0, separator_position));
    103   *path = base::FilePath::FromUTF8Unsafe(
    104       key_body.substr(separator_position + 1));
    105 }
    106 
    107 bool UpdateResourceIdMap(ResourceIdByOrigin* map,
    108                          OriginByResourceId* reverse_map,
    109                          const GURL& origin,
    110                          const std::string& resource_id) {
    111   ResourceIdByOrigin::iterator found = map->find(origin);
    112   if (found == map->end())
    113     return false;
    114   reverse_map->erase(found->second);
    115   reverse_map->insert(std::make_pair(resource_id, origin));
    116 
    117   found->second = resource_id;
    118   return true;
    119 }
    120 
    121 ////////////////////////////////////////////////////////////////////////////////
    122 
    123 bool IsDBEmpty(leveldb::DB* db) {
    124   DCHECK(db);
    125   scoped_ptr<leveldb::Iterator> itr(db->NewIterator(leveldb::ReadOptions()));
    126   itr->SeekToFirst();
    127   return !itr->Valid();
    128 }
    129 
    130 scoped_ptr<leveldb::DB> OpenDatabase(const base::FilePath& path,
    131                                      SyncStatusCode* status,
    132                                      bool* created) {
    133   DCHECK(status);
    134   DCHECK(created);
    135 
    136   leveldb::Options options;
    137   options.max_open_files = 64;  // Use minimum.
    138   options.create_if_missing = true;
    139   leveldb::DB* db = NULL;
    140   leveldb::Status db_status = leveldb::DB::Open(
    141       options, path.AsUTF8Unsafe(), &db);
    142   if (db_status.ok()) {
    143     *created = IsDBEmpty(db);
    144   } else {
    145     delete db;
    146     db = NULL;
    147   }
    148   *status = LevelDBStatusToSyncStatusCode(db_status);
    149 
    150   return make_scoped_ptr(db);
    151 }
    152 
    153 SyncStatusCode WriteInitialData(leveldb::DB* db) {
    154   DCHECK(db);
    155   return LevelDBStatusToSyncStatusCode(db->Put(
    156       leveldb::WriteOptions(),
    157       kDatabaseVersionKey,
    158       base::Int64ToString(kCurrentDatabaseVersion)));
    159 }
    160 
    161 SyncStatusCode MigrateDatabaseIfNeeded(leveldb::DB* db) {
    162   DCHECK(db);
    163   std::string value;
    164   leveldb::Status status = db->Get(leveldb::ReadOptions(),
    165                                    kDatabaseVersionKey, &value);
    166   int64 version = 0;
    167   if (status.ok()) {
    168     if (!base::StringToInt64(value, &version))
    169       return SYNC_DATABASE_ERROR_FAILED;
    170   } else {
    171     if (!status.IsNotFound())
    172       return SYNC_DATABASE_ERROR_FAILED;
    173   }
    174 
    175   switch (version) {
    176     case 0:
    177       drive_backend::MigrateDatabaseFromV0ToV1(db);
    178       // fall-through
    179     case 1:
    180       drive_backend::MigrateDatabaseFromV1ToV2(db);
    181       // fall-through
    182     case 2:
    183       DCHECK_EQ(2, kCurrentDatabaseVersion);
    184       return SYNC_STATUS_OK;
    185     default:
    186       return SYNC_DATABASE_ERROR_FAILED;
    187   }
    188 }
    189 
    190 SyncStatusCode ReadContents(DBContents* contents) {
    191   DCHECK(contents);
    192   DCHECK(contents->db);
    193 
    194   contents->largest_changestamp = 0;
    195   contents->metadata_map.clear();
    196   contents->incremental_sync_origins.clear();
    197 
    198   scoped_ptr<leveldb::Iterator> itr(
    199       contents->db->NewIterator(leveldb::ReadOptions()));
    200   for (itr->SeekToFirst(); itr->Valid(); itr->Next()) {
    201     std::string key = itr->key().ToString();
    202     if (key == kChangeStampKey) {
    203       bool success = base::StringToInt64(itr->value().ToString(),
    204                                          &contents->largest_changestamp);
    205       DCHECK(success);
    206       continue;
    207     }
    208 
    209     if (key == kSyncRootDirectoryKey) {
    210       std::string resource_id = itr->value().ToString();
    211       if (IsDriveAPIDisabled())
    212         resource_id = drive_backend::AddWapiFolderPrefix(resource_id);
    213       contents->sync_root_directory_resource_id = resource_id;
    214       continue;
    215     }
    216 
    217     if (StartsWithASCII(key, kDriveMetadataKeyPrefix, true)) {
    218       GURL origin;
    219       base::FilePath path;
    220       MetadataKeyToOriginAndPath(key, &origin, &path);
    221 
    222       DriveMetadata metadata;
    223       bool success = metadata.ParseFromString(itr->value().ToString());
    224       DCHECK(success);
    225 
    226       if (IsDriveAPIDisabled()) {
    227         metadata.set_resource_id(drive_backend::AddWapiIdPrefix(
    228             metadata.resource_id(), metadata.type()));
    229       }
    230 
    231       success = contents->metadata_map[origin].insert(
    232           std::make_pair(path, metadata)).second;
    233       DCHECK(success);
    234       continue;
    235     }
    236 
    237     if (StartsWithASCII(key, kDriveIncrementalSyncOriginKeyPrefix, true)) {
    238       GURL origin(RemovePrefix(key, kDriveIncrementalSyncOriginKeyPrefix));
    239       DCHECK(origin.is_valid());
    240 
    241       std::string origin_resource_id = IsDriveAPIDisabled()
    242           ? drive_backend::AddWapiFolderPrefix(itr->value().ToString())
    243           : itr->value().ToString();
    244 
    245       DCHECK(!ContainsKey(contents->incremental_sync_origins, origin));
    246       contents->incremental_sync_origins[origin] = origin_resource_id;
    247       continue;
    248     }
    249 
    250     if (StartsWithASCII(key, kDriveDisabledOriginKeyPrefix, true)) {
    251       GURL origin(RemovePrefix(key, kDriveDisabledOriginKeyPrefix));
    252       DCHECK(origin.is_valid());
    253 
    254       std::string origin_resource_id = IsDriveAPIDisabled()
    255           ? drive_backend::AddWapiFolderPrefix(itr->value().ToString())
    256           : itr->value().ToString();
    257 
    258       DCHECK(!ContainsKey(contents->disabled_origins, origin));
    259       contents->disabled_origins[origin] = origin_resource_id;
    260       continue;
    261     }
    262   }
    263 
    264   return SYNC_STATUS_OK;
    265 }
    266 
    267 scoped_ptr<DBContents> LoadDBContents(const base::FilePath& db_path) {
    268   scoped_ptr<DBContents> contents(new DBContents);
    269   contents->db = OpenDatabase(db_path,
    270                               &contents->status,
    271                               &contents->created);
    272   if (contents->status != SYNC_STATUS_OK)
    273     return contents.Pass();
    274 
    275   if (contents->created) {
    276     contents->status = WriteInitialData(contents->db.get());
    277     if (contents->status != SYNC_STATUS_OK)
    278       return contents.Pass();
    279   } else {
    280     contents->status = MigrateDatabaseIfNeeded(contents->db.get());
    281     if (contents->status != SYNC_STATUS_OK)
    282       return contents.Pass();
    283   }
    284 
    285   contents->status = ReadContents(contents.get());
    286   return contents.Pass();
    287 }
    288 
    289 ////////////////////////////////////////////////////////////////////////////////
    290 
    291 // Returns a key string for the given origin.
    292 // For example, when |origin| is "http://www.example.com" and |sync_type| is
    293 // BATCH_SYNC_ORIGIN, returns "BSYNC_ORIGIN: http://www.example.com".
    294 std::string CreateKeyForOriginRoot(const GURL& origin,
    295                                    OriginSyncType sync_type) {
    296   DCHECK(origin.is_valid());
    297   switch (sync_type) {
    298     case INCREMENTAL_SYNC_ORIGIN:
    299       return kDriveIncrementalSyncOriginKeyPrefix + origin.spec();
    300     case DISABLED_ORIGIN:
    301       return kDriveDisabledOriginKeyPrefix + origin.spec();
    302   }
    303   NOTREACHED();
    304   return std::string();
    305 }
    306 
    307 void AddOriginsToVector(std::vector<GURL>* all_origins,
    308                         const ResourceIdByOrigin& resource_map) {
    309   for (ResourceIdByOrigin::const_iterator itr = resource_map.begin();
    310        itr != resource_map.end();
    311        ++itr) {
    312     all_origins->push_back(itr->first);
    313   }
    314 }
    315 
    316 void InsertReverseMap(const ResourceIdByOrigin& forward_map,
    317                       OriginByResourceId* backward_map) {
    318   for (ResourceIdByOrigin::const_iterator itr = forward_map.begin();
    319        itr != forward_map.end(); ++itr)
    320     backward_map->insert(std::make_pair(itr->second, itr->first));
    321 }
    322 
    323 bool EraseIfExists(ResourceIdByOrigin* map,
    324                    const GURL& origin,
    325                    std::string* resource_id) {
    326   ResourceIdByOrigin::iterator found = map->find(origin);
    327   if (found == map->end())
    328     return false;
    329   *resource_id = found->second;
    330   map->erase(found);
    331   return true;
    332 }
    333 
    334 void AppendMetadataDeletionToBatch(const MetadataMap& metadata_map,
    335                                    const GURL& origin,
    336                                    leveldb::WriteBatch* batch) {
    337   MetadataMap::const_iterator found = metadata_map.find(origin);
    338   if (found == metadata_map.end())
    339     return;
    340 
    341   for (PathToMetadata::const_iterator itr = found->second.begin();
    342        itr != found->second.end(); ++itr)
    343     batch->Delete(OriginAndPathToMetadataKey(origin, itr->first));
    344 }
    345 
    346 std::string DriveTypeToString(DriveMetadata_ResourceType drive_type) {
    347   switch (drive_type) {
    348     case DriveMetadata_ResourceType_RESOURCE_TYPE_FILE:
    349       return "file";
    350     case DriveMetadata_ResourceType_RESOURCE_TYPE_FOLDER:
    351       return "folder";
    352   }
    353 
    354   NOTREACHED();
    355   return "unknown";
    356 }
    357 
    358 }  // namespace
    359 
    360 DriveMetadataStore::DriveMetadataStore(
    361     const base::FilePath& base_dir,
    362     base::SequencedTaskRunner* file_task_runner)
    363     : file_task_runner_(file_task_runner),
    364       base_dir_(base_dir),
    365       db_status_(SYNC_STATUS_UNKNOWN),
    366       largest_changestamp_(0) {
    367   DCHECK(file_task_runner);
    368 }
    369 
    370 DriveMetadataStore::~DriveMetadataStore() {
    371   DCHECK(CalledOnValidThread());
    372   file_task_runner_->DeleteSoon(FROM_HERE, db_.release());
    373 }
    374 
    375 void DriveMetadataStore::Initialize(const InitializationCallback& callback) {
    376   DCHECK(CalledOnValidThread());
    377   base::PostTaskAndReplyWithResult(
    378       file_task_runner_.get(), FROM_HERE,
    379       base::Bind(&LoadDBContents, base_dir_.Append(kDatabaseName)),
    380       base::Bind(&DriveMetadataStore::DidInitialize, AsWeakPtr(), callback));
    381 }
    382 
    383 void DriveMetadataStore::DidInitialize(const InitializationCallback& callback,
    384                                        scoped_ptr<DBContents> contents) {
    385   DCHECK(CalledOnValidThread());
    386   DCHECK(contents);
    387 
    388   db_status_ = contents->status;
    389   if (db_status_ != SYNC_STATUS_OK) {
    390     callback.Run(db_status_, false);
    391     return;
    392   }
    393 
    394   db_ = contents->db.Pass();
    395   largest_changestamp_ = contents->largest_changestamp;
    396   metadata_map_.swap(contents->metadata_map);
    397   sync_root_directory_resource_id_ = contents->sync_root_directory_resource_id;
    398   incremental_sync_origins_.swap(contents->incremental_sync_origins);
    399   disabled_origins_.swap(contents->disabled_origins);
    400 
    401   origin_by_resource_id_.clear();
    402   InsertReverseMap(incremental_sync_origins_, &origin_by_resource_id_);
    403   InsertReverseMap(disabled_origins_, &origin_by_resource_id_);
    404 
    405   callback.Run(db_status_, contents->created);
    406 }
    407 
    408 leveldb::DB* DriveMetadataStore::GetDBInstanceForTesting() {
    409   return db_.get();
    410 }
    411 
    412 void DriveMetadataStore::SetLargestChangeStamp(
    413     int64 largest_changestamp,
    414     const SyncStatusCallback& callback) {
    415   DCHECK(CalledOnValidThread());
    416   DCHECK_EQ(SYNC_STATUS_OK, db_status_);
    417   largest_changestamp_ = largest_changestamp;
    418 
    419   scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
    420   batch->Put(kChangeStampKey, base::Int64ToString(largest_changestamp));
    421   return WriteToDB(batch.Pass(), callback);
    422 }
    423 
    424 int64 DriveMetadataStore::GetLargestChangeStamp() const {
    425   DCHECK(CalledOnValidThread());
    426   DCHECK_EQ(SYNC_STATUS_OK, db_status_);
    427   return largest_changestamp_;
    428 }
    429 
    430 void DriveMetadataStore::UpdateEntry(
    431     const FileSystemURL& url,
    432     const DriveMetadata& metadata,
    433     const SyncStatusCallback& callback) {
    434   DCHECK(CalledOnValidThread());
    435   DCHECK_EQ(SYNC_STATUS_OK, db_status_);
    436   DCHECK(!metadata.conflicted() || !metadata.to_be_fetched());
    437 
    438   std::pair<PathToMetadata::iterator, bool> result =
    439       metadata_map_[url.origin()].insert(std::make_pair(url.path(), metadata));
    440   if (!result.second)
    441     result.first->second = metadata;
    442 
    443   std::string value;
    444   if (IsDriveAPIDisabled()) {
    445     DriveMetadata metadata_in_db(metadata);
    446     metadata_in_db.set_resource_id(
    447         drive_backend::RemoveWapiIdPrefix(metadata.resource_id()));
    448     bool success = metadata_in_db.SerializeToString(&value);
    449     DCHECK(success);
    450   } else {
    451     bool success = metadata.SerializeToString(&value);
    452     DCHECK(success);
    453   }
    454 
    455   scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
    456   batch->Put(FileSystemURLToMetadataKey(url), value);
    457   WriteToDB(batch.Pass(), callback);
    458 }
    459 
    460 void DriveMetadataStore::DeleteEntry(
    461     const FileSystemURL& url,
    462     const SyncStatusCallback& callback) {
    463   DCHECK(CalledOnValidThread());
    464   MetadataMap::iterator found = metadata_map_.find(url.origin());
    465   if (found == metadata_map_.end()) {
    466     base::MessageLoopProxy::current()->PostTask(
    467         FROM_HERE,
    468         base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND));
    469     return;
    470   }
    471 
    472   if (found->second.erase(url.path()) == 1) {
    473     scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
    474     batch->Delete(FileSystemURLToMetadataKey(url));
    475     WriteToDB(batch.Pass(), callback);
    476     return;
    477   }
    478 
    479   base::MessageLoopProxy::current()->PostTask(
    480       FROM_HERE,
    481       base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND));
    482 }
    483 
    484 SyncStatusCode DriveMetadataStore::ReadEntry(const FileSystemURL& url,
    485                                              DriveMetadata* metadata) const {
    486   DCHECK(CalledOnValidThread());
    487   DCHECK(metadata);
    488 
    489   MetadataMap::const_iterator found_origin = metadata_map_.find(url.origin());
    490   if (found_origin == metadata_map_.end())
    491     return SYNC_DATABASE_ERROR_NOT_FOUND;
    492 
    493   PathToMetadata::const_iterator found = found_origin->second.find(url.path());
    494   if (found == found_origin->second.end())
    495     return SYNC_DATABASE_ERROR_NOT_FOUND;
    496 
    497   *metadata = found->second;
    498   return SYNC_STATUS_OK;
    499 }
    500 
    501 void DriveMetadataStore::AddIncrementalSyncOrigin(
    502     const GURL& origin,
    503     const std::string& resource_id) {
    504   DCHECK(CalledOnValidThread());
    505   DCHECK(!IsIncrementalSyncOrigin(origin));
    506   DCHECK(!IsOriginDisabled(origin));
    507   DCHECK_EQ(SYNC_STATUS_OK, db_status_);
    508 
    509   incremental_sync_origins_.insert(std::make_pair(origin, resource_id));
    510   origin_by_resource_id_.insert(std::make_pair(resource_id, origin));
    511 
    512   scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
    513   batch->Delete(CreateKeyForOriginRoot(origin, DISABLED_ORIGIN));
    514   batch->Put(CreateKeyForOriginRoot(origin, INCREMENTAL_SYNC_ORIGIN),
    515              drive_backend::RemoveWapiIdPrefix(resource_id));
    516   WriteToDB(batch.Pass(),
    517             base::Bind(&DriveMetadataStore::UpdateDBStatus, AsWeakPtr()));
    518 }
    519 
    520 void DriveMetadataStore::SetSyncRootDirectory(const std::string& resource_id) {
    521   DCHECK(CalledOnValidThread());
    522 
    523   sync_root_directory_resource_id_ = resource_id;
    524 
    525   scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
    526   batch->Put(kSyncRootDirectoryKey,
    527              drive_backend::RemoveWapiIdPrefix(resource_id));
    528   return WriteToDB(batch.Pass(),
    529                    base::Bind(&DriveMetadataStore::UpdateDBStatus,
    530                               AsWeakPtr()));
    531 }
    532 
    533 void DriveMetadataStore::SetOriginRootDirectory(
    534     const GURL& origin,
    535     const std::string& resource_id) {
    536   DCHECK(CalledOnValidThread());
    537   DCHECK(IsKnownOrigin(origin));
    538 
    539   OriginSyncType sync_type;
    540   if (UpdateResourceIdMap(
    541       &incremental_sync_origins_, &origin_by_resource_id_,
    542       origin, resource_id)) {
    543     sync_type = INCREMENTAL_SYNC_ORIGIN;
    544   } else if (UpdateResourceIdMap(&disabled_origins_, &origin_by_resource_id_,
    545                                  origin, resource_id)) {
    546     sync_type = DISABLED_ORIGIN;
    547   } else {
    548     return;
    549   }
    550 
    551   std::string key = CreateKeyForOriginRoot(origin, sync_type);
    552   DCHECK(!key.empty());
    553 
    554   scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
    555   batch->Put(key, drive_backend::RemoveWapiIdPrefix(resource_id));
    556   WriteToDB(batch.Pass(),
    557             base::Bind(&DriveMetadataStore::UpdateDBStatus, AsWeakPtr()));
    558 }
    559 
    560 bool DriveMetadataStore::IsKnownOrigin(const GURL& origin) const {
    561   DCHECK(CalledOnValidThread());
    562   return IsIncrementalSyncOrigin(origin) || IsOriginDisabled(origin);
    563 }
    564 
    565 bool DriveMetadataStore::IsIncrementalSyncOrigin(const GURL& origin) const {
    566   DCHECK(CalledOnValidThread());
    567   return ContainsKey(incremental_sync_origins_, origin);
    568 }
    569 
    570 bool DriveMetadataStore::IsOriginDisabled(const GURL& origin) const {
    571   DCHECK(CalledOnValidThread());
    572   return ContainsKey(disabled_origins_, origin);
    573 }
    574 
    575 void DriveMetadataStore::EnableOrigin(
    576     const GURL& origin,
    577     const SyncStatusCallback& callback) {
    578   DCHECK(CalledOnValidThread());
    579 
    580   std::map<GURL, std::string>::iterator found = disabled_origins_.find(origin);
    581   if (found == disabled_origins_.end()) {
    582     // |origin| has not been registered yet.
    583     return;
    584   }
    585   std::string resource_id = found->second;
    586   disabled_origins_.erase(found);
    587 
    588   // |origin| goes back to DriveFileSyncService::pending_batch_sync_origins_
    589   // only and is not stored in drive_metadata_store.
    590   found = incremental_sync_origins_.find(origin);
    591   if (found != incremental_sync_origins_.end())
    592     incremental_sync_origins_.erase(found);
    593 
    594   scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
    595   batch->Delete(CreateKeyForOriginRoot(origin, INCREMENTAL_SYNC_ORIGIN));
    596   batch->Delete(CreateKeyForOriginRoot(origin, DISABLED_ORIGIN));
    597   WriteToDB(batch.Pass(), callback);
    598 }
    599 
    600 void DriveMetadataStore::DisableOrigin(
    601     const GURL& origin,
    602     const SyncStatusCallback& callback) {
    603   DCHECK(CalledOnValidThread());
    604 
    605   std::string resource_id;
    606   if (!EraseIfExists(&incremental_sync_origins_, origin, &resource_id))
    607     return;
    608   disabled_origins_[origin] = resource_id;
    609 
    610   scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
    611   batch->Delete(CreateKeyForOriginRoot(origin, INCREMENTAL_SYNC_ORIGIN));
    612   batch->Put(CreateKeyForOriginRoot(origin, DISABLED_ORIGIN),
    613              drive_backend::RemoveWapiIdPrefix(resource_id));
    614   AppendMetadataDeletionToBatch(metadata_map_, origin, batch.get());
    615   metadata_map_.erase(origin);
    616 
    617   WriteToDB(batch.Pass(), callback);
    618 }
    619 
    620 void DriveMetadataStore::RemoveOrigin(
    621     const GURL& origin,
    622     const SyncStatusCallback& callback) {
    623   DCHECK(CalledOnValidThread());
    624 
    625   std::string resource_id;
    626   if (!EraseIfExists(&incremental_sync_origins_, origin, &resource_id) &&
    627       !EraseIfExists(&disabled_origins_, origin, &resource_id))
    628     return;
    629   origin_by_resource_id_.erase(resource_id);
    630 
    631   scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
    632   batch->Delete(CreateKeyForOriginRoot(origin, INCREMENTAL_SYNC_ORIGIN));
    633   batch->Delete(CreateKeyForOriginRoot(origin, DISABLED_ORIGIN));
    634   AppendMetadataDeletionToBatch(metadata_map_, origin, batch.get());
    635   metadata_map_.erase(origin);
    636 
    637   WriteToDB(batch.Pass(), callback);
    638 }
    639 
    640 void DriveMetadataStore::DidUpdateOrigin(
    641     const SyncStatusCallback& callback,
    642     SyncStatusCode status) {
    643   UpdateDBStatus(status);
    644   callback.Run(status);
    645 }
    646 
    647 void DriveMetadataStore::WriteToDB(scoped_ptr<leveldb::WriteBatch> batch,
    648                                    const SyncStatusCallback& callback) {
    649   base::PostTaskAndReplyWithResult(
    650       file_task_runner_.get(),
    651       FROM_HERE,
    652       base::Bind(&leveldb::DB::Write,
    653                  base::Unretained(db_.get()),
    654                  leveldb::WriteOptions(),
    655                  base::Owned(batch.release())),
    656       base::Bind(&DriveMetadataStore::UpdateDBStatusAndInvokeCallback,
    657                  AsWeakPtr(),
    658                  callback));
    659 }
    660 
    661 void DriveMetadataStore::UpdateDBStatus(SyncStatusCode status) {
    662   DCHECK(CalledOnValidThread());
    663   if (db_status_ != SYNC_STATUS_OK &&
    664       db_status_ != SYNC_DATABASE_ERROR_NOT_FOUND) {
    665     // TODO(tzik): Handle database corruption. http://crbug.com/153709
    666     db_status_ = status;
    667     util::Log(logging::LOG_WARNING,
    668               FROM_HERE,
    669               "DriveMetadataStore turned to wrong state: %s",
    670               SyncStatusCodeToString(status));
    671     return;
    672   }
    673   db_status_ = SYNC_STATUS_OK;
    674 }
    675 
    676 void DriveMetadataStore::UpdateDBStatusAndInvokeCallback(
    677     const SyncStatusCallback& callback,
    678     const leveldb::Status& leveldb_status) {
    679   SyncStatusCode status = LevelDBStatusToSyncStatusCode(leveldb_status);
    680   UpdateDBStatus(status);
    681   callback.Run(status);
    682 }
    683 
    684 SyncStatusCode DriveMetadataStore::GetConflictURLs(
    685     fileapi::FileSystemURLSet* urls) const {
    686   DCHECK(CalledOnValidThread());
    687   DCHECK_EQ(SYNC_STATUS_OK, db_status_);
    688 
    689   urls->clear();
    690   for (MetadataMap::const_iterator origin_itr = metadata_map_.begin();
    691        origin_itr != metadata_map_.end();
    692        ++origin_itr) {
    693     for (PathToMetadata::const_iterator itr = origin_itr->second.begin();
    694          itr != origin_itr->second.end();
    695          ++itr) {
    696       if (itr->second.conflicted()) {
    697         urls->insert(CreateSyncableFileSystemURL(
    698             origin_itr->first, itr->first));
    699       }
    700     }
    701   }
    702   return SYNC_STATUS_OK;
    703 }
    704 
    705 SyncStatusCode DriveMetadataStore::GetToBeFetchedFiles(
    706     URLAndDriveMetadataList* list) const {
    707   DCHECK(CalledOnValidThread());
    708   DCHECK_EQ(SYNC_STATUS_OK, db_status_);
    709 
    710   list->clear();
    711   for (MetadataMap::const_iterator origin_itr = metadata_map_.begin();
    712        origin_itr != metadata_map_.end();
    713        ++origin_itr) {
    714     for (PathToMetadata::const_iterator itr = origin_itr->second.begin();
    715          itr != origin_itr->second.end();
    716          ++itr) {
    717       if (itr->second.to_be_fetched()) {
    718         FileSystemURL url = CreateSyncableFileSystemURL(
    719             origin_itr->first, itr->first);
    720         list->push_back(std::make_pair(url, itr->second));
    721       }
    722     }
    723   }
    724   return SYNC_STATUS_OK;
    725 }
    726 
    727 std::string DriveMetadataStore::GetResourceIdForOrigin(
    728     const GURL& origin) const {
    729   DCHECK(CalledOnValidThread());
    730 
    731   // If we don't have valid root directory (this could be reset even after
    732   // initialized) just return empty string, as the origin directories
    733   // in the root directory must have become invalid now too.
    734   if (sync_root_directory().empty())
    735     return std::string();
    736 
    737   ResourceIdByOrigin::const_iterator found =
    738       incremental_sync_origins_.find(origin);
    739   if (found != incremental_sync_origins_.end())
    740     return found->second;
    741 
    742   found = disabled_origins_.find(origin);
    743   if (found != disabled_origins_.end())
    744     return found->second;
    745 
    746   return std::string();
    747 }
    748 
    749 void DriveMetadataStore::GetAllOrigins(std::vector<GURL>* origins) {
    750   DCHECK(CalledOnValidThread());
    751   DCHECK(origins);
    752   origins->clear();
    753   origins->reserve(incremental_sync_origins_.size() +
    754                    disabled_origins_.size());
    755   AddOriginsToVector(origins, incremental_sync_origins_);
    756   AddOriginsToVector(origins, disabled_origins_);
    757 }
    758 
    759 bool DriveMetadataStore::GetOriginByOriginRootDirectoryId(
    760     const std::string& resource_id,
    761     GURL* origin) {
    762   DCHECK(CalledOnValidThread());
    763   DCHECK(origin);
    764 
    765   OriginByResourceId::iterator found = origin_by_resource_id_.find(resource_id);
    766   if (found == origin_by_resource_id_.end())
    767     return false;
    768   *origin = found->second;
    769   return true;
    770 }
    771 
    772 scoped_ptr<base::ListValue> DriveMetadataStore::DumpFiles(const GURL& origin) {
    773   DCHECK(CalledOnValidThread());
    774 
    775   scoped_ptr<base::ListValue> files(new base::ListValue);
    776 
    777   MetadataMap::const_iterator found = metadata_map_.find(origin);
    778   if (found == metadata_map_.end())
    779     return make_scoped_ptr(new base::ListValue);
    780 
    781   for (PathToMetadata::const_iterator itr = found->second.begin();
    782        itr != found->second.end();
    783        ++itr) {
    784     // Convert Drive specific metadata to Common File metadata object.
    785     const DriveMetadata& metadata = itr->second;
    786 
    787     base::DictionaryValue* file = new DictionaryValue;
    788     file->SetString("path", itr->first.AsUTF8Unsafe());
    789     file->SetString("title", itr->first.BaseName().AsUTF8Unsafe());
    790     file->SetString("type", DriveTypeToString(metadata.type()));
    791 
    792     base::DictionaryValue* details = new DictionaryValue;
    793     details->SetString("resource_id", metadata.resource_id());
    794     details->SetString("md5", metadata.md5_checksum());
    795     details->SetString("dirty", metadata.to_be_fetched() ? "true" : "false");
    796 
    797     file->Set("details", details);
    798     files->Append(file);
    799   }
    800 
    801   return files.Pass();
    802 }
    803 
    804 }  // namespace sync_file_system
    805