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/metadata_database.h"
      6 
      7 #include <algorithm>
      8 #include <stack>
      9 
     10 #include "base/bind.h"
     11 #include "base/command_line.h"
     12 #include "base/files/file_path.h"
     13 #include "base/files/file_util.h"
     14 #include "base/location.h"
     15 #include "base/memory/scoped_vector.h"
     16 #include "base/single_thread_task_runner.h"
     17 #include "base/stl_util.h"
     18 #include "base/strings/string_number_conversions.h"
     19 #include "base/strings/string_util.h"
     20 #include "base/strings/stringprintf.h"
     21 #include "base/task_runner_util.h"
     22 #include "base/thread_task_runner_handle.h"
     23 #include "base/threading/thread_restrictions.h"
     24 #include "chrome/browser/drive/drive_api_util.h"
     25 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h"
     26 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_util.h"
     27 #include "chrome/browser/sync_file_system/drive_backend/leveldb_wrapper.h"
     28 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.pb.h"
     29 #include "chrome/browser/sync_file_system/drive_backend/metadata_database_index.h"
     30 #include "chrome/browser/sync_file_system/drive_backend/metadata_database_index_interface.h"
     31 #include "chrome/browser/sync_file_system/drive_backend/metadata_database_index_on_disk.h"
     32 #include "chrome/browser/sync_file_system/drive_backend/metadata_db_migration_util.h"
     33 #include "chrome/browser/sync_file_system/logger.h"
     34 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
     35 #include "google_apis/drive/drive_api_parser.h"
     36 #include "storage/common/fileapi/file_system_util.h"
     37 #include "third_party/leveldatabase/src/include/leveldb/db.h"
     38 #include "third_party/leveldatabase/src/include/leveldb/env.h"
     39 #include "third_party/leveldatabase/src/include/leveldb/status.h"
     40 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
     41 
     42 namespace sync_file_system {
     43 namespace drive_backend {
     44 
     45 namespace {
     46 
     47 // Command line flag to disable on-disk indexing.
     48 const char kDisableMetadataDatabaseOnDisk[] = "disable-syncfs-on-disk-indexing";
     49 
     50 std::string FileKindToString(FileKind file_kind) {
     51   switch (file_kind) {
     52     case FILE_KIND_UNSUPPORTED:
     53       return "unsupported";
     54     case FILE_KIND_FILE:
     55       return "file";
     56     case FILE_KIND_FOLDER:
     57       return "folder";
     58   }
     59 
     60   NOTREACHED();
     61   return "unknown";
     62 }
     63 
     64 base::FilePath ReverseConcatPathComponents(
     65     const std::vector<base::FilePath>& components) {
     66   if (components.empty())
     67     return base::FilePath(FILE_PATH_LITERAL("/")).NormalizePathSeparators();
     68 
     69   size_t total_size = 0;
     70   typedef std::vector<base::FilePath> PathComponents;
     71   for (PathComponents::const_iterator itr = components.begin();
     72        itr != components.end(); ++itr)
     73     total_size += itr->value().size() + 1;
     74 
     75   base::FilePath::StringType result;
     76   result.reserve(total_size);
     77   for (PathComponents::const_reverse_iterator itr = components.rbegin();
     78        itr != components.rend(); ++itr) {
     79     result.append(1, base::FilePath::kSeparators[0]);
     80     result.append(itr->value());
     81   }
     82 
     83   return base::FilePath(result).NormalizePathSeparators();
     84 }
     85 
     86 void PopulateFileDetailsByFileResource(
     87     const google_apis::FileResource& file_resource,
     88     FileDetails* details) {
     89   details->clear_parent_folder_ids();
     90   for (std::vector<google_apis::ParentReference>::const_iterator itr =
     91            file_resource.parents().begin();
     92        itr != file_resource.parents().end();
     93        ++itr) {
     94     details->add_parent_folder_ids(itr->file_id());
     95   }
     96   details->set_title(file_resource.title());
     97 
     98   if (file_resource.IsDirectory())
     99     details->set_file_kind(FILE_KIND_FOLDER);
    100   else if (file_resource.IsHostedDocument())
    101     details->set_file_kind(FILE_KIND_UNSUPPORTED);
    102   else
    103     details->set_file_kind(FILE_KIND_FILE);
    104 
    105   details->set_md5(file_resource.md5_checksum());
    106   details->set_etag(file_resource.etag());
    107   details->set_creation_time(file_resource.created_date().ToInternalValue());
    108   details->set_modification_time(
    109       file_resource.modified_date().ToInternalValue());
    110   details->set_missing(file_resource.labels().is_trashed());
    111 }
    112 
    113 scoped_ptr<FileMetadata> CreateFileMetadataFromFileResource(
    114     int64 change_id,
    115     const google_apis::FileResource& resource) {
    116   scoped_ptr<FileMetadata> file(new FileMetadata);
    117   file->set_file_id(resource.file_id());
    118 
    119   FileDetails* details = file->mutable_details();
    120   details->set_change_id(change_id);
    121 
    122   if (resource.labels().is_trashed()) {
    123     details->set_missing(true);
    124     return file.Pass();
    125   }
    126 
    127   PopulateFileDetailsByFileResource(resource, details);
    128   return file.Pass();
    129 }
    130 
    131 scoped_ptr<FileMetadata> CreateFileMetadataFromChangeResource(
    132     const google_apis::ChangeResource& change) {
    133   scoped_ptr<FileMetadata> file(new FileMetadata);
    134   file->set_file_id(change.file_id());
    135 
    136   FileDetails* details = file->mutable_details();
    137   details->set_change_id(change.change_id());
    138 
    139   if (change.is_deleted()) {
    140     details->set_missing(true);
    141     return file.Pass();
    142   }
    143 
    144   PopulateFileDetailsByFileResource(*change.file(), details);
    145   return file.Pass();
    146 }
    147 
    148 scoped_ptr<FileMetadata> CreateDeletedFileMetadata(
    149     int64 change_id,
    150     const std::string& file_id) {
    151   scoped_ptr<FileMetadata> file(new FileMetadata);
    152   file->set_file_id(file_id);
    153 
    154   FileDetails* details = file->mutable_details();
    155   details->set_change_id(change_id);
    156   details->set_missing(true);
    157   return file.Pass();
    158 }
    159 
    160 scoped_ptr<FileTracker> CreateSyncRootTracker(
    161     int64 tracker_id,
    162     const FileMetadata& sync_root_metadata) {
    163   scoped_ptr<FileTracker> sync_root_tracker(new FileTracker);
    164   sync_root_tracker->set_tracker_id(tracker_id);
    165   sync_root_tracker->set_file_id(sync_root_metadata.file_id());
    166   sync_root_tracker->set_parent_tracker_id(0);
    167   sync_root_tracker->set_tracker_kind(TRACKER_KIND_REGULAR);
    168   sync_root_tracker->set_dirty(false);
    169   sync_root_tracker->set_active(true);
    170   sync_root_tracker->set_needs_folder_listing(false);
    171   *sync_root_tracker->mutable_synced_details() = sync_root_metadata.details();
    172   return sync_root_tracker.Pass();
    173 }
    174 
    175 scoped_ptr<FileTracker> CreateInitialAppRootTracker(
    176     int64 tracker_id,
    177     int64 parent_tracker_id,
    178     const FileMetadata& app_root_metadata) {
    179   scoped_ptr<FileTracker> app_root_tracker(new FileTracker);
    180   app_root_tracker->set_tracker_id(tracker_id);
    181   app_root_tracker->set_parent_tracker_id(parent_tracker_id);
    182   app_root_tracker->set_file_id(app_root_metadata.file_id());
    183   app_root_tracker->set_tracker_kind(TRACKER_KIND_REGULAR);
    184   app_root_tracker->set_dirty(false);
    185   app_root_tracker->set_active(false);
    186   app_root_tracker->set_needs_folder_listing(false);
    187   *app_root_tracker->mutable_synced_details() = app_root_metadata.details();
    188   return app_root_tracker.Pass();
    189 }
    190 
    191 scoped_ptr<FileTracker> CloneFileTracker(const FileTracker* obj) {
    192   if (!obj)
    193     return scoped_ptr<FileTracker>();
    194   return scoped_ptr<FileTracker>(new FileTracker(*obj));
    195 }
    196 
    197 // Returns true if |db| has no content.
    198 bool IsDatabaseEmpty(LevelDBWrapper* db) {
    199   DCHECK(db);
    200   scoped_ptr<LevelDBWrapper::Iterator> itr(db->NewIterator());
    201   itr->SeekToFirst();
    202   return !itr->Valid();
    203 }
    204 
    205 SyncStatusCode OpenDatabase(const base::FilePath& path,
    206                             leveldb::Env* env_override,
    207                             scoped_ptr<LevelDBWrapper>* db_out,
    208                             bool* created) {
    209   base::ThreadRestrictions::AssertIOAllowed();
    210   DCHECK(db_out);
    211   DCHECK(created);
    212   DCHECK(path.IsAbsolute());
    213 
    214   leveldb::Options options;
    215   options.max_open_files = 0;  // Use minimum.
    216   options.create_if_missing = true;
    217   if (env_override)
    218     options.env = env_override;
    219   leveldb::DB* db = NULL;
    220   leveldb::Status db_status =
    221       leveldb::DB::Open(options, path.AsUTF8Unsafe(), &db);
    222   SyncStatusCode status = LevelDBStatusToSyncStatusCode(db_status);
    223   if (status != SYNC_STATUS_OK) {
    224     delete db;
    225     return status;
    226   }
    227 
    228   db_out->reset(new LevelDBWrapper(make_scoped_ptr(db)));
    229   *created = IsDatabaseEmpty(db_out->get());
    230   return status;
    231 }
    232 
    233 SyncStatusCode MigrateDatabaseIfNeeded(LevelDBWrapper* db) {
    234   // See metadata_database_index.cc for the database schema.
    235   base::ThreadRestrictions::AssertIOAllowed();
    236   DCHECK(db);
    237   std::string value;
    238   leveldb::Status status = db->Get(kDatabaseVersionKey, &value);
    239   int64 version = 0;
    240   if (status.ok()) {
    241     if (!base::StringToInt64(value, &version))
    242       return SYNC_DATABASE_ERROR_FAILED;
    243   } else {
    244     if (!status.IsNotFound())
    245       return SYNC_DATABASE_ERROR_FAILED;
    246   }
    247 
    248   switch (version) {
    249     case 0:
    250     case 1:
    251     case 2:
    252       // Drop all data in old database and refetch them from the remote service.
    253       NOTREACHED();
    254       return SYNC_DATABASE_ERROR_FAILED;
    255     case 3:
    256       DCHECK_EQ(3, kCurrentDatabaseVersion);
    257       // If MetadataDatabaseOnDisk is enabled, migration will be done in
    258       // MetadataDatabaseOnDisk::Create().
    259       // TODO(peria): Move the migration code (from v3 to v4) here.
    260       return SYNC_STATUS_OK;
    261     case 4:
    262       if (CommandLine::ForCurrentProcess()->HasSwitch(
    263               kDisableMetadataDatabaseOnDisk)) {
    264         MigrateDatabaseFromV4ToV3(db->GetLevelDB());
    265       }
    266       return SYNC_STATUS_OK;
    267     default:
    268       return SYNC_DATABASE_ERROR_FAILED;
    269   }
    270 }
    271 
    272 bool HasInvalidTitle(const std::string& title) {
    273   return title.empty() ||
    274       title.find('/') != std::string::npos ||
    275       title.find('\\') != std::string::npos;
    276 }
    277 
    278 void MarkTrackerSetDirty(const TrackerIDSet& trackers,
    279                          MetadataDatabaseIndexInterface* index) {
    280   for (TrackerIDSet::const_iterator itr = trackers.begin();
    281        itr != trackers.end(); ++itr) {
    282     scoped_ptr<FileTracker> tracker(new FileTracker);
    283     index->GetFileTracker(*itr, tracker.get());
    284     if (tracker->dirty())
    285       continue;
    286     tracker->set_dirty(true);
    287     index->StoreFileTracker(tracker.Pass());
    288   }
    289 }
    290 
    291 void MarkTrackersDirtyByPath(int64 parent_tracker_id,
    292                              const std::string& title,
    293                              MetadataDatabaseIndexInterface* index) {
    294   if (parent_tracker_id == kInvalidTrackerID || title.empty())
    295     return;
    296   MarkTrackerSetDirty(
    297       index->GetFileTrackerIDsByParentAndTitle(parent_tracker_id, title),
    298       index);
    299 }
    300 
    301 void MarkTrackersDirtyByFileID(const std::string& file_id,
    302                                MetadataDatabaseIndexInterface* index) {
    303   MarkTrackerSetDirty(index->GetFileTrackerIDsByFileID(file_id), index);
    304 }
    305 
    306 void MarkTrackersDirtyRecursively(int64 root_tracker_id,
    307                                   MetadataDatabaseIndexInterface* index) {
    308   std::vector<int64> stack;
    309   stack.push_back(root_tracker_id);
    310   while (!stack.empty()) {
    311     int64 tracker_id = stack.back();
    312     stack.pop_back();
    313     AppendContents(index->GetFileTrackerIDsByParent(tracker_id), &stack);
    314 
    315     scoped_ptr<FileTracker> tracker(new FileTracker);
    316     index->GetFileTracker(tracker_id, tracker.get());
    317     tracker->set_dirty(true);
    318 
    319     index->StoreFileTracker(tracker.Pass());
    320   }
    321 }
    322 
    323 void RemoveAllDescendantTrackers(int64 root_tracker_id,
    324                                  MetadataDatabaseIndexInterface* index) {
    325   std::vector<int64> pending_trackers;
    326   AppendContents(index->GetFileTrackerIDsByParent(root_tracker_id),
    327                  &pending_trackers);
    328 
    329   std::vector<int64> to_be_removed;
    330 
    331   // List trackers to remove.
    332   while (!pending_trackers.empty()) {
    333     int64 tracker_id = pending_trackers.back();
    334     pending_trackers.pop_back();
    335     AppendContents(index->GetFileTrackerIDsByParent(tracker_id),
    336                    &pending_trackers);
    337     to_be_removed.push_back(tracker_id);
    338   }
    339 
    340   // Remove trackers in the reversed order.
    341   base::hash_set<std::string> affected_file_ids;
    342   for (std::vector<int64>::reverse_iterator itr = to_be_removed.rbegin();
    343        itr != to_be_removed.rend(); ++itr) {
    344     FileTracker tracker;
    345     index->GetFileTracker(*itr, &tracker);
    346     affected_file_ids.insert(tracker.file_id());
    347     index->RemoveFileTracker(*itr);
    348   }
    349 
    350   for (base::hash_set<std::string>::iterator itr = affected_file_ids.begin();
    351        itr != affected_file_ids.end(); ++itr) {
    352     TrackerIDSet trackers = index->GetFileTrackerIDsByFileID(*itr);
    353     if (trackers.empty()) {
    354       // Remove metadata that no longer has any tracker.
    355       index->RemoveFileMetadata(*itr);
    356     } else {
    357       MarkTrackerSetDirty(trackers, index);
    358     }
    359   }
    360 }
    361 
    362 bool FilterFileTrackersByParent(
    363     const MetadataDatabaseIndexInterface* index,
    364     const TrackerIDSet& trackers,
    365     int64 parent_tracker_id,
    366     FileTracker* tracker_out) {
    367   FileTracker tracker;
    368   for (TrackerIDSet::const_iterator itr = trackers.begin();
    369        itr != trackers.end(); ++itr) {
    370     if (!index->GetFileTracker(*itr, &tracker)) {
    371       NOTREACHED();
    372       continue;
    373     }
    374 
    375     if (tracker.parent_tracker_id() == parent_tracker_id) {
    376       if (tracker_out)
    377         tracker_out->CopyFrom(tracker);
    378       return true;
    379     }
    380   }
    381   return false;
    382 }
    383 
    384 bool FilterFileTrackersByParentAndTitle(
    385     const MetadataDatabaseIndexInterface* index,
    386     const TrackerIDSet& trackers,
    387     int64 parent_tracker_id,
    388     const std::string& title,
    389     FileTracker* result) {
    390   bool found = false;
    391   for (TrackerIDSet::const_iterator itr = trackers.begin();
    392        itr != trackers.end(); ++itr) {
    393     FileTracker tracker;
    394     if (!index->GetFileTracker(*itr, &tracker)) {
    395       NOTREACHED();
    396       continue;
    397     }
    398 
    399     if (tracker.parent_tracker_id() != parent_tracker_id)
    400       continue;
    401 
    402     if (tracker.has_synced_details() &&
    403         tracker.synced_details().title() != title)
    404       continue;
    405 
    406     // Prioritize trackers that has |synced_details| when |trackers| has
    407     // multiple candidates.
    408     if (!found || tracker.has_synced_details()) {
    409       found = true;
    410       if (result)
    411         result->CopyFrom(tracker);
    412       if (!result || result->has_synced_details())
    413         return found;
    414     }
    415   }
    416 
    417   return found;
    418 }
    419 
    420 bool FilterFileTrackersByFileID(
    421     const MetadataDatabaseIndexInterface* index,
    422     const TrackerIDSet& trackers,
    423     const std::string& file_id,
    424     FileTracker* tracker_out) {
    425   FileTracker tracker;
    426   for (TrackerIDSet::const_iterator itr = trackers.begin();
    427        itr != trackers.end(); ++itr) {
    428     if (!index->GetFileTracker(*itr, &tracker)) {
    429       NOTREACHED();
    430       continue;
    431     }
    432 
    433     if (tracker.file_id() == file_id) {
    434       if (tracker_out)
    435         tracker_out->CopyFrom(tracker);
    436       return true;
    437     }
    438   }
    439   return false;
    440 }
    441 
    442 enum DirtyingOption {
    443   MARK_NOTHING_DIRTY = 0,
    444   MARK_ITSELF_DIRTY = 1 << 0,
    445   MARK_SAME_FILE_ID_TRACKERS_DIRTY = 1 << 1,
    446   MARK_SAME_PATH_TRACKERS_DIRTY = 1 << 2,
    447 };
    448 
    449 void ActivateFileTracker(int64 tracker_id,
    450                          int dirtying_options,
    451                          MetadataDatabaseIndexInterface* index) {
    452   DCHECK(dirtying_options == MARK_NOTHING_DIRTY ||
    453          dirtying_options == MARK_ITSELF_DIRTY);
    454 
    455   scoped_ptr<FileTracker> tracker(new FileTracker);
    456   index->GetFileTracker(tracker_id, tracker.get());
    457   tracker->set_active(true);
    458   if (dirtying_options & MARK_ITSELF_DIRTY) {
    459     tracker->set_dirty(true);
    460     tracker->set_needs_folder_listing(
    461         tracker->has_synced_details() &&
    462         tracker->synced_details().file_kind() == FILE_KIND_FOLDER);
    463   } else {
    464     tracker->set_dirty(false);
    465     tracker->set_needs_folder_listing(false);
    466   }
    467 
    468   index->StoreFileTracker(tracker.Pass());
    469 }
    470 
    471 void DeactivateFileTracker(int64 tracker_id,
    472                            int dirtying_options,
    473                            MetadataDatabaseIndexInterface* index) {
    474   RemoveAllDescendantTrackers(tracker_id, index);
    475 
    476   scoped_ptr<FileTracker> tracker(new FileTracker);
    477   index->GetFileTracker(tracker_id, tracker.get());
    478 
    479   if (dirtying_options & MARK_SAME_FILE_ID_TRACKERS_DIRTY)
    480     MarkTrackersDirtyByFileID(tracker->file_id(), index);
    481   if (dirtying_options & MARK_SAME_PATH_TRACKERS_DIRTY) {
    482     MarkTrackersDirtyByPath(tracker->parent_tracker_id(),
    483                             GetTrackerTitle(*tracker), index);
    484   }
    485 
    486   tracker->set_dirty(dirtying_options & MARK_ITSELF_DIRTY);
    487   tracker->set_active(false);
    488   index->StoreFileTracker(tracker.Pass());
    489 }
    490 
    491 void RemoveFileTracker(int64 tracker_id,
    492                        int dirtying_options,
    493                        MetadataDatabaseIndexInterface* index) {
    494   DCHECK(!(dirtying_options & MARK_ITSELF_DIRTY));
    495 
    496   FileTracker tracker;
    497   if (!index->GetFileTracker(tracker_id, &tracker))
    498     return;
    499 
    500   std::string file_id = tracker.file_id();
    501   int64 parent_tracker_id = tracker.parent_tracker_id();
    502   std::string title = GetTrackerTitle(tracker);
    503 
    504   RemoveAllDescendantTrackers(tracker_id, index);
    505   index->RemoveFileTracker(tracker_id);
    506 
    507   if (dirtying_options & MARK_SAME_FILE_ID_TRACKERS_DIRTY)
    508     MarkTrackersDirtyByFileID(file_id, index);
    509   if (dirtying_options & MARK_SAME_PATH_TRACKERS_DIRTY)
    510     MarkTrackersDirtyByPath(parent_tracker_id, title, index);
    511 
    512   if (index->GetFileTrackerIDsByFileID(file_id).empty()) {
    513     index->RemoveFileMetadata(file_id);
    514   }
    515 }
    516 
    517 }  // namespace
    518 
    519 // static
    520 scoped_ptr<MetadataDatabase> MetadataDatabase::Create(
    521     const base::FilePath& database_path,
    522     leveldb::Env* env_override,
    523     SyncStatusCode* status_out) {
    524   bool enable_on_disk_index = !CommandLine::ForCurrentProcess()->HasSwitch(
    525       kDisableMetadataDatabaseOnDisk);
    526   return CreateInternal(database_path, env_override, enable_on_disk_index,
    527                         status_out);
    528 }
    529 
    530 // static
    531 scoped_ptr<MetadataDatabase> MetadataDatabase::CreateInternal(
    532     const base::FilePath& database_path,
    533     leveldb::Env* env_override,
    534     bool enable_on_disk_index,
    535     SyncStatusCode* status_out) {
    536   scoped_ptr<MetadataDatabase> metadata_database(
    537       new MetadataDatabase(database_path,
    538                            enable_on_disk_index,
    539                            env_override));
    540 
    541   SyncStatusCode status = metadata_database->Initialize();
    542   if (status == SYNC_DATABASE_ERROR_FAILED) {
    543     // Delete the previous instance to avoid creating a LevelDB instance for
    544     // the same path.
    545     metadata_database.reset();
    546 
    547     metadata_database.reset(
    548         new MetadataDatabase(database_path,
    549                              enable_on_disk_index,
    550                              env_override));
    551     status = metadata_database->Initialize();
    552   }
    553 
    554   if (status != SYNC_STATUS_OK)
    555     metadata_database.reset();
    556 
    557   *status_out = status;
    558   return metadata_database.Pass();
    559 }
    560 
    561 // static
    562 SyncStatusCode MetadataDatabase::CreateForTesting(
    563     scoped_ptr<LevelDBWrapper> db,
    564     bool enable_on_disk_index,
    565     scoped_ptr<MetadataDatabase>* metadata_database_out) {
    566   scoped_ptr<MetadataDatabase> metadata_database(
    567       new MetadataDatabase(base::FilePath(),
    568                            enable_on_disk_index,
    569                            NULL));
    570   metadata_database->db_ = db.Pass();
    571   SyncStatusCode status = metadata_database->Initialize();
    572   if (status == SYNC_STATUS_OK)
    573     *metadata_database_out = metadata_database.Pass();
    574   return status;
    575 }
    576 
    577 MetadataDatabase::~MetadataDatabase() {
    578 }
    579 
    580 // static
    581 void MetadataDatabase::ClearDatabase(
    582     scoped_ptr<MetadataDatabase> metadata_database) {
    583   DCHECK(metadata_database);
    584   base::FilePath database_path = metadata_database->database_path_;
    585   DCHECK(!database_path.empty());
    586   metadata_database.reset();
    587 
    588   base::DeleteFile(database_path, true /* recursive */);
    589 }
    590 
    591 int64 MetadataDatabase::GetLargestFetchedChangeID() const {
    592   return index_->GetLargestChangeID();
    593 }
    594 
    595 int64 MetadataDatabase::GetSyncRootTrackerID() const {
    596   return index_->GetSyncRootTrackerID();
    597 }
    598 
    599 int64 MetadataDatabase::GetLargestKnownChangeID() const {
    600   DCHECK_LE(GetLargestFetchedChangeID(), largest_known_change_id_);
    601   return largest_known_change_id_;
    602 }
    603 
    604 void MetadataDatabase::UpdateLargestKnownChangeID(int64 change_id) {
    605   if (largest_known_change_id_ < change_id)
    606     largest_known_change_id_ = change_id;
    607 }
    608 
    609 bool MetadataDatabase::HasSyncRoot() const {
    610   return index_->GetSyncRootTrackerID() != kInvalidTrackerID;
    611 }
    612 
    613 SyncStatusCode MetadataDatabase::PopulateInitialData(
    614     int64 largest_change_id,
    615     const google_apis::FileResource& sync_root_folder,
    616     const ScopedVector<google_apis::FileResource>& app_root_folders) {
    617   index_->SetLargestChangeID(largest_change_id);
    618   UpdateLargestKnownChangeID(largest_change_id);
    619 
    620   AttachSyncRoot(sync_root_folder);
    621   for (size_t i = 0; i < app_root_folders.size(); ++i)
    622     AttachInitialAppRoot(*app_root_folders[i]);
    623 
    624   return WriteToDatabase();
    625 }
    626 
    627 bool MetadataDatabase::IsAppEnabled(const std::string& app_id) const {
    628   int64 tracker_id = index_->GetAppRootTracker(app_id);
    629   if (tracker_id == kInvalidTrackerID)
    630     return false;
    631 
    632   FileTracker tracker;
    633   if (!index_->GetFileTracker(tracker_id, &tracker))
    634     return false;
    635   return tracker.tracker_kind() == TRACKER_KIND_APP_ROOT;
    636 }
    637 
    638 SyncStatusCode MetadataDatabase::RegisterApp(const std::string& app_id,
    639                                    const std::string& folder_id) {
    640   if (index_->GetAppRootTracker(app_id)) {
    641     // The app-root is already registered.
    642     return SYNC_STATUS_OK;
    643   }
    644 
    645   TrackerIDSet trackers = index_->GetFileTrackerIDsByFileID(folder_id);
    646   if (trackers.empty()) {
    647     return SYNC_DATABASE_ERROR_NOT_FOUND;
    648   }
    649 
    650   if (trackers.has_active()) {
    651     // The folder is tracked by another tracker.
    652     util::Log(logging::LOG_WARNING, FROM_HERE,
    653               "Failed to register App for %s", app_id.c_str());
    654     return SYNC_STATUS_HAS_CONFLICT;
    655   }
    656 
    657   int64 sync_root_tracker_id = index_->GetSyncRootTrackerID();
    658   if (!sync_root_tracker_id) {
    659     util::Log(logging::LOG_WARNING, FROM_HERE,
    660               "Sync-root needs to be set up before registering app-root");
    661     return SYNC_DATABASE_ERROR_NOT_FOUND;
    662   }
    663 
    664   scoped_ptr<FileTracker> tracker(new FileTracker);
    665   if (!FilterFileTrackersByParent(index_.get(), trackers,
    666                                   sync_root_tracker_id, tracker.get())) {
    667     return SYNC_DATABASE_ERROR_NOT_FOUND;
    668   }
    669 
    670   tracker->set_app_id(app_id);
    671   tracker->set_tracker_kind(TRACKER_KIND_APP_ROOT);
    672   tracker->set_active(true);
    673   tracker->set_needs_folder_listing(true);
    674   tracker->set_dirty(true);
    675 
    676   index_->StoreFileTracker(tracker.Pass());
    677   return WriteToDatabase();
    678 }
    679 
    680 SyncStatusCode MetadataDatabase::DisableApp(const std::string& app_id) {
    681   int64 tracker_id = index_->GetAppRootTracker(app_id);
    682   scoped_ptr<FileTracker> tracker(new FileTracker);
    683   if (!index_->GetFileTracker(tracker_id, tracker.get())) {
    684     return SYNC_DATABASE_ERROR_NOT_FOUND;
    685   }
    686 
    687   if (tracker->tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT) {
    688     return SYNC_STATUS_OK;
    689   }
    690 
    691   DCHECK_EQ(TRACKER_KIND_APP_ROOT, tracker->tracker_kind());
    692   DCHECK(tracker->active());
    693 
    694   // Keep the app-root tracker active (but change the tracker_kind) so that
    695   // other conflicting trackers won't become active.
    696   tracker->set_tracker_kind(TRACKER_KIND_DISABLED_APP_ROOT);
    697 
    698   index_->StoreFileTracker(tracker.Pass());
    699   return WriteToDatabase();
    700 }
    701 
    702 SyncStatusCode MetadataDatabase::EnableApp(const std::string& app_id) {
    703   int64 tracker_id = index_->GetAppRootTracker(app_id);
    704   scoped_ptr<FileTracker> tracker(new FileTracker);
    705   if (!index_->GetFileTracker(tracker_id, tracker.get())) {
    706     return SYNC_DATABASE_ERROR_NOT_FOUND;
    707   }
    708 
    709   if (tracker->tracker_kind() == TRACKER_KIND_APP_ROOT) {
    710     return SYNC_STATUS_OK;
    711   }
    712 
    713   DCHECK_EQ(TRACKER_KIND_DISABLED_APP_ROOT, tracker->tracker_kind());
    714   DCHECK(tracker->active());
    715 
    716   tracker->set_tracker_kind(TRACKER_KIND_APP_ROOT);
    717   index_->StoreFileTracker(tracker.Pass());
    718 
    719   MarkTrackersDirtyRecursively(tracker_id, index_.get());
    720   return WriteToDatabase();
    721 }
    722 
    723 SyncStatusCode MetadataDatabase::UnregisterApp(const std::string& app_id) {
    724   int64 tracker_id = index_->GetAppRootTracker(app_id);
    725   scoped_ptr<FileTracker> tracker(new FileTracker);
    726   if (!index_->GetFileTracker(tracker_id, tracker.get()) ||
    727       tracker->tracker_kind() == TRACKER_KIND_REGULAR) {
    728     return SYNC_STATUS_OK;
    729   }
    730 
    731   RemoveAllDescendantTrackers(tracker_id, index_.get());
    732 
    733   tracker->clear_app_id();
    734   tracker->set_tracker_kind(TRACKER_KIND_REGULAR);
    735   tracker->set_active(false);
    736   tracker->set_dirty(true);
    737 
    738   index_->StoreFileTracker(tracker.Pass());
    739   return WriteToDatabase();
    740 }
    741 
    742 bool MetadataDatabase::FindAppRootTracker(const std::string& app_id,
    743                                           FileTracker* tracker_out) const {
    744   int64 app_root_tracker_id = index_->GetAppRootTracker(app_id);
    745   if (!app_root_tracker_id)
    746     return false;
    747 
    748   if (tracker_out &&
    749       !index_->GetFileTracker(app_root_tracker_id, tracker_out)) {
    750     NOTREACHED();
    751     return false;
    752   }
    753 
    754   return true;
    755 }
    756 
    757 bool MetadataDatabase::FindFileByFileID(const std::string& file_id,
    758                                         FileMetadata* metadata_out) const {
    759   return index_->GetFileMetadata(file_id, metadata_out);
    760 }
    761 
    762 bool MetadataDatabase::FindTrackersByFileID(const std::string& file_id,
    763                                             TrackerIDSet* trackers_out) const {
    764   TrackerIDSet trackers = index_->GetFileTrackerIDsByFileID(file_id);
    765   if (trackers.empty())
    766     return false;
    767 
    768   if (trackers_out)
    769     std::swap(trackers, *trackers_out);
    770   return true;
    771 }
    772 
    773 bool MetadataDatabase::FindTrackersByParentAndTitle(
    774     int64 parent_tracker_id,
    775     const std::string& title,
    776     TrackerIDSet* trackers_out) const {
    777   TrackerIDSet trackers =
    778       index_->GetFileTrackerIDsByParentAndTitle(parent_tracker_id, title);
    779   if (trackers.empty())
    780     return false;
    781 
    782   if (trackers_out)
    783     std::swap(trackers, *trackers_out);
    784   return true;
    785 }
    786 
    787 bool MetadataDatabase::FindTrackerByTrackerID(int64 tracker_id,
    788                                               FileTracker* tracker_out) const {
    789   return index_->GetFileTracker(tracker_id, tracker_out);
    790 }
    791 
    792 bool MetadataDatabase::BuildPathForTracker(int64 tracker_id,
    793                                            base::FilePath* path) const {
    794   FileTracker current;
    795   if (!FindTrackerByTrackerID(tracker_id, &current) || !current.active())
    796     return false;
    797 
    798   std::vector<base::FilePath> components;
    799   while (!IsAppRoot(current)) {
    800     std::string title = GetTrackerTitle(current);
    801     if (title.empty())
    802       return false;
    803     components.push_back(base::FilePath::FromUTF8Unsafe(title));
    804     if (!FindTrackerByTrackerID(current.parent_tracker_id(), &current) ||
    805         !current.active())
    806       return false;
    807   }
    808 
    809   if (path)
    810     *path = ReverseConcatPathComponents(components);
    811 
    812   return true;
    813 }
    814 
    815 base::FilePath MetadataDatabase::BuildDisplayPathForTracker(
    816     const FileTracker& tracker) const {
    817   base::FilePath path;
    818   if (tracker.active()) {
    819     BuildPathForTracker(tracker.tracker_id(), &path);
    820     return path;
    821   }
    822   BuildPathForTracker(tracker.parent_tracker_id(), &path);
    823   if (tracker.has_synced_details()) {
    824     path = path.Append(
    825         base::FilePath::FromUTF8Unsafe(tracker.synced_details().title()));
    826   } else {
    827     path = path.Append(FILE_PATH_LITERAL("<unknown>"));
    828   }
    829   return path;
    830 }
    831 
    832 bool MetadataDatabase::FindNearestActiveAncestor(
    833     const std::string& app_id,
    834     const base::FilePath& full_path,
    835     FileTracker* tracker_out,
    836     base::FilePath* path_out) const {
    837   DCHECK(tracker_out);
    838   DCHECK(path_out);
    839 
    840   if (full_path.IsAbsolute() ||
    841       !FindAppRootTracker(app_id, tracker_out) ||
    842       tracker_out->tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT) {
    843     return false;
    844   }
    845 
    846   std::vector<base::FilePath::StringType> components;
    847   full_path.GetComponents(&components);
    848   path_out->clear();
    849 
    850   for (size_t i = 0; i < components.size(); ++i) {
    851     const std::string title = base::FilePath(components[i]).AsUTF8Unsafe();
    852     TrackerIDSet trackers;
    853     if (!FindTrackersByParentAndTitle(
    854             tracker_out->tracker_id(), title, &trackers) ||
    855         !trackers.has_active()) {
    856       return true;
    857     }
    858 
    859     FileTracker tracker;
    860     index_->GetFileTracker(trackers.active_tracker(), &tracker);
    861 
    862     DCHECK(tracker.has_synced_details());
    863     const FileDetails& details = tracker.synced_details();
    864     if (details.file_kind() != FILE_KIND_FOLDER && i != components.size() - 1) {
    865       // This non-last component indicates file. Give up search.
    866       return true;
    867     }
    868 
    869     tracker_out->CopyFrom(tracker);
    870     *path_out = path_out->Append(components[i]);
    871   }
    872 
    873   return true;
    874 }
    875 
    876 SyncStatusCode MetadataDatabase::UpdateByChangeList(
    877     int64 largest_change_id,
    878     ScopedVector<google_apis::ChangeResource> changes) {
    879   DCHECK_LE(index_->GetLargestChangeID(), largest_change_id);
    880 
    881   for (size_t i = 0; i < changes.size(); ++i) {
    882     const google_apis::ChangeResource& change = *changes[i];
    883     if (HasNewerFileMetadata(change.file_id(), change.change_id()))
    884       continue;
    885 
    886     scoped_ptr<FileMetadata> metadata(
    887         CreateFileMetadataFromChangeResource(change));
    888     UpdateByFileMetadata(FROM_HERE, metadata.Pass(),
    889                          UPDATE_TRACKER_FOR_UNSYNCED_FILE);
    890   }
    891 
    892   UpdateLargestKnownChangeID(largest_change_id);
    893   index_->SetLargestChangeID(largest_change_id);
    894   return WriteToDatabase();
    895 }
    896 
    897 SyncStatusCode MetadataDatabase::UpdateByFileResource(
    898     const google_apis::FileResource& resource) {
    899   scoped_ptr<FileMetadata> metadata(
    900       CreateFileMetadataFromFileResource(
    901           GetLargestKnownChangeID(), resource));
    902   UpdateByFileMetadata(FROM_HERE, metadata.Pass(),
    903                        UPDATE_TRACKER_FOR_UNSYNCED_FILE);
    904   return WriteToDatabase();
    905 }
    906 
    907 SyncStatusCode MetadataDatabase::UpdateByFileResourceList(
    908     ScopedVector<google_apis::FileResource> resources) {
    909   for (size_t i = 0; i < resources.size(); ++i) {
    910     scoped_ptr<FileMetadata> metadata(
    911         CreateFileMetadataFromFileResource(
    912             GetLargestKnownChangeID(), *resources[i]));
    913     UpdateByFileMetadata(FROM_HERE, metadata.Pass(),
    914                          UPDATE_TRACKER_FOR_UNSYNCED_FILE);
    915   }
    916   return WriteToDatabase();
    917 }
    918 
    919 SyncStatusCode MetadataDatabase::UpdateByDeletedRemoteFile(
    920     const std::string& file_id) {
    921   scoped_ptr<FileMetadata> metadata(
    922       CreateDeletedFileMetadata(GetLargestKnownChangeID(), file_id));
    923   UpdateByFileMetadata(FROM_HERE, metadata.Pass(),
    924                        UPDATE_TRACKER_FOR_UNSYNCED_FILE);
    925   return WriteToDatabase();
    926 }
    927 
    928 SyncStatusCode MetadataDatabase::UpdateByDeletedRemoteFileList(
    929     const FileIDList& file_ids) {
    930   for (FileIDList::const_iterator itr = file_ids.begin();
    931        itr != file_ids.end(); ++itr) {
    932     scoped_ptr<FileMetadata> metadata(
    933         CreateDeletedFileMetadata(GetLargestKnownChangeID(), *itr));
    934     UpdateByFileMetadata(FROM_HERE, metadata.Pass(),
    935                          UPDATE_TRACKER_FOR_UNSYNCED_FILE);
    936   }
    937   return WriteToDatabase();
    938 }
    939 
    940 SyncStatusCode MetadataDatabase::ReplaceActiveTrackerWithNewResource(
    941     int64 parent_tracker_id,
    942     const google_apis::FileResource& resource) {
    943   DCHECK(!index_->GetFileMetadata(resource.file_id(), NULL));
    944   DCHECK(index_->GetFileTracker(parent_tracker_id, NULL));
    945 
    946   UpdateByFileMetadata(
    947       FROM_HERE,
    948       CreateFileMetadataFromFileResource(GetLargestKnownChangeID(), resource),
    949       UPDATE_TRACKER_FOR_SYNCED_FILE);
    950 
    951   DCHECK(index_->GetFileMetadata(resource.file_id(), NULL));
    952   DCHECK(!index_->GetFileTrackerIDsByFileID(resource.file_id()).has_active());
    953 
    954   TrackerIDSet same_path_trackers =
    955       index_->GetFileTrackerIDsByParentAndTitle(
    956           parent_tracker_id, resource.title());
    957   FileTracker to_be_activated;
    958   if (!FilterFileTrackersByFileID(index_.get(), same_path_trackers,
    959                                   resource.file_id(), &to_be_activated)) {
    960     NOTREACHED();
    961     return SYNC_STATUS_FAILED;
    962   }
    963 
    964   int64 tracker_id = to_be_activated.tracker_id();
    965   if (same_path_trackers.has_active()) {
    966     DeactivateFileTracker(same_path_trackers.active_tracker(),
    967                           MARK_ITSELF_DIRTY |
    968                           MARK_SAME_FILE_ID_TRACKERS_DIRTY,
    969                           index_.get());
    970   }
    971 
    972   ActivateFileTracker(tracker_id, MARK_NOTHING_DIRTY, index_.get());
    973   return WriteToDatabase();
    974 }
    975 
    976 SyncStatusCode MetadataDatabase::PopulateFolderByChildList(
    977     const std::string& folder_id,
    978     const FileIDList& child_file_ids) {
    979   TrackerIDSet trackers = index_->GetFileTrackerIDsByFileID(folder_id);
    980   if (!trackers.has_active()) {
    981     // It's OK that there is no folder to populate its children.
    982     // Inactive folders should ignore their contents updates.
    983     return SYNC_STATUS_OK;
    984   }
    985 
    986   scoped_ptr<FileTracker> folder_tracker(new FileTracker);
    987   if (!index_->GetFileTracker(trackers.active_tracker(),
    988                               folder_tracker.get())) {
    989     NOTREACHED();
    990     return SYNC_STATUS_FAILED;
    991   }
    992 
    993   base::hash_set<std::string> children(child_file_ids.begin(),
    994                                        child_file_ids.end());
    995 
    996   std::vector<int64> known_children =
    997       index_->GetFileTrackerIDsByParent(folder_tracker->tracker_id());
    998   for (size_t i = 0; i < known_children.size(); ++i) {
    999     FileTracker tracker;
   1000     if (!index_->GetFileTracker(known_children[i], &tracker)) {
   1001       NOTREACHED();
   1002       continue;
   1003     }
   1004     children.erase(tracker.file_id());
   1005   }
   1006 
   1007   for (base::hash_set<std::string>::const_iterator itr = children.begin();
   1008        itr != children.end(); ++itr)
   1009     CreateTrackerForParentAndFileID(*folder_tracker, *itr);
   1010   folder_tracker->set_needs_folder_listing(false);
   1011   if (folder_tracker->dirty() && !ShouldKeepDirty(*folder_tracker))
   1012     folder_tracker->set_dirty(false);
   1013   index_->StoreFileTracker(folder_tracker.Pass());
   1014 
   1015   return WriteToDatabase();
   1016 }
   1017 
   1018 SyncStatusCode MetadataDatabase::UpdateTracker(
   1019     int64 tracker_id,
   1020     const FileDetails& updated_details) {
   1021   FileTracker tracker;
   1022   if (!index_->GetFileTracker(tracker_id, &tracker)) {
   1023     return SYNC_DATABASE_ERROR_NOT_FOUND;
   1024   }
   1025 
   1026   // Check if the tracker is to be deleted.
   1027   if (updated_details.missing()) {
   1028     FileMetadata metadata;
   1029     if (!index_->GetFileMetadata(tracker.file_id(), &metadata) ||
   1030         metadata.details().missing()) {
   1031       // Both the tracker and metadata have the missing flag, now it's safe to
   1032       // delete the |tracker|.
   1033       RemoveFileTracker(tracker_id,
   1034                         MARK_SAME_FILE_ID_TRACKERS_DIRTY |
   1035                         MARK_SAME_PATH_TRACKERS_DIRTY,
   1036                         index_.get());
   1037       return WriteToDatabase();
   1038     }
   1039   }
   1040 
   1041   // Sync-root deletion should be handled separately by SyncEngine.
   1042   DCHECK(tracker_id != GetSyncRootTrackerID() ||
   1043          (tracker.has_synced_details() &&
   1044           tracker.synced_details().title() == updated_details.title() &&
   1045           !updated_details.missing()));
   1046 
   1047   if (tracker_id != GetSyncRootTrackerID()) {
   1048     // Check if the tracker's parent is still in |parent_tracker_ids|.
   1049     // If not, there should exist another tracker for the new parent, so delete
   1050     // old tracker.
   1051     FileTracker parent_tracker;
   1052     index_->GetFileTracker(tracker.parent_tracker_id(), &parent_tracker);
   1053 
   1054     if (!HasFileAsParent(updated_details, parent_tracker.file_id())) {
   1055       RemoveFileTracker(tracker.tracker_id(),
   1056                         MARK_SAME_PATH_TRACKERS_DIRTY,
   1057                         index_.get());
   1058       return WriteToDatabase();
   1059     }
   1060 
   1061     if (tracker.has_synced_details()) {
   1062       // Check if the tracker was retitled.  If it was, there should exist
   1063       // another tracker for the new title, so delete the tracker being updated.
   1064       if (tracker.synced_details().title() != updated_details.title()) {
   1065         RemoveFileTracker(tracker.tracker_id(),
   1066                           MARK_SAME_FILE_ID_TRACKERS_DIRTY,
   1067                           index_.get());
   1068         return WriteToDatabase();
   1069       }
   1070     } else {
   1071       // Check if any other tracker exists has the same parent, title and
   1072       // file_id to the updated tracker.  If it exists, delete the tracker being
   1073       // updated.
   1074       if (FilterFileTrackersByFileID(
   1075               index_.get(),
   1076               index_->GetFileTrackerIDsByParentAndTitle(
   1077                   parent_tracker.tracker_id(),
   1078                   updated_details.title()),
   1079               tracker.file_id(),
   1080               NULL)) {
   1081         RemoveFileTracker(tracker.tracker_id(),
   1082                           MARK_NOTHING_DIRTY,
   1083                           index_.get());
   1084         return WriteToDatabase();
   1085       }
   1086     }
   1087   }
   1088 
   1089   scoped_ptr<FileTracker> updated_tracker = CloneFileTracker(&tracker);
   1090   *updated_tracker->mutable_synced_details() = updated_details;
   1091 
   1092   bool should_promote = false;
   1093 
   1094   // Activate the tracker if:
   1095   //   - There is no active tracker that tracks |tracker->file_id()|.
   1096   //   - There is no active tracker that has the same |parent| and |title|.
   1097   if (!tracker.active() && CanActivateTracker(tracker)) {
   1098     updated_tracker->set_active(true);
   1099     updated_tracker->set_dirty(true);
   1100     updated_tracker->set_needs_folder_listing(
   1101         tracker.synced_details().file_kind() == FILE_KIND_FOLDER);
   1102     should_promote = true;
   1103   } else if (tracker.dirty() && !ShouldKeepDirty(tracker)) {
   1104     updated_tracker->set_dirty(false);
   1105   }
   1106   index_->StoreFileTracker(updated_tracker.Pass());
   1107   if (should_promote)
   1108     index_->PromoteDemotedDirtyTracker(tracker_id);
   1109 
   1110   return WriteToDatabase();
   1111 }
   1112 
   1113 MetadataDatabase::ActivationStatus MetadataDatabase::TryActivateTracker(
   1114     int64 parent_tracker_id,
   1115     const std::string& file_id,
   1116     SyncStatusCode* status_out) {
   1117   FileMetadata metadata;
   1118   if (!index_->GetFileMetadata(file_id, &metadata)) {
   1119     NOTREACHED();
   1120     *status_out = SYNC_STATUS_FAILED;
   1121     return ACTIVATION_PENDING;
   1122   }
   1123   std::string title = metadata.details().title();
   1124   DCHECK(!HasInvalidTitle(title));
   1125 
   1126   TrackerIDSet same_file_id_trackers =
   1127       index_->GetFileTrackerIDsByFileID(file_id);
   1128   scoped_ptr<FileTracker> tracker_to_be_activated(new FileTracker);
   1129   FilterFileTrackersByParentAndTitle(
   1130       index_.get(), same_file_id_trackers, parent_tracker_id,
   1131       title, tracker_to_be_activated.get());
   1132 
   1133   // Check if there is another active tracker that tracks |file_id|.
   1134   // This can happen when the tracked file has multiple parents.
   1135   // In this case, report the failure to the caller.
   1136   if (!tracker_to_be_activated->active() && same_file_id_trackers.has_active())
   1137     return ACTIVATION_FAILED_ANOTHER_ACTIVE_TRACKER;
   1138 
   1139   if (!tracker_to_be_activated->active()) {
   1140     // Check if there exists another active tracker that has the same path to
   1141     // the tracker.  If there is, deactivate it, assuming the caller already
   1142     // overrides local file with newly added file,
   1143     TrackerIDSet same_title_trackers =
   1144         index_->GetFileTrackerIDsByParentAndTitle(parent_tracker_id, title);
   1145     if (same_title_trackers.has_active()) {
   1146       RemoveAllDescendantTrackers(same_title_trackers.active_tracker(),
   1147                                   index_.get());
   1148 
   1149       scoped_ptr<FileTracker> tracker_to_be_deactivated(new FileTracker);
   1150       if (index_->GetFileTracker(same_title_trackers.active_tracker(),
   1151                                  tracker_to_be_deactivated.get())) {
   1152         const std::string file_id = tracker_to_be_deactivated->file_id();
   1153         tracker_to_be_deactivated->set_active(false);
   1154         index_->StoreFileTracker(tracker_to_be_deactivated.Pass());
   1155 
   1156         MarkTrackersDirtyByFileID(file_id, index_.get());
   1157       } else {
   1158         NOTREACHED();
   1159       }
   1160     }
   1161   }
   1162 
   1163   tracker_to_be_activated->set_dirty(false);
   1164   tracker_to_be_activated->set_active(true);
   1165   *tracker_to_be_activated->mutable_synced_details() = metadata.details();
   1166   if (tracker_to_be_activated->synced_details().file_kind() ==
   1167       FILE_KIND_FOLDER) {
   1168     tracker_to_be_activated->set_needs_folder_listing(true);
   1169   }
   1170   tracker_to_be_activated->set_dirty(false);
   1171 
   1172   index_->StoreFileTracker(tracker_to_be_activated.Pass());
   1173 
   1174   *status_out = WriteToDatabase();
   1175   return ACTIVATION_PENDING;
   1176 }
   1177 
   1178 void MetadataDatabase::DemoteTracker(int64 tracker_id) {
   1179   index_->DemoteDirtyTracker(tracker_id);
   1180   WriteToDatabase();
   1181 }
   1182 
   1183 bool MetadataDatabase::PromoteDemotedTrackers() {
   1184   bool promoted = index_->PromoteDemotedDirtyTrackers();
   1185   WriteToDatabase();
   1186   return promoted;
   1187 }
   1188 
   1189 void MetadataDatabase::PromoteDemotedTracker(int64 tracker_id) {
   1190   index_->PromoteDemotedDirtyTracker(tracker_id);
   1191   WriteToDatabase();
   1192 }
   1193 
   1194 bool MetadataDatabase::GetDirtyTracker(
   1195     FileTracker* tracker_out) const {
   1196   int64 dirty_tracker_id = index_->PickDirtyTracker();
   1197   if (!dirty_tracker_id)
   1198     return false;
   1199 
   1200   if (tracker_out) {
   1201     if (!index_->GetFileTracker(dirty_tracker_id, tracker_out)) {
   1202       NOTREACHED();
   1203       return false;
   1204     }
   1205   }
   1206   return true;
   1207 }
   1208 
   1209 bool MetadataDatabase::HasDemotedDirtyTracker() const {
   1210   return index_->HasDemotedDirtyTracker();
   1211 }
   1212 
   1213 bool MetadataDatabase::HasDirtyTracker() const {
   1214   return index_->PickDirtyTracker() != kInvalidTrackerID;
   1215 }
   1216 
   1217 size_t MetadataDatabase::CountDirtyTracker() const {
   1218   return index_->CountDirtyTracker();
   1219 }
   1220 
   1221 bool MetadataDatabase::GetMultiParentFileTrackers(std::string* file_id_out,
   1222                                                   TrackerIDSet* trackers_out) {
   1223   DCHECK(file_id_out);
   1224   DCHECK(trackers_out);
   1225 
   1226   std::string file_id = index_->PickMultiTrackerFileID();
   1227   if (file_id.empty())
   1228     return false;
   1229 
   1230   TrackerIDSet trackers = index_->GetFileTrackerIDsByFileID(file_id);
   1231   if (trackers.size() <= 1) {
   1232     NOTREACHED();
   1233     return false;
   1234   }
   1235 
   1236   *file_id_out = file_id;
   1237   std::swap(*trackers_out, trackers);
   1238   return true;
   1239 }
   1240 
   1241 size_t MetadataDatabase::CountFileMetadata() const {
   1242   return index_->CountFileMetadata();
   1243 }
   1244 
   1245 size_t MetadataDatabase::CountFileTracker() const {
   1246   return index_->CountFileTracker();
   1247 }
   1248 
   1249 bool MetadataDatabase::GetConflictingTrackers(TrackerIDSet* trackers_out) {
   1250   DCHECK(trackers_out);
   1251 
   1252   ParentIDAndTitle parent_and_title = index_->PickMultiBackingFilePath();
   1253   if (parent_and_title.parent_id == kInvalidTrackerID)
   1254     return false;
   1255 
   1256   TrackerIDSet trackers = index_->GetFileTrackerIDsByParentAndTitle(
   1257       parent_and_title.parent_id, parent_and_title.title);
   1258   if (trackers.size() <= 1) {
   1259     NOTREACHED();
   1260     return false;
   1261   }
   1262 
   1263   std::swap(*trackers_out, trackers);
   1264   return true;
   1265 }
   1266 
   1267 void MetadataDatabase::GetRegisteredAppIDs(std::vector<std::string>* app_ids) {
   1268   DCHECK(app_ids);
   1269   *app_ids = index_->GetRegisteredAppIDs();
   1270 }
   1271 
   1272 SyncStatusCode MetadataDatabase::SweepDirtyTrackers(
   1273     const std::vector<std::string>& file_ids) {
   1274   std::set<int64> tracker_ids;
   1275   for (size_t i = 0; i < file_ids.size(); ++i) {
   1276     TrackerIDSet trackers_for_file_id =
   1277         index_->GetFileTrackerIDsByFileID(file_ids[i]);
   1278     for (TrackerIDSet::iterator itr = trackers_for_file_id.begin();
   1279          itr != trackers_for_file_id.end(); ++itr)
   1280       tracker_ids.insert(*itr);
   1281   }
   1282 
   1283   for (std::set<int64>::iterator itr = tracker_ids.begin();
   1284        itr != tracker_ids.end(); ++itr) {
   1285     scoped_ptr<FileTracker> tracker(new FileTracker);
   1286     if (!index_->GetFileTracker(*itr, tracker.get()) ||
   1287         !CanClearDirty(*tracker))
   1288       continue;
   1289     tracker->set_dirty(false);
   1290     index_->StoreFileTracker(tracker.Pass());
   1291   }
   1292 
   1293   return WriteToDatabase();
   1294 }
   1295 
   1296 MetadataDatabase::MetadataDatabase(
   1297     const base::FilePath& database_path,
   1298     bool enable_on_disk_index,
   1299     leveldb::Env* env_override)
   1300     : database_path_(database_path),
   1301       env_override_(env_override),
   1302       enable_on_disk_index_(enable_on_disk_index),
   1303       largest_known_change_id_(0),
   1304       weak_ptr_factory_(this) {
   1305 }
   1306 
   1307 SyncStatusCode MetadataDatabase::Initialize() {
   1308   SyncStatusCode status = SYNC_STATUS_UNKNOWN;
   1309   bool created = false;
   1310   // Open database unless |db_| is overridden for testing.
   1311   if (!db_) {
   1312     status = OpenDatabase(database_path_, env_override_, &db_, &created);
   1313     if (status != SYNC_STATUS_OK)
   1314       return status;
   1315   }
   1316 
   1317   if (!created) {
   1318     status = MigrateDatabaseIfNeeded(db_.get());
   1319     if (status != SYNC_STATUS_OK)
   1320       return status;
   1321   }
   1322 
   1323   if (enable_on_disk_index_) {
   1324     index_ = MetadataDatabaseIndexOnDisk::Create(db_.get());
   1325   } else {
   1326     index_ = MetadataDatabaseIndex::Create(db_.get());
   1327   }
   1328   if (!index_) {
   1329     // Delete all entries in |db_| to reset it.
   1330     // TODO(peria): Make LevelDBWrapper::DestroyDB() to avoid a full scan.
   1331     scoped_ptr<LevelDBWrapper::Iterator> itr = db_->NewIterator();
   1332     for (itr->SeekToFirst(); itr->Valid();)
   1333       itr->Delete();
   1334     db_->Commit();
   1335 
   1336     return SYNC_DATABASE_ERROR_FAILED;
   1337   }
   1338 
   1339   status = LevelDBStatusToSyncStatusCode(db_->Commit());
   1340   if (status != SYNC_STATUS_OK)
   1341     return status;
   1342 
   1343   UpdateLargestKnownChangeID(index_->GetLargestChangeID());
   1344 
   1345   return status;
   1346 }
   1347 
   1348 void MetadataDatabase::CreateTrackerForParentAndFileID(
   1349     const FileTracker& parent_tracker,
   1350     const std::string& file_id) {
   1351   CreateTrackerInternal(parent_tracker, file_id, NULL,
   1352                         UPDATE_TRACKER_FOR_UNSYNCED_FILE);
   1353 }
   1354 
   1355 void MetadataDatabase::CreateTrackerForParentAndFileMetadata(
   1356     const FileTracker& parent_tracker,
   1357     const FileMetadata& file_metadata,
   1358     UpdateOption option) {
   1359   DCHECK(file_metadata.has_details());
   1360   CreateTrackerInternal(parent_tracker,
   1361                         file_metadata.file_id(),
   1362                         &file_metadata.details(),
   1363                         option);
   1364 }
   1365 
   1366 void MetadataDatabase::CreateTrackerInternal(const FileTracker& parent_tracker,
   1367                                              const std::string& file_id,
   1368                                              const FileDetails* details,
   1369                                              UpdateOption option) {
   1370   int64 tracker_id = IncrementTrackerID();
   1371   scoped_ptr<FileTracker> tracker(new FileTracker);
   1372   tracker->set_tracker_id(tracker_id);
   1373   tracker->set_parent_tracker_id(parent_tracker.tracker_id());
   1374   tracker->set_file_id(file_id);
   1375   tracker->set_app_id(parent_tracker.app_id());
   1376   tracker->set_tracker_kind(TRACKER_KIND_REGULAR);
   1377   tracker->set_dirty(true);
   1378   tracker->set_active(false);
   1379   tracker->set_needs_folder_listing(false);
   1380   if (details) {
   1381     *tracker->mutable_synced_details() = *details;
   1382     if (option == UPDATE_TRACKER_FOR_UNSYNCED_FILE) {
   1383       tracker->mutable_synced_details()->set_missing(true);
   1384       tracker->mutable_synced_details()->clear_md5();
   1385     }
   1386   }
   1387   index_->StoreFileTracker(tracker.Pass());
   1388 }
   1389 
   1390 void MetadataDatabase::MaybeAddTrackersForNewFile(
   1391     const FileMetadata& metadata,
   1392     UpdateOption option) {
   1393   std::set<int64> parents_to_exclude;
   1394   TrackerIDSet existing_trackers =
   1395       index_->GetFileTrackerIDsByFileID(metadata.file_id());
   1396   for (TrackerIDSet::const_iterator itr = existing_trackers.begin();
   1397        itr != existing_trackers.end(); ++itr) {
   1398     FileTracker tracker;
   1399     if (!index_->GetFileTracker(*itr, &tracker)) {
   1400       NOTREACHED();
   1401       continue;
   1402     }
   1403 
   1404     int64 parent_tracker_id = tracker.parent_tracker_id();
   1405     if (!parent_tracker_id)
   1406       continue;
   1407 
   1408     // Exclude |parent_tracker_id| if it already has a tracker that has
   1409     // unknown title or has the same title with |file|.
   1410     if (!tracker.has_synced_details() ||
   1411         tracker.synced_details().title() == metadata.details().title()) {
   1412       parents_to_exclude.insert(parent_tracker_id);
   1413     }
   1414   }
   1415 
   1416   for (int i = 0; i < metadata.details().parent_folder_ids_size(); ++i) {
   1417     std::string parent_folder_id = metadata.details().parent_folder_ids(i);
   1418     TrackerIDSet parent_trackers =
   1419         index_->GetFileTrackerIDsByFileID(parent_folder_id);
   1420     for (TrackerIDSet::const_iterator itr = parent_trackers.begin();
   1421          itr != parent_trackers.end(); ++itr) {
   1422       FileTracker parent_tracker;
   1423       index_->GetFileTracker(*itr, &parent_tracker);
   1424       if (!parent_tracker.active())
   1425         continue;
   1426 
   1427       if (ContainsKey(parents_to_exclude, parent_tracker.tracker_id()))
   1428         continue;
   1429 
   1430       CreateTrackerForParentAndFileMetadata(
   1431           parent_tracker, metadata, option);
   1432     }
   1433   }
   1434 }
   1435 
   1436 int64 MetadataDatabase::IncrementTrackerID() {
   1437   int64 tracker_id = index_->GetNextTrackerID();
   1438   index_->SetNextTrackerID(tracker_id + 1);
   1439   DCHECK_GT(tracker_id, 0);
   1440   return tracker_id;
   1441 }
   1442 
   1443 bool MetadataDatabase::CanActivateTracker(const FileTracker& tracker) {
   1444   DCHECK(!tracker.active());
   1445   DCHECK_NE(index_->GetSyncRootTrackerID(), tracker.tracker_id());
   1446 
   1447   if (HasActiveTrackerForFileID(tracker.file_id()))
   1448     return false;
   1449 
   1450   if (tracker.app_id().empty() &&
   1451       tracker.tracker_id() != GetSyncRootTrackerID()) {
   1452     return false;
   1453   }
   1454 
   1455   if (!tracker.has_synced_details())
   1456     return false;
   1457   if (tracker.synced_details().file_kind() == FILE_KIND_UNSUPPORTED)
   1458     return false;
   1459   if (HasInvalidTitle(tracker.synced_details().title()))
   1460     return false;
   1461   DCHECK(tracker.parent_tracker_id());
   1462 
   1463   return !HasActiveTrackerForPath(tracker.parent_tracker_id(),
   1464                                   tracker.synced_details().title());
   1465 }
   1466 
   1467 bool MetadataDatabase::ShouldKeepDirty(const FileTracker& tracker) const {
   1468   if (HasDisabledAppRoot(tracker))
   1469     return false;
   1470 
   1471   DCHECK(tracker.dirty());
   1472   if (!tracker.has_synced_details())
   1473     return true;
   1474 
   1475   FileMetadata metadata;
   1476   if (!index_->GetFileMetadata(tracker.file_id(), &metadata))
   1477     return true;
   1478   DCHECK(metadata.has_details());
   1479 
   1480   const FileDetails& local_details = tracker.synced_details();
   1481   const FileDetails& remote_details = metadata.details();
   1482 
   1483   if (tracker.active()) {
   1484     if (tracker.needs_folder_listing())
   1485       return true;
   1486     if (local_details.md5() != remote_details.md5())
   1487       return true;
   1488     if (local_details.missing() != remote_details.missing())
   1489       return true;
   1490   }
   1491 
   1492   if (local_details.title() != remote_details.title())
   1493     return true;
   1494 
   1495   return false;
   1496 }
   1497 
   1498 bool MetadataDatabase::HasDisabledAppRoot(const FileTracker& tracker) const {
   1499   int64 app_root_tracker_id = index_->GetAppRootTracker(tracker.app_id());
   1500   if (app_root_tracker_id == kInvalidTrackerID)
   1501     return false;
   1502 
   1503   FileTracker app_root_tracker;
   1504   if (!index_->GetFileTracker(app_root_tracker_id, &app_root_tracker)) {
   1505     NOTREACHED();
   1506     return false;
   1507   }
   1508   return app_root_tracker.tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT;
   1509 }
   1510 
   1511 bool MetadataDatabase::HasActiveTrackerForFileID(
   1512     const std::string& file_id) const {
   1513   return index_->GetFileTrackerIDsByFileID(file_id).has_active();
   1514 }
   1515 
   1516 bool MetadataDatabase::HasActiveTrackerForPath(int64 parent_tracker_id,
   1517                                                const std::string& title) const {
   1518   return index_->GetFileTrackerIDsByParentAndTitle(parent_tracker_id, title)
   1519       .has_active();
   1520 }
   1521 
   1522 void MetadataDatabase::RemoveUnneededTrackersForMissingFile(
   1523     const std::string& file_id) {
   1524   TrackerIDSet trackers = index_->GetFileTrackerIDsByFileID(file_id);
   1525   for (TrackerIDSet::const_iterator itr = trackers.begin();
   1526        itr != trackers.end(); ++itr) {
   1527     FileTracker tracker;
   1528     if (!index_->GetFileTracker(*itr, &tracker)) {
   1529       NOTREACHED();
   1530       continue;
   1531     }
   1532 
   1533     if (!tracker.has_synced_details() || tracker.synced_details().missing()) {
   1534       RemoveFileTracker(*itr, MARK_NOTHING_DIRTY, index_.get());
   1535     }
   1536   }
   1537 }
   1538 
   1539 void MetadataDatabase::UpdateByFileMetadata(
   1540     const tracked_objects::Location& from_where,
   1541     scoped_ptr<FileMetadata> metadata,
   1542     UpdateOption option) {
   1543   DCHECK(metadata);
   1544   DCHECK(metadata->has_details());
   1545 
   1546   DVLOG(1) << from_where.function_name() << ": "
   1547            << metadata->file_id() << " ("
   1548            << metadata->details().title() << ")"
   1549            << (metadata->details().missing() ? " deleted" : "");
   1550 
   1551   std::string file_id = metadata->file_id();
   1552   if (metadata->details().missing())
   1553     RemoveUnneededTrackersForMissingFile(file_id);
   1554   else
   1555     MaybeAddTrackersForNewFile(*metadata, option);
   1556 
   1557   TrackerIDSet trackers = index_->GetFileTrackerIDsByFileID(file_id);
   1558   if (!trackers.empty()) {
   1559     index_->StoreFileMetadata(metadata.Pass());
   1560 
   1561     if (option != UPDATE_TRACKER_FOR_SYNCED_FILE)
   1562       MarkTrackerSetDirty(trackers, index_.get());
   1563   }
   1564 }
   1565 
   1566 
   1567 SyncStatusCode MetadataDatabase::WriteToDatabase() {
   1568   return LevelDBStatusToSyncStatusCode(db_->Commit());
   1569 }
   1570 
   1571 scoped_ptr<base::ListValue> MetadataDatabase::DumpFiles(
   1572     const std::string& app_id) {
   1573   scoped_ptr<base::ListValue> files(new base::ListValue);
   1574 
   1575   FileTracker app_root_tracker;
   1576   if (!FindAppRootTracker(app_id, &app_root_tracker))
   1577     return files.Pass();
   1578 
   1579   std::vector<int64> stack;
   1580   AppendContents(
   1581       index_->GetFileTrackerIDsByParent(app_root_tracker.tracker_id()), &stack);
   1582   while (!stack.empty()) {
   1583     int64 tracker_id = stack.back();
   1584     stack.pop_back();
   1585     AppendContents(index_->GetFileTrackerIDsByParent(tracker_id), &stack);
   1586 
   1587     FileTracker tracker;
   1588     if (!index_->GetFileTracker(tracker_id, &tracker)) {
   1589       NOTREACHED();
   1590       continue;
   1591     }
   1592     base::DictionaryValue* file = new base::DictionaryValue;
   1593 
   1594     base::FilePath path = BuildDisplayPathForTracker(tracker);
   1595     file->SetString("path", path.AsUTF8Unsafe());
   1596     if (tracker.has_synced_details()) {
   1597       file->SetString("title", tracker.synced_details().title());
   1598       file->SetString("type",
   1599                       FileKindToString(tracker.synced_details().file_kind()));
   1600     }
   1601 
   1602     base::DictionaryValue* details = new base::DictionaryValue;
   1603     details->SetString("file_id", tracker.file_id());
   1604     if (tracker.has_synced_details() &&
   1605         tracker.synced_details().file_kind() == FILE_KIND_FILE)
   1606       details->SetString("md5", tracker.synced_details().md5());
   1607     details->SetString("active", tracker.active() ? "true" : "false");
   1608     details->SetString("dirty", tracker.dirty() ? "true" : "false");
   1609 
   1610     file->Set("details", details);
   1611 
   1612     files->Append(file);
   1613   }
   1614 
   1615   return files.Pass();
   1616 }
   1617 
   1618 scoped_ptr<base::ListValue> MetadataDatabase::DumpDatabase() {
   1619   scoped_ptr<base::ListValue> list(new base::ListValue);
   1620   list->Append(DumpTrackers().release());
   1621   list->Append(DumpMetadata().release());
   1622   return list.Pass();
   1623 }
   1624 
   1625 bool MetadataDatabase::HasNewerFileMetadata(const std::string& file_id,
   1626                                             int64 change_id) {
   1627   FileMetadata metadata;
   1628   if (!index_->GetFileMetadata(file_id, &metadata))
   1629     return false;
   1630   DCHECK(metadata.has_details());
   1631   return metadata.details().change_id() >= change_id;
   1632 }
   1633 
   1634 scoped_ptr<base::ListValue> MetadataDatabase::DumpTrackers() {
   1635   scoped_ptr<base::ListValue> trackers(new base::ListValue);
   1636 
   1637   // Append the first element for metadata.
   1638   base::DictionaryValue* metadata = new base::DictionaryValue;
   1639   const char *trackerKeys[] = {
   1640     "tracker_id", "path", "file_id", "tracker_kind", "app_id",
   1641     "active", "dirty", "folder_listing",
   1642     "title", "kind", "md5", "etag", "missing", "change_id",
   1643   };
   1644   std::vector<std::string> key_strings(
   1645       trackerKeys, trackerKeys + ARRAYSIZE_UNSAFE(trackerKeys));
   1646   base::ListValue* keys = new base::ListValue;
   1647   keys->AppendStrings(key_strings);
   1648   metadata->SetString("title", "Trackers");
   1649   metadata->Set("keys", keys);
   1650   trackers->Append(metadata);
   1651 
   1652   // Append tracker data.
   1653   std::vector<int64> tracker_ids(index_->GetAllTrackerIDs());
   1654   for (std::vector<int64>::const_iterator itr = tracker_ids.begin();
   1655        itr != tracker_ids.end(); ++itr) {
   1656     const int64 tracker_id = *itr;
   1657     FileTracker tracker;
   1658     if (!index_->GetFileTracker(tracker_id, &tracker)) {
   1659       NOTREACHED();
   1660       continue;
   1661     }
   1662 
   1663     base::DictionaryValue* dict = new base::DictionaryValue;
   1664     base::FilePath path = BuildDisplayPathForTracker(tracker);
   1665     dict->SetString("tracker_id", base::Int64ToString(tracker_id));
   1666     dict->SetString("path", path.AsUTF8Unsafe());
   1667     dict->SetString("file_id", tracker.file_id());
   1668     TrackerKind tracker_kind = tracker.tracker_kind();
   1669     dict->SetString(
   1670         "tracker_kind",
   1671         tracker_kind == TRACKER_KIND_APP_ROOT ? "AppRoot" :
   1672         tracker_kind == TRACKER_KIND_DISABLED_APP_ROOT ? "Disabled App" :
   1673         tracker.tracker_id() == GetSyncRootTrackerID() ? "SyncRoot" :
   1674         "Regular");
   1675     dict->SetString("app_id", tracker.app_id());
   1676     dict->SetString("active", tracker.active() ? "true" : "false");
   1677     dict->SetString("dirty", tracker.dirty() ? "true" : "false");
   1678     dict->SetString("folder_listing",
   1679                     tracker.needs_folder_listing() ? "needed" : "no");
   1680     if (tracker.has_synced_details()) {
   1681       const FileDetails& details = tracker.synced_details();
   1682       dict->SetString("title", details.title());
   1683       dict->SetString("kind", FileKindToString(details.file_kind()));
   1684       dict->SetString("md5", details.md5());
   1685       dict->SetString("etag", details.etag());
   1686       dict->SetString("missing", details.missing() ? "true" : "false");
   1687       dict->SetString("change_id", base::Int64ToString(details.change_id()));
   1688     }
   1689     trackers->Append(dict);
   1690   }
   1691   return trackers.Pass();
   1692 }
   1693 
   1694 scoped_ptr<base::ListValue> MetadataDatabase::DumpMetadata() {
   1695   scoped_ptr<base::ListValue> files(new base::ListValue);
   1696 
   1697   // Append the first element for metadata.
   1698   base::DictionaryValue* metadata = new base::DictionaryValue;
   1699   const char *fileKeys[] = {
   1700     "file_id", "title", "type", "md5", "etag", "missing",
   1701     "change_id", "parents"
   1702   };
   1703   std::vector<std::string> key_strings(
   1704       fileKeys, fileKeys + ARRAYSIZE_UNSAFE(fileKeys));
   1705   base::ListValue* keys = new base::ListValue;
   1706   keys->AppendStrings(key_strings);
   1707   metadata->SetString("title", "Metadata");
   1708   metadata->Set("keys", keys);
   1709   files->Append(metadata);
   1710 
   1711   // Append metadata data.
   1712   std::vector<std::string> metadata_ids(index_->GetAllMetadataIDs());
   1713   for (std::vector<std::string>::const_iterator itr = metadata_ids.begin();
   1714        itr != metadata_ids.end(); ++itr) {
   1715     const std::string& file_id = *itr;
   1716     FileMetadata file;
   1717     if (!index_->GetFileMetadata(file_id, &file)) {
   1718       NOTREACHED();
   1719       continue;
   1720     }
   1721 
   1722     base::DictionaryValue* dict = new base::DictionaryValue;
   1723     dict->SetString("file_id", file_id);
   1724     if (file.has_details()) {
   1725       const FileDetails& details = file.details();
   1726       dict->SetString("title", details.title());
   1727       dict->SetString("type", FileKindToString(details.file_kind()));
   1728       dict->SetString("md5", details.md5());
   1729       dict->SetString("etag", details.etag());
   1730       dict->SetString("missing", details.missing() ? "true" : "false");
   1731       dict->SetString("change_id", base::Int64ToString(details.change_id()));
   1732 
   1733       std::vector<std::string> parents;
   1734       for (int i = 0; i < details.parent_folder_ids_size(); ++i)
   1735         parents.push_back(details.parent_folder_ids(i));
   1736       dict->SetString("parents", JoinString(parents, ","));
   1737     }
   1738     files->Append(dict);
   1739   }
   1740   return files.Pass();
   1741 }
   1742 
   1743 void MetadataDatabase::AttachSyncRoot(
   1744     const google_apis::FileResource& sync_root_folder) {
   1745   scoped_ptr<FileMetadata> sync_root_metadata =
   1746       CreateFileMetadataFromFileResource(
   1747           GetLargestKnownChangeID(), sync_root_folder);
   1748   scoped_ptr<FileTracker> sync_root_tracker =
   1749       CreateSyncRootTracker(IncrementTrackerID(), *sync_root_metadata);
   1750 
   1751   index_->SetSyncRootTrackerID(sync_root_tracker->tracker_id());
   1752   index_->StoreFileMetadata(sync_root_metadata.Pass());
   1753   index_->StoreFileTracker(sync_root_tracker.Pass());
   1754 }
   1755 
   1756 void MetadataDatabase::AttachInitialAppRoot(
   1757     const google_apis::FileResource& app_root_folder) {
   1758   scoped_ptr<FileMetadata> app_root_metadata =
   1759       CreateFileMetadataFromFileResource(
   1760           GetLargestKnownChangeID(), app_root_folder);
   1761   scoped_ptr<FileTracker> app_root_tracker =
   1762       CreateInitialAppRootTracker(IncrementTrackerID(),
   1763                                   GetSyncRootTrackerID(),
   1764                                   *app_root_metadata);
   1765 
   1766   index_->StoreFileMetadata(app_root_metadata.Pass());
   1767   index_->StoreFileTracker(app_root_tracker.Pass());
   1768 }
   1769 
   1770 bool MetadataDatabase::CanClearDirty(const FileTracker& tracker) {
   1771   FileMetadata metadata;
   1772   if (!index_->GetFileMetadata(tracker.file_id(), &metadata) ||
   1773       !tracker.active() || !tracker.dirty() ||
   1774       !tracker.has_synced_details() ||
   1775       tracker.needs_folder_listing())
   1776     return false;
   1777 
   1778   const FileDetails& remote_details = metadata.details();
   1779   const FileDetails& synced_details = tracker.synced_details();
   1780   if (remote_details.title() != synced_details.title() ||
   1781       remote_details.md5() != synced_details.md5() ||
   1782       remote_details.missing() != synced_details.missing())
   1783     return false;
   1784 
   1785   std::set<std::string> parents;
   1786   for (int i = 0; i < remote_details.parent_folder_ids_size(); ++i)
   1787     parents.insert(remote_details.parent_folder_ids(i));
   1788 
   1789   for (int i = 0; i < synced_details.parent_folder_ids_size(); ++i)
   1790     if (parents.erase(synced_details.parent_folder_ids(i)) != 1)
   1791       return false;
   1792 
   1793   if (!parents.empty())
   1794     return false;
   1795 
   1796   return true;
   1797 }
   1798 
   1799 }  // namespace drive_backend
   1800 }  // namespace sync_file_system
   1801