Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / sync_file_system / drive_backend / metadata_database.cc
index 737ded8..2fe427a 100644 (file)
@@ -9,55 +9,59 @@
 
 #include "base/bind.h"
 #include "base/callback.h"
+#include "base/command_line.h"
+#include "base/file_util.h"
 #include "base/files/file_path.h"
 #include "base/location.h"
 #include "base/memory/scoped_vector.h"
-#include "base/message_loop/message_loop_proxy.h"
-#include "base/sequenced_task_runner.h"
+#include "base/single_thread_task_runner.h"
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/task_runner_util.h"
+#include "base/thread_task_runner_handle.h"
 #include "base/threading/thread_restrictions.h"
 #include "chrome/browser/drive/drive_api_util.h"
-#include "chrome/browser/google_apis/drive_api_parser.h"
-#include "chrome/browser/google_apis/drive_entry_kinds.h"
 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h"
 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_util.h"
+#include "chrome/browser/sync_file_system/drive_backend/leveldb_wrapper.h"
 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.pb.h"
+#include "chrome/browser/sync_file_system/drive_backend/metadata_database_index.h"
+#include "chrome/browser/sync_file_system/drive_backend/metadata_database_index_interface.h"
+#include "chrome/browser/sync_file_system/drive_backend/metadata_database_index_on_disk.h"
 #include "chrome/browser/sync_file_system/drive_backend/metadata_db_migration_util.h"
 #include "chrome/browser/sync_file_system/logger.h"
 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
+#include "google_apis/drive/drive_api_parser.h"
 #include "third_party/leveldatabase/src/include/leveldb/db.h"
+#include "third_party/leveldatabase/src/include/leveldb/env.h"
+#include "third_party/leveldatabase/src/include/leveldb/status.h"
 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
 #include "webkit/common/fileapi/file_system_util.h"
 
 namespace sync_file_system {
 namespace drive_backend {
 
-struct DatabaseContents {
-  scoped_ptr<ServiceMetadata> service_metadata;
-  ScopedVector<FileMetadata> file_metadata;
-  ScopedVector<FileTracker> file_trackers;
-};
-
 namespace {
 
-typedef MetadataDatabase::FileByID FileByID;
-typedef MetadataDatabase::TrackerByID TrackerByID;
-typedef MetadataDatabase::TrackersByParentAndTitle TrackersByParentAndTitle;
-typedef MetadataDatabase::TrackersByTitle TrackersByTitle;
+// Command line flag to enable on-disk indexing.
+const char kEnableMetadataDatabaseOnDisk[] = "enable-syncfs-on-disk-indexing";
 
-bool IsAppRoot(const FileTracker& tracker) {
-  return tracker.tracker_kind() == TRACKER_KIND_APP_ROOT ||
-      tracker.tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT;
-}
+void EmptyStatusCallback(SyncStatusCode status) {}
+
+std::string FileKindToString(FileKind file_kind) {
+  switch (file_kind) {
+    case FILE_KIND_UNSUPPORTED:
+      return "unsupported";
+    case FILE_KIND_FILE:
+      return "file";
+    case FILE_KIND_FOLDER:
+      return "folder";
+  }
 
-std::string RemovePrefix(const std::string& str, const std::string& prefix) {
-  if (StartsWithASCII(str, prefix, true))
-    return str.substr(prefix.size());
-  return str;
+  NOTREACHED();
+  return "unknown";
 }
 
 base::FilePath ReverseConcatPathComponents(
@@ -82,119 +86,139 @@ base::FilePath ReverseConcatPathComponents(
   return base::FilePath(result).NormalizePathSeparators();
 }
 
-void CreateInitialSyncRootTracker(
-    int64 tracker_id,
+void PopulateFileDetailsByFileResource(
     const google_apis::FileResource& file_resource,
-    scoped_ptr<FileMetadata>* file_out,
-    scoped_ptr<FileTracker>* tracker_out) {
-  FileDetails details;
-  PopulateFileDetailsByFileResource(file_resource, &details);
+    FileDetails* details) {
+  details->clear_parent_folder_ids();
+  for (std::vector<google_apis::ParentReference>::const_iterator itr =
+           file_resource.parents().begin();
+       itr != file_resource.parents().end();
+       ++itr) {
+    details->add_parent_folder_ids(itr->file_id());
+  }
+  details->set_title(file_resource.title());
 
+  if (file_resource.IsDirectory())
+    details->set_file_kind(FILE_KIND_FOLDER);
+  else if (file_resource.IsHostedDocument())
+    details->set_file_kind(FILE_KIND_UNSUPPORTED);
+  else
+    details->set_file_kind(FILE_KIND_FILE);
+
+  details->set_md5(file_resource.md5_checksum());
+  details->set_etag(file_resource.etag());
+  details->set_creation_time(file_resource.created_date().ToInternalValue());
+  details->set_modification_time(
+      file_resource.modified_date().ToInternalValue());
+  details->set_missing(file_resource.labels().is_trashed());
+}
+
+scoped_ptr<FileMetadata> CreateFileMetadataFromFileResource(
+    int64 change_id,
+    const google_apis::FileResource& resource) {
   scoped_ptr<FileMetadata> file(new FileMetadata);
-  file->set_file_id(file_resource.file_id());
-  *file->mutable_details() = details;
+  file->set_file_id(resource.file_id());
 
-  scoped_ptr<FileTracker> tracker(new FileTracker);
-  tracker->set_tracker_id(tracker_id);
-  tracker->set_file_id(file_resource.file_id());
-  tracker->set_parent_tracker_id(0);
-  tracker->set_tracker_kind(TRACKER_KIND_REGULAR);
-  tracker->set_dirty(false);
-  tracker->set_active(true);
-  tracker->set_needs_folder_listing(false);
-  *tracker->mutable_synced_details() = details;
+  FileDetails* details = file->mutable_details();
+  details->set_change_id(change_id);
 
-  *file_out = file.Pass();
-  *tracker_out = tracker.Pass();
-}
+  if (resource.labels().is_trashed()) {
+    details->set_missing(true);
+    return file.Pass();
+  }
 
-void CreateInitialAppRootTracker(
-    int64 tracker_id,
-    const FileTracker& parent_tracker,
-    const google_apis::FileResource& file_resource,
-    scoped_ptr<FileMetadata>* file_out,
-    scoped_ptr<FileTracker>* tracker_out) {
-  FileDetails details;
-  PopulateFileDetailsByFileResource(file_resource, &details);
+  PopulateFileDetailsByFileResource(resource, details);
+  return file.Pass();
+}
 
+scoped_ptr<FileMetadata> CreateFileMetadataFromChangeResource(
+    const google_apis::ChangeResource& change) {
   scoped_ptr<FileMetadata> file(new FileMetadata);
-  file->set_file_id(file_resource.file_id());
-  *file->mutable_details() = details;
+  file->set_file_id(change.file_id());
 
-  scoped_ptr<FileTracker> tracker(new FileTracker);
-  tracker->set_tracker_id(tracker_id);
-  tracker->set_parent_tracker_id(parent_tracker.tracker_id());
-  tracker->set_file_id(file_resource.file_id());
-  tracker->set_tracker_kind(TRACKER_KIND_REGULAR);
-  tracker->set_dirty(false);
-  tracker->set_active(false);
-  tracker->set_needs_folder_listing(false);
-  *tracker->mutable_synced_details() = details;
+  FileDetails* details = file->mutable_details();
+  details->set_change_id(change.change_id());
 
-  *file_out = file.Pass();
-  *tracker_out = tracker.Pass();
-}
+  if (change.is_deleted()) {
+    details->set_missing(true);
+    return file.Pass();
+  }
 
-void AdaptLevelDBStatusToSyncStatusCode(const SyncStatusCallback& callback,
-                                        const leveldb::Status& status) {
-  callback.Run(LevelDBStatusToSyncStatusCode(status));
+  PopulateFileDetailsByFileResource(*change.file(), details);
+  return file.Pass();
 }
 
-void PutFileDeletionToBatch(const std::string& file_id,
-                            leveldb::WriteBatch* batch) {
-  batch->Delete(kFileMetadataKeyPrefix + file_id);
-}
+scoped_ptr<FileMetadata> CreateDeletedFileMetadata(
+    int64 change_id,
+    const std::string& file_id) {
+  scoped_ptr<FileMetadata> file(new FileMetadata);
+  file->set_file_id(file_id);
 
-void PutTrackerDeletionToBatch(int64 tracker_id, leveldb::WriteBatch* batch) {
-  batch->Delete(kFileTrackerKeyPrefix + base::Int64ToString(tracker_id));
+  FileDetails* details = file->mutable_details();
+  details->set_change_id(change_id);
+  details->set_missing(true);
+  return file.Pass();
 }
 
-template <typename OutputIterator>
-OutputIterator PushChildTrackersToContainer(
-    const TrackersByParentAndTitle& trackers_by_parent,
+scoped_ptr<FileTracker> CreateSyncRootTracker(
+    int64 tracker_id,
+    const FileMetadata& sync_root_metadata) {
+  scoped_ptr<FileTracker> sync_root_tracker(new FileTracker);
+  sync_root_tracker->set_tracker_id(tracker_id);
+  sync_root_tracker->set_file_id(sync_root_metadata.file_id());
+  sync_root_tracker->set_parent_tracker_id(0);
+  sync_root_tracker->set_tracker_kind(TRACKER_KIND_REGULAR);
+  sync_root_tracker->set_dirty(false);
+  sync_root_tracker->set_active(true);
+  sync_root_tracker->set_needs_folder_listing(false);
+  *sync_root_tracker->mutable_synced_details() = sync_root_metadata.details();
+  return sync_root_tracker.Pass();
+}
+
+scoped_ptr<FileTracker> CreateInitialAppRootTracker(
+    int64 tracker_id,
     int64 parent_tracker_id,
-    OutputIterator target_itr) {
-  TrackersByParentAndTitle::const_iterator found =
-      trackers_by_parent.find(parent_tracker_id);
-  if (found == trackers_by_parent.end())
-    return target_itr;
-
-  for (TrackersByTitle::const_iterator title_itr = found->second.begin();
-       title_itr != found->second.end(); ++title_itr) {
-    const TrackerSet& trackers = title_itr->second;
-    for (TrackerSet::const_iterator tracker_itr = trackers.begin();
-         tracker_itr != trackers.end(); ++tracker_itr) {
-      *target_itr = (*tracker_itr)->tracker_id();
-      ++target_itr;
-    }
-  }
-  return target_itr;
+    const FileMetadata& app_root_metadata) {
+  scoped_ptr<FileTracker> app_root_tracker(new FileTracker);
+  app_root_tracker->set_tracker_id(tracker_id);
+  app_root_tracker->set_parent_tracker_id(parent_tracker_id);
+  app_root_tracker->set_file_id(app_root_metadata.file_id());
+  app_root_tracker->set_tracker_kind(TRACKER_KIND_REGULAR);
+  app_root_tracker->set_dirty(false);
+  app_root_tracker->set_active(false);
+  app_root_tracker->set_needs_folder_listing(false);
+  *app_root_tracker->mutable_synced_details() = app_root_metadata.details();
+  return app_root_tracker.Pass();
 }
 
-std::string GetTrackerTitle(const FileTracker& tracker) {
-  if (tracker.has_synced_details())
-    return tracker.synced_details().title();
-  return std::string();
+scoped_ptr<FileTracker> CloneFileTracker(const FileTracker* obj) {
+  if (!obj)
+    return scoped_ptr<FileTracker>();
+  return scoped_ptr<FileTracker>(new FileTracker(*obj));
 }
 
 // Returns true if |db| has no content.
-bool IsDatabaseEmpty(leveldb::DB* db) {
+bool IsDatabaseEmpty(LevelDBWrapper* db) {
   DCHECK(db);
-  scoped_ptr<leveldb::Iterator> itr(db->NewIterator(leveldb::ReadOptions()));
+  scoped_ptr<LevelDBWrapper::Iterator> itr(db->NewIterator());
   itr->SeekToFirst();
   return !itr->Valid();
 }
 
 SyncStatusCode OpenDatabase(const base::FilePath& path,
-                            scoped_ptr<leveldb::DB>* db_out,
+                            leveldb::Env* env_override,
+                            scoped_ptr<LevelDBWrapper>* db_out,
                             bool* created) {
   base::ThreadRestrictions::AssertIOAllowed();
   DCHECK(db_out);
   DCHECK(created);
+  DCHECK(path.IsAbsolute());
 
   leveldb::Options options;
   options.max_open_files = 0;  // Use minimum.
   options.create_if_missing = true;
+  if (env_override)
+    options.env = env_override;
   leveldb::DB* db = NULL;
   leveldb::Status db_status =
       leveldb::DB::Open(options, path.AsUTF8Unsafe(), &db);
@@ -204,17 +228,17 @@ SyncStatusCode OpenDatabase(const base::FilePath& path,
     return status;
   }
 
-  *created = IsDatabaseEmpty(db);
-  db_out->reset(db);
+  db_out->reset(new LevelDBWrapper(make_scoped_ptr(db)));
+  *created = IsDatabaseEmpty(db_out->get());
   return status;
 }
 
-SyncStatusCode MigrateDatabaseIfNeeded(leveldb::DB* db) {
+SyncStatusCode MigrateDatabaseIfNeeded(LevelDBWrapper* db) {
+  // See metadata_database_index.cc for the database schema.
   base::ThreadRestrictions::AssertIOAllowed();
   DCHECK(db);
   std::string value;
-  leveldb::Status status =
-      db->Get(leveldb::ReadOptions(), kDatabaseVersionKey, &value);
+  leveldb::Status status = db->Get(kDatabaseVersionKey, &value);
   int64 version = 0;
   if (status.ok()) {
     if (!base::StringToInt64(value, &version))
@@ -226,10 +250,10 @@ SyncStatusCode MigrateDatabaseIfNeeded(leveldb::DB* db) {
 
   switch (version) {
     case 0:
-      drive_backend::MigrateDatabaseFromV0ToV1(db);
+      drive_backend::MigrateDatabaseFromV0ToV1(db->GetLevelDB());
       // fall-through
     case 1:
-      drive_backend::MigrateDatabaseFromV1ToV2(db);
+      drive_backend::MigrateDatabaseFromV1ToV2(db->GetLevelDB());
       // fall-through
     case 2:
       // TODO(tzik): Migrate database from version 2 to 3.
@@ -246,262 +270,355 @@ SyncStatusCode MigrateDatabaseIfNeeded(leveldb::DB* db) {
       // fall-through
     case 3:
       DCHECK_EQ(3, kCurrentDatabaseVersion);
+      // If MetadataDatabaseOnDisk is enabled, migration will be done in
+      // MetadataDatabaseOnDisk::Create().
+      // TODO(peria): Move the migration code (from v3 to v4) here.
+      return SYNC_STATUS_OK;
+    case 4:
+      if (!CommandLine::ForCurrentProcess()->HasSwitch(
+              kEnableMetadataDatabaseOnDisk)) {
+        MigrateDatabaseFromV4ToV3(db->GetLevelDB());
+      }
       return SYNC_STATUS_OK;
     default:
       return SYNC_DATABASE_ERROR_FAILED;
   }
 }
 
-SyncStatusCode WriteVersionInfo(leveldb::DB* db) {
-  base::ThreadRestrictions::AssertIOAllowed();
-  DCHECK(db);
-  return LevelDBStatusToSyncStatusCode(
-      db->Put(leveldb::WriteOptions(),
-              kDatabaseVersionKey,
-              base::Int64ToString(kCurrentDatabaseVersion)));
+bool HasInvalidTitle(const std::string& title) {
+  return title.empty() ||
+      title.find('/') != std::string::npos ||
+      title.find('\\') != std::string::npos;
 }
 
-SyncStatusCode ReadDatabaseContents(leveldb::DB* db,
-                                    DatabaseContents* contents) {
-  base::ThreadRestrictions::AssertIOAllowed();
-  DCHECK(db);
-  DCHECK(contents);
-
-  scoped_ptr<leveldb::Iterator> itr(db->NewIterator(leveldb::ReadOptions()));
-  for (itr->SeekToFirst(); itr->Valid(); itr->Next()) {
-    std::string key = itr->key().ToString();
-    std::string value = itr->value().ToString();
-    if (key == kServiceMetadataKey) {
-      scoped_ptr<ServiceMetadata> service_metadata(new ServiceMetadata);
-      if (!service_metadata->ParseFromString(value)) {
-        util::Log(logging::LOG_WARNING, FROM_HERE,
-                  "Failed to parse SyncServiceMetadata");
-        continue;
-      }
-
-      contents->service_metadata = service_metadata.Pass();
+void MarkTrackerSetDirty(const TrackerIDSet& trackers,
+                         MetadataDatabaseIndexInterface* index) {
+  for (TrackerIDSet::const_iterator itr = trackers.begin();
+       itr != trackers.end(); ++itr) {
+    scoped_ptr<FileTracker> tracker(new FileTracker);
+    index->GetFileTracker(*itr, tracker.get());
+    if (tracker->dirty())
       continue;
+    tracker->set_dirty(true);
+    index->StoreFileTracker(tracker.Pass());
+  }
+}
+
+void MarkTrackersDirtyByPath(int64 parent_tracker_id,
+                             const std::string& title,
+                             MetadataDatabaseIndexInterface* index) {
+  if (parent_tracker_id == kInvalidTrackerID || title.empty())
+    return;
+  MarkTrackerSetDirty(
+      index->GetFileTrackerIDsByParentAndTitle(parent_tracker_id, title),
+      index);
+}
+
+void MarkTrackersDirtyByFileID(const std::string& file_id,
+                               MetadataDatabaseIndexInterface* index) {
+  MarkTrackerSetDirty(index->GetFileTrackerIDsByFileID(file_id), index);
+}
+
+void MarkTrackersDirtyRecursively(int64 root_tracker_id,
+                                  MetadataDatabaseIndexInterface* index) {
+  std::vector<int64> stack;
+  stack.push_back(root_tracker_id);
+  while (!stack.empty()) {
+    int64 tracker_id = stack.back();
+    stack.pop_back();
+    AppendContents(index->GetFileTrackerIDsByParent(tracker_id), &stack);
+
+    scoped_ptr<FileTracker> tracker(new FileTracker);
+    index->GetFileTracker(tracker_id, tracker.get());
+    tracker->set_dirty(true);
+
+    index->StoreFileTracker(tracker.Pass());
+  }
+}
+
+void RemoveAllDescendantTrackers(int64 root_tracker_id,
+                                 MetadataDatabaseIndexInterface* index) {
+  std::vector<int64> pending_trackers;
+  AppendContents(index->GetFileTrackerIDsByParent(root_tracker_id),
+                 &pending_trackers);
+
+  std::vector<int64> to_be_removed;
+
+  // List trackers to remove.
+  while (!pending_trackers.empty()) {
+    int64 tracker_id = pending_trackers.back();
+    pending_trackers.pop_back();
+    AppendContents(index->GetFileTrackerIDsByParent(tracker_id),
+                   &pending_trackers);
+    to_be_removed.push_back(tracker_id);
+  }
+
+  // Remove trackers in the reversed order.
+  base::hash_set<std::string> affected_file_ids;
+  for (std::vector<int64>::reverse_iterator itr = to_be_removed.rbegin();
+       itr != to_be_removed.rend(); ++itr) {
+    FileTracker tracker;
+    index->GetFileTracker(*itr, &tracker);
+    affected_file_ids.insert(tracker.file_id());
+    index->RemoveFileTracker(*itr);
+  }
+
+  for (base::hash_set<std::string>::iterator itr = affected_file_ids.begin();
+       itr != affected_file_ids.end(); ++itr) {
+    TrackerIDSet trackers = index->GetFileTrackerIDsByFileID(*itr);
+    if (trackers.empty()) {
+      // Remove metadata that no longer has any tracker.
+      index->RemoveFileMetadata(*itr);
+    } else {
+      MarkTrackerSetDirty(trackers, index);
     }
+  }
+}
 
-    if (StartsWithASCII(key, kFileMetadataKeyPrefix, true)) {
-      std::string file_id = RemovePrefix(key, kFileMetadataKeyPrefix);
+bool FilterFileTrackersByParent(
+    const MetadataDatabaseIndexInterface* index,
+    const TrackerIDSet& trackers,
+    int64 parent_tracker_id,
+    FileTracker* tracker_out) {
+  FileTracker tracker;
+  for (TrackerIDSet::const_iterator itr = trackers.begin();
+       itr != trackers.end(); ++itr) {
+    if (!index->GetFileTracker(*itr, &tracker)) {
+      NOTREACHED();
+      continue;
+    }
 
-      scoped_ptr<FileMetadata> file(new FileMetadata);
-      if (!file->ParseFromString(itr->value().ToString())) {
-        util::Log(logging::LOG_WARNING, FROM_HERE,
-                  "Failed to parse a FileMetadata");
-        continue;
-      }
+    if (tracker.parent_tracker_id() == parent_tracker_id) {
+      if (tracker_out)
+        tracker_out->CopyFrom(tracker);
+      return true;
+    }
+  }
+  return false;
+}
 
-      contents->file_metadata.push_back(file.release());
+bool FilterFileTrackersByParentAndTitle(
+    const MetadataDatabaseIndexInterface* index,
+    const TrackerIDSet& trackers,
+    int64 parent_tracker_id,
+    const std::string& title,
+    FileTracker* result) {
+  bool found = false;
+  for (TrackerIDSet::const_iterator itr = trackers.begin();
+       itr != trackers.end(); ++itr) {
+    FileTracker tracker;
+    if (!index->GetFileTracker(*itr, &tracker)) {
+      NOTREACHED();
       continue;
     }
 
-    if (StartsWithASCII(key, kFileTrackerKeyPrefix, true)) {
-      int64 tracker_id = 0;
-      if (!base::StringToInt64(RemovePrefix(key, kFileTrackerKeyPrefix),
-                               &tracker_id)) {
-        util::Log(logging::LOG_WARNING, FROM_HERE,
-                  "Failed to parse TrackerID");
-        continue;
-      }
+    if (tracker.parent_tracker_id() != parent_tracker_id)
+      continue;
 
-      scoped_ptr<FileTracker> tracker(new FileTracker);
-      if (!tracker->ParseFromString(itr->value().ToString())) {
-        util::Log(logging::LOG_WARNING, FROM_HERE,
-                  "Failed to parse a Tracker");
-        continue;
-      }
-      contents->file_trackers.push_back(tracker.release());
+    if (tracker.has_synced_details() &&
+        tracker.synced_details().title() != title)
       continue;
+
+    // Prioritize trackers that has |synced_details| when |trackers| has
+    // multiple candidates.
+    if (!found || tracker.has_synced_details()) {
+      found = true;
+      if (result)
+        result->CopyFrom(tracker);
+      if (!result || result->has_synced_details())
+        return found;
     }
   }
 
-  return SYNC_STATUS_OK;
+  return found;
 }
 
-SyncStatusCode InitializeServiceMetadata(DatabaseContents* contents,
-                                         leveldb::WriteBatch* batch) {
-  if (!contents->service_metadata) {
-    contents->service_metadata.reset(new ServiceMetadata);
-    contents->service_metadata->set_next_tracker_id(1);
+bool FilterFileTrackersByFileID(
+    const MetadataDatabaseIndexInterface* index,
+    const TrackerIDSet& trackers,
+    const std::string& file_id,
+    FileTracker* tracker_out) {
+  FileTracker tracker;
+  for (TrackerIDSet::const_iterator itr = trackers.begin();
+       itr != trackers.end(); ++itr) {
+    if (!index->GetFileTracker(*itr, &tracker)) {
+      NOTREACHED();
+      continue;
+    }
 
-    std::string value;
-    contents->service_metadata->SerializeToString(&value);
-    batch->Put(kServiceMetadataKey, value);
+    if (tracker.file_id() == file_id) {
+      if (tracker_out)
+        tracker_out->CopyFrom(tracker);
+      return true;
+    }
   }
-  return SYNC_STATUS_OK;
+  return false;
 }
 
-SyncStatusCode RemoveUnreachableItems(DatabaseContents* contents,
-                                      leveldb::WriteBatch* batch) {
-  TrackerByID unvisited_trackers;
-  typedef std::map<int64, std::set<FileTracker*> > TrackersByParent;
-  TrackersByParent trackers_by_parent;
+enum DirtyingOption {
+  MARK_NOTHING_DIRTY = 0,
+  MARK_ITSELF_DIRTY = 1 << 0,
+  MARK_SAME_FILE_ID_TRACKERS_DIRTY = 1 << 1,
+  MARK_SAME_PATH_TRACKERS_DIRTY = 1 << 2,
+};
 
-  for (ScopedVector<FileTracker>::iterator itr =
-           contents->file_trackers.begin();
-       itr != contents->file_trackers.end();
-       ++itr) {
-    FileTracker* tracker = *itr;
-    DCHECK(!ContainsKey(unvisited_trackers, tracker->tracker_id()));
-    unvisited_trackers[tracker->tracker_id()] = tracker;
-    if (tracker->parent_tracker_id())
-      trackers_by_parent[tracker->parent_tracker_id()].insert(tracker);
-  }
-
-  // Traverse synced tracker tree. Take only active items, app-root and their
-  // children. Drop unreachable items.
-  ScopedVector<FileTracker> reachable_trackers;
-  std::stack<int64> pending;
-  if (contents->service_metadata->sync_root_tracker_id())
-    pending.push(contents->service_metadata->sync_root_tracker_id());
-
-  while (!pending.empty()) {
-    int64 tracker_id = pending.top();
-    pending.pop();
-
-    {
-      TrackerByID::iterator found = unvisited_trackers.find(tracker_id);
-      if (found == unvisited_trackers.end())
-        continue;
+void ActivateFileTracker(int64 tracker_id,
+                         int dirtying_options,
+                         MetadataDatabaseIndexInterface* index) {
+  DCHECK(dirtying_options == MARK_NOTHING_DIRTY ||
+         dirtying_options == MARK_ITSELF_DIRTY);
 
-      FileTracker* tracker = found->second;
-      unvisited_trackers.erase(found);
-      reachable_trackers.push_back(tracker);
+  scoped_ptr<FileTracker> tracker(new FileTracker);
+  index->GetFileTracker(tracker_id, tracker.get());
+  tracker->set_active(true);
+  if (dirtying_options & MARK_ITSELF_DIRTY) {
+    tracker->set_dirty(true);
+    tracker->set_needs_folder_listing(
+        tracker->has_synced_details() &&
+        tracker->synced_details().file_kind() == FILE_KIND_FOLDER);
+  } else {
+    tracker->set_dirty(false);
+    tracker->set_needs_folder_listing(false);
+  }
 
-      if (!tracker->active())
-        continue;
-    }
+  index->StoreFileTracker(tracker.Pass());
+}
 
-    TrackersByParent::iterator found = trackers_by_parent.find(tracker_id);
-    if (found == trackers_by_parent.end())
-      continue;
+void DeactivateFileTracker(int64 tracker_id,
+                           int dirtying_options,
+                           MetadataDatabaseIndexInterface* index) {
+  RemoveAllDescendantTrackers(tracker_id, index);
 
-    for (std::set<FileTracker*>::const_iterator itr =
-             found->second.begin();
-         itr != found->second.end();
-         ++itr)
-      pending.push((*itr)->tracker_id());
-  }
-
-  // Delete all unreachable trackers.
-  for (TrackerByID::iterator itr = unvisited_trackers.begin();
-       itr != unvisited_trackers.end(); ++itr) {
-    FileTracker* tracker = itr->second;
-    PutTrackerDeletionToBatch(tracker->tracker_id(), batch);
-    delete tracker;
-  }
-  unvisited_trackers.clear();
-
-  // |reachable_trackers| contains all files/folders reachable from sync-root
-  // folder via active folders and app-root folders.
-  // Update the tracker set in database contents with the reachable tracker set.
-  contents->file_trackers.weak_clear();
-  contents->file_trackers.swap(reachable_trackers);
-
-  // Do the similar traverse for FileMetadata and remove FileMetadata that don't
-  // have reachable trackers.
-  FileByID unreferred_files;
-  for (ScopedVector<FileMetadata>::const_iterator itr =
-           contents->file_metadata.begin();
-       itr != contents->file_metadata.end();
-       ++itr) {
-    unreferred_files.insert(std::make_pair((*itr)->file_id(), *itr));
-  }
+  scoped_ptr<FileTracker> tracker(new FileTracker);
+  index->GetFileTracker(tracker_id, tracker.get());
 
-  ScopedVector<FileMetadata> referred_files;
-  for (ScopedVector<FileTracker>::const_iterator itr =
-           contents->file_trackers.begin();
-       itr != contents->file_trackers.end();
-       ++itr) {
-    FileByID::iterator found = unreferred_files.find((*itr)->file_id());
-    if (found != unreferred_files.end()) {
-      referred_files.push_back(found->second);
-      unreferred_files.erase(found);
-    }
+  if (dirtying_options & MARK_SAME_FILE_ID_TRACKERS_DIRTY)
+    MarkTrackersDirtyByFileID(tracker->file_id(), index);
+  if (dirtying_options & MARK_SAME_PATH_TRACKERS_DIRTY) {
+    MarkTrackersDirtyByPath(tracker->parent_tracker_id(),
+                            GetTrackerTitle(*tracker), index);
   }
 
-  for (FileByID::iterator itr = unreferred_files.begin();
-       itr != unreferred_files.end(); ++itr) {
-    FileMetadata* file = itr->second;
-    PutFileDeletionToBatch(file->file_id(), batch);
-    delete file;
-  }
-  unreferred_files.clear();
+  tracker->set_dirty(dirtying_options & MARK_ITSELF_DIRTY);
+  tracker->set_active(false);
+  index->StoreFileTracker(tracker.Pass());
+}
 
-  contents->file_metadata.weak_clear();
-  contents->file_metadata.swap(referred_files);
+void RemoveFileTracker(int64 tracker_id,
+                       int dirtying_options,
+                       MetadataDatabaseIndexInterface* index) {
+  DCHECK(!(dirtying_options & MARK_ITSELF_DIRTY));
 
-  return SYNC_STATUS_OK;
-}
+  FileTracker tracker;
+  if (!index->GetFileTracker(tracker_id, &tracker))
+    return;
 
-template <typename Container, typename Key, typename Value>
-bool FindItem(const Container& container, const Key& key, Value* value) {
-  typename Container::const_iterator found = container.find(key);
-  if (found == container.end())
-    return false;
-  if (value)
-    *value = *found->second;
-  return true;
-}
+  std::string file_id = tracker.file_id();
+  int64 parent_tracker_id = tracker.parent_tracker_id();
+  std::string title = GetTrackerTitle(tracker);
 
-template <typename Container, typename Key>
-typename Container::mapped_type FindAndEraseItem(Container* container,
-                                                 const Key& key) {
-  typedef typename Container::mapped_type Value;
-  typename Container::iterator found = container->find(key);
-  if (found == container->end())
-    return Value();
+  RemoveAllDescendantTrackers(tracker_id, index);
+  index->RemoveFileTracker(tracker_id);
 
-  Value result = found->second;
-  container->erase(found);
-  return result;
-}
+  if (dirtying_options & MARK_SAME_FILE_ID_TRACKERS_DIRTY)
+    MarkTrackersDirtyByFileID(file_id, index);
+  if (dirtying_options & MARK_SAME_PATH_TRACKERS_DIRTY)
+    MarkTrackersDirtyByPath(parent_tracker_id, title, index);
 
-void RunSoon(const tracked_objects::Location& from_here,
-             const base::Closure& closure) {
-  base::MessageLoopProxy::current()->PostTask(from_here, closure);
+  if (index->GetFileTrackerIDsByFileID(file_id).empty()) {
+    index->RemoveFileMetadata(file_id);
+  }
 }
 
 }  // namespace
 
-bool MetadataDatabase::DirtyTrackerComparator::operator()(
-    const FileTracker* left,
-    const FileTracker* right) const {
-  return left->tracker_id() < right->tracker_id();
-}
+struct MetadataDatabase::CreateParam {
+  scoped_refptr<base::SequencedTaskRunner> worker_task_runner;
+  base::FilePath database_path;
+  leveldb::Env* env_override;
+
+  CreateParam(base::SequencedTaskRunner* worker_task_runner,
+              const base::FilePath& database_path,
+              leveldb::Env* env_override)
+      : worker_task_runner(worker_task_runner),
+        database_path(database_path),
+        env_override(env_override) {
+  }
+};
 
 // static
-void MetadataDatabase::Create(base::SequencedTaskRunner* task_runner,
+void MetadataDatabase::Create(base::SequencedTaskRunner* worker_task_runner,
                               const base::FilePath& database_path,
+                              leveldb::Env* env_override,
                               const CreateCallback& callback) {
-  task_runner->PostTask(FROM_HERE, base::Bind(
-      &CreateOnTaskRunner,
-      base::MessageLoopProxy::current(),
-      make_scoped_refptr(task_runner),
-      database_path, callback));
+  worker_task_runner->PostTask(FROM_HERE, base::Bind(
+      &MetadataDatabase::CreateOnWorkerTaskRunner,
+      base::Passed(make_scoped_ptr(new CreateParam(
+          worker_task_runner,
+          database_path,
+          env_override))),
+      callback));
+}
+
+// static
+SyncStatusCode MetadataDatabase::CreateForTesting(
+    scoped_ptr<LevelDBWrapper> db,
+    scoped_ptr<MetadataDatabase>* metadata_database_out) {
+  scoped_ptr<MetadataDatabase> metadata_database(
+      new MetadataDatabase(base::ThreadTaskRunnerHandle::Get(),
+                           base::FilePath(), NULL));
+  metadata_database->db_ = db.Pass();
+  SyncStatusCode status = metadata_database->Initialize();
+  if (status == SYNC_STATUS_OK)
+    *metadata_database_out = metadata_database.Pass();
+  return status;
 }
 
 MetadataDatabase::~MetadataDatabase() {
-  task_runner_->DeleteSoon(FROM_HERE, db_.release());
-  STLDeleteContainerPairSecondPointers(
-      file_by_id_.begin(), file_by_id_.end());
-  STLDeleteContainerPairSecondPointers(
-      tracker_by_id_.begin(), tracker_by_id_.end());
+  worker_task_runner_->DeleteSoon(FROM_HERE, db_.release());
+}
+
+// static
+void MetadataDatabase::ClearDatabase(
+    scoped_ptr<MetadataDatabase> metadata_database) {
+  DCHECK(metadata_database);
+  scoped_refptr<base::SequencedTaskRunner> worker_task_runner =
+      metadata_database->worker_task_runner_;
+  base::FilePath database_path = metadata_database->database_path_;
+  DCHECK(!database_path.empty());
+  metadata_database.reset();
+
+  worker_task_runner->PostTask(
+      FROM_HERE,
+      base::Bind(base::IgnoreResult(base::DeleteFile),
+                 database_path, true /* recursive */));
 }
 
-int64 MetadataDatabase::GetLargestChangeID() const {
-  return service_metadata_->largest_change_id();
+int64 MetadataDatabase::GetLargestFetchedChangeID() const {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+  return index_->GetLargestChangeID();
 }
 
 int64 MetadataDatabase::GetSyncRootTrackerID() const {
-  return service_metadata_->sync_root_tracker_id();
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+  return index_->GetSyncRootTrackerID();
+}
+
+int64 MetadataDatabase::GetLargestKnownChangeID() const {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+  DCHECK_LE(GetLargestFetchedChangeID(), largest_known_change_id_);
+  return largest_known_change_id_;
+}
+
+void MetadataDatabase::UpdateLargestKnownChangeID(int64 change_id) {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+  if (largest_known_change_id_ < change_id)
+    largest_known_change_id_ = change_id;
 }
 
 bool MetadataDatabase::HasSyncRoot() const {
-  return service_metadata_->has_sync_root_tracker_id() &&
-      !!service_metadata_->sync_root_tracker_id();
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+  return index_->GetSyncRootTrackerID() != kInvalidTrackerID;
 }
 
 void MetadataDatabase::PopulateInitialData(
@@ -509,210 +626,231 @@ void MetadataDatabase::PopulateInitialData(
     const google_apis::FileResource& sync_root_folder,
     const ScopedVector<google_apis::FileResource>& app_root_folders,
     const SyncStatusCallback& callback) {
-  DCHECK(tracker_by_id_.empty());
-  DCHECK(file_by_id_.empty());
-
-  scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
-  service_metadata_->set_largest_change_id(largest_change_id);
-
-  FileTracker* sync_root_tracker = NULL;
-  int64 sync_root_tracker_id = 0;
-  {
-    scoped_ptr<FileMetadata> folder;
-    scoped_ptr<FileTracker> tracker;
-    CreateInitialSyncRootTracker(GetNextTrackerID(batch.get()),
-                                 sync_root_folder,
-                                 &folder,
-                                 &tracker);
-    std::string sync_root_folder_id = folder->file_id();
-    sync_root_tracker = tracker.get();
-    sync_root_tracker_id = tracker->tracker_id();
-
-    PutFileToBatch(*folder, batch.get());
-    PutTrackerToBatch(*tracker, batch.get());
-
-    service_metadata_->set_sync_root_tracker_id(tracker->tracker_id());
-    PutServiceMetadataToBatch(*service_metadata_, batch.get());
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
 
-    trackers_by_file_id_[folder->file_id()].Insert(tracker.get());
+  index_->SetLargestChangeID(largest_change_id);
+  UpdateLargestKnownChangeID(largest_change_id);
 
-    file_by_id_[sync_root_folder_id] = folder.release();
-    tracker_by_id_[sync_root_tracker_id] = tracker.release();
-  }
-
-  for (ScopedVector<google_apis::FileResource>::const_iterator itr =
-           app_root_folders.begin();
-       itr != app_root_folders.end();
-       ++itr) {
-    const google_apis::FileResource& folder_resource = **itr;
-    scoped_ptr<FileMetadata> folder;
-    scoped_ptr<FileTracker> tracker;
-    CreateInitialAppRootTracker(GetNextTrackerID(batch.get()),
-                                *sync_root_tracker,
-                                folder_resource,
-                                &folder,
-                                &tracker);
-    std::string title = folder->details().title();
-    std::string folder_id = folder->file_id();
-    int64 tracker_id = tracker->tracker_id();
+  AttachSyncRoot(sync_root_folder);
+  for (size_t i = 0; i < app_root_folders.size(); ++i)
+    AttachInitialAppRoot(*app_root_folders[i]);
 
-    PutFileToBatch(*folder, batch.get());
-    PutTrackerToBatch(*tracker, batch.get());
+  WriteToDatabase(callback);
+}
 
-    trackers_by_file_id_[folder_id].Insert(tracker.get());
-    trackers_by_parent_and_title_[sync_root_tracker_id][title]
-        .Insert(tracker.get());
+bool MetadataDatabase::IsAppEnabled(const std::string& app_id) const {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
 
-    file_by_id_[folder_id] = folder.release();
-    tracker_by_id_[tracker_id] = tracker.release();
-  }
+  int64 tracker_id = index_->GetAppRootTracker(app_id);
+  if (tracker_id == kInvalidTrackerID)
+    return false;
 
-  WriteToDatabase(batch.Pass(), callback);
+  FileTracker tracker;
+  if (!index_->GetFileTracker(tracker_id, &tracker))
+    return false;
+  return tracker.tracker_kind() == TRACKER_KIND_APP_ROOT;
 }
 
-
 void MetadataDatabase::RegisterApp(const std::string& app_id,
                                    const std::string& folder_id,
                                    const SyncStatusCallback& callback) {
-  if (FindAppRootTracker(app_id, NULL)) {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+
+  if (index_->GetAppRootTracker(app_id)) {
     // The app-root is already registered.
-    RunSoon(FROM_HERE, base::Bind(callback, SYNC_STATUS_OK));
+    worker_task_runner_->PostTask(
+        FROM_HERE,
+        base::Bind(callback, SYNC_STATUS_OK));
+    return;
+  }
+
+  TrackerIDSet trackers = index_->GetFileTrackerIDsByFileID(folder_id);
+  if (trackers.empty()) {
+    worker_task_runner_->PostTask(
+        FROM_HERE,
+        base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND));
     return;
   }
 
-  TrackerSet trackers;
-  if (!FindTrackersByFileID(folder_id, &trackers) || trackers.has_active()) {
+  if (trackers.has_active()) {
     // The folder is tracked by another tracker.
     util::Log(logging::LOG_WARNING, FROM_HERE,
               "Failed to register App for %s", app_id.c_str());
-    RunSoon(FROM_HERE, base::Bind(callback, SYNC_STATUS_HAS_CONFLICT));
+    worker_task_runner_->PostTask(
+        FROM_HERE,
+        base::Bind(callback, SYNC_STATUS_HAS_CONFLICT));
     return;
   }
 
-  int64 sync_root_tracker_id = service_metadata_->sync_root_tracker_id();
+  int64 sync_root_tracker_id = index_->GetSyncRootTrackerID();
   if (!sync_root_tracker_id) {
     util::Log(logging::LOG_WARNING, FROM_HERE,
               "Sync-root needs to be set up before registering app-root");
-    RunSoon(FROM_HERE, base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND));
+    worker_task_runner_->PostTask(
+        FROM_HERE,
+        base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND));
     return;
   }
 
-  // Make this tracker an app-root tracker.
-  FileTracker* app_root_tracker = NULL;
-  for (TrackerSet::iterator itr = trackers.begin();
-       itr != trackers.end(); ++itr) {
-    FileTracker* tracker = *itr;
-    if (tracker->parent_tracker_id() == sync_root_tracker_id)
-      app_root_tracker = tracker;
-  }
-
-  if (!app_root_tracker) {
-    RunSoon(FROM_HERE, base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND));
+  scoped_ptr<FileTracker> tracker(new FileTracker);
+  if (!FilterFileTrackersByParent(index_.get(), trackers,
+                                  sync_root_tracker_id, tracker.get())) {
+    worker_task_runner_->PostTask(
+        FROM_HERE,
+        base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND));
     return;
   }
 
-  scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
-  RegisterTrackerAsAppRoot(app_id, app_root_tracker->tracker_id(), batch.get());
-  WriteToDatabase(batch.Pass(), callback);
+  tracker->set_app_id(app_id);
+  tracker->set_tracker_kind(TRACKER_KIND_APP_ROOT);
+  tracker->set_active(true);
+  tracker->set_needs_folder_listing(true);
+  tracker->set_dirty(true);
+
+  index_->StoreFileTracker(tracker.Pass());
+  WriteToDatabase(callback);
 }
 
 void MetadataDatabase::DisableApp(const std::string& app_id,
                                   const SyncStatusCallback& callback) {
-  FileTracker tracker;
-  if (!FindAppRootTracker(app_id, &tracker)) {
-    RunSoon(FROM_HERE, base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND));
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+
+  int64 tracker_id = index_->GetAppRootTracker(app_id);
+  scoped_ptr<FileTracker> tracker(new FileTracker);
+  if (!index_->GetFileTracker(tracker_id, tracker.get())) {
+    callback.Run(SYNC_DATABASE_ERROR_NOT_FOUND);
     return;
   }
 
-  if (tracker.tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT) {
-    RunSoon(FROM_HERE, base::Bind(callback, SYNC_STATUS_OK));
+  if (tracker->tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT) {
+    callback.Run(SYNC_STATUS_OK);
     return;
   }
 
-  scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
-  MakeAppRootDisabled(tracker.tracker_id(), batch.get());
-  WriteToDatabase(batch.Pass(), callback);
+  DCHECK_EQ(TRACKER_KIND_APP_ROOT, tracker->tracker_kind());
+  DCHECK(tracker->active());
+
+  // Keep the app-root tracker active (but change the tracker_kind) so that
+  // other conflicting trackers won't become active.
+  tracker->set_tracker_kind(TRACKER_KIND_DISABLED_APP_ROOT);
+
+  index_->StoreFileTracker(tracker.Pass());
+  WriteToDatabase(callback);
 }
 
 void MetadataDatabase::EnableApp(const std::string& app_id,
                                  const SyncStatusCallback& callback) {
-  FileTracker tracker;
-  if (!FindAppRootTracker(app_id, &tracker) ||
-      tracker.tracker_kind() == TRACKER_KIND_REGULAR) {
-    RunSoon(FROM_HERE, base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND));
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+
+  int64 tracker_id = index_->GetAppRootTracker(app_id);
+  scoped_ptr<FileTracker> tracker(new FileTracker);
+  if (!index_->GetFileTracker(tracker_id, tracker.get())) {
+    callback.Run(SYNC_DATABASE_ERROR_NOT_FOUND);
     return;
   }
 
-  if (tracker.tracker_kind() == TRACKER_KIND_APP_ROOT) {
-    RunSoon(FROM_HERE, base::Bind(callback, SYNC_STATUS_OK));
+  if (tracker->tracker_kind() == TRACKER_KIND_APP_ROOT) {
+    callback.Run(SYNC_STATUS_OK);
     return;
   }
 
-  scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
-  MakeAppRootEnabled(tracker.tracker_id(), batch.get());
-  WriteToDatabase(batch.Pass(), callback);
+  DCHECK_EQ(TRACKER_KIND_DISABLED_APP_ROOT, tracker->tracker_kind());
+  DCHECK(tracker->active());
+
+  tracker->set_tracker_kind(TRACKER_KIND_APP_ROOT);
+  index_->StoreFileTracker(tracker.Pass());
+
+  MarkTrackersDirtyRecursively(tracker_id, index_.get());
+  WriteToDatabase(callback);
 }
 
 void MetadataDatabase::UnregisterApp(const std::string& app_id,
                                      const SyncStatusCallback& callback) {
-  FileTracker tracker;
-  if (!FindAppRootTracker(app_id, &tracker) ||
-      tracker.tracker_kind() == TRACKER_KIND_REGULAR) {
-    RunSoon(FROM_HERE, base::Bind(callback, SYNC_STATUS_OK));
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+
+  int64 tracker_id = index_->GetAppRootTracker(app_id);
+  scoped_ptr<FileTracker> tracker(new FileTracker);
+  if (!index_->GetFileTracker(tracker_id, tracker.get()) ||
+      tracker->tracker_kind() == TRACKER_KIND_REGULAR) {
+    worker_task_runner_->PostTask(
+        FROM_HERE,
+        base::Bind(callback, SYNC_STATUS_OK));
     return;
   }
 
-  scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
-  UnregisterTrackerAsAppRoot(app_id, batch.get());
-  WriteToDatabase(batch.Pass(), callback);
+  RemoveAllDescendantTrackers(tracker_id, index_.get());
+
+  tracker->clear_app_id();
+  tracker->set_tracker_kind(TRACKER_KIND_REGULAR);
+  tracker->set_active(false);
+  tracker->set_dirty(true);
+
+  index_->StoreFileTracker(tracker.Pass());
+  WriteToDatabase(callback);
 }
 
 bool MetadataDatabase::FindAppRootTracker(const std::string& app_id,
-                                          FileTracker* tracker) const {
-  return FindItem(app_root_by_app_id_, app_id, tracker);
+                                          FileTracker* tracker_out) const {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+
+  int64 app_root_tracker_id = index_->GetAppRootTracker(app_id);
+  if (!app_root_tracker_id)
+    return false;
+
+  if (tracker_out &&
+      !index_->GetFileTracker(app_root_tracker_id, tracker_out)) {
+    NOTREACHED();
+    return false;
+  }
+
+  return true;
 }
 
 bool MetadataDatabase::FindFileByFileID(const std::string& file_id,
-                                        FileMetadata* file) const {
-  return FindItem(file_by_id_, file_id, file);
+                                        FileMetadata* metadata_out) const {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+  return index_->GetFileMetadata(file_id, metadata_out);
 }
 
 bool MetadataDatabase::FindTrackersByFileID(const std::string& file_id,
-                                            TrackerSet* trackers) const {
-  TrackersByFileID::const_iterator found = trackers_by_file_id_.find(file_id);
-  if (found == trackers_by_file_id_.end())
+                                            TrackerIDSet* trackers_out) const {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+
+  TrackerIDSet trackers = index_->GetFileTrackerIDsByFileID(file_id);
+  if (trackers.empty())
     return false;
-  if (trackers)
-    *trackers = found->second;
+
+  if (trackers_out)
+    std::swap(trackers, *trackers_out);
   return true;
 }
 
 bool MetadataDatabase::FindTrackersByParentAndTitle(
     int64 parent_tracker_id,
     const std::string& title,
-    TrackerSet* trackers) const {
-  TrackersByParentAndTitle::const_iterator found_by_parent =
-      trackers_by_parent_and_title_.find(parent_tracker_id);
-  if (found_by_parent == trackers_by_parent_and_title_.end())
-    return false;
+    TrackerIDSet* trackers_out) const {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
 
-  TrackersByTitle::const_iterator found_by_title =
-      found_by_parent->second.find(title);
-  if (found_by_title == found_by_parent->second.end())
+  TrackerIDSet trackers =
+      index_->GetFileTrackerIDsByParentAndTitle(parent_tracker_id, title);
+  if (trackers.empty())
     return false;
 
-  if (trackers)
-    *trackers = found_by_title->second;
+  if (trackers_out)
+    std::swap(trackers, *trackers_out);
   return true;
 }
 
 bool MetadataDatabase::FindTrackerByTrackerID(int64 tracker_id,
-                                              FileTracker* tracker) const {
-  return FindItem(tracker_by_id_, tracker_id, tracker);
+                                              FileTracker* tracker_out) const {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+  return index_->GetFileTracker(tracker_id, tracker_out);
 }
 
 bool MetadataDatabase::BuildPathForTracker(int64 tracker_id,
                                            base::FilePath* path) const {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+
   FileTracker current;
   if (!FindTrackerByTrackerID(tracker_id, &current) || !current.active())
     return false;
@@ -734,395 +872,643 @@ bool MetadataDatabase::BuildPathForTracker(int64 tracker_id,
   return true;
 }
 
-void MetadataDatabase::UpdateByChangeList(
-    int64 largest_change_id,
-    ScopedVector<google_apis::ChangeResource> changes,
-    const SyncStatusCallback& callback) {
-  DCHECK_LE(service_metadata_->largest_change_id(), largest_change_id);
+base::FilePath MetadataDatabase::BuildDisplayPathForTracker(
+    const FileTracker& tracker) const {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
 
-  scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
+  base::FilePath path;
+  if (tracker.active()) {
+    BuildPathForTracker(tracker.tracker_id(), &path);
+    return path;
+  }
+  BuildPathForTracker(tracker.parent_tracker_id(), &path);
+  if (tracker.has_synced_details()) {
+    path = path.Append(
+        base::FilePath::FromUTF8Unsafe(tracker.synced_details().title()));
+  } else {
+    path = path.Append(FILE_PATH_LITERAL("<unknown>"));
+  }
+  return path;
+}
 
-  for (ScopedVector<google_apis::ChangeResource>::const_iterator itr =
-           changes.begin();
-       itr != changes.end();
-       ++itr) {
-    const google_apis::ChangeResource& change = **itr;
-    scoped_ptr<FileMetadata> file(
-        CreateFileMetadataFromChangeResource(change));
-    std::string file_id = file->file_id();
+bool MetadataDatabase::FindNearestActiveAncestor(
+    const std::string& app_id,
+    const base::FilePath& full_path,
+    FileTracker* tracker_out,
+    base::FilePath* path_out) const {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+  DCHECK(tracker_out);
+  DCHECK(path_out);
+
+  if (full_path.IsAbsolute() ||
+      !FindAppRootTracker(app_id, tracker_out) ||
+      tracker_out->tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT) {
+    return false;
+  }
+
+  std::vector<base::FilePath::StringType> components;
+  full_path.GetComponents(&components);
+  path_out->clear();
 
-    MarkTrackersDirtyByFileID(file_id, batch.get());
-    if (!file->details().deleted())
-      MaybeAddTrackersForNewFile(*file, batch.get());
+  for (size_t i = 0; i < components.size(); ++i) {
+    const std::string title = base::FilePath(components[i]).AsUTF8Unsafe();
+    TrackerIDSet trackers;
+    if (!FindTrackersByParentAndTitle(
+            tracker_out->tracker_id(), title, &trackers) ||
+        !trackers.has_active()) {
+      return true;
+    }
 
-    if (FindTrackersByFileID(file_id, NULL)) {
-      PutFileToBatch(*file, batch.get());
+    FileTracker tracker;
+    index_->GetFileTracker(trackers.active_tracker(), &tracker);
 
-      // Set |file| to |file_by_id_[file_id]| and delete old value.
-      FileMetadata* file_ptr = file.release();
-      std::swap(file_ptr, file_by_id_[file_id]);
-      delete file_ptr;
+    DCHECK(tracker.has_synced_details());
+    const FileDetails& details = tracker.synced_details();
+    if (details.file_kind() != FILE_KIND_FOLDER && i != components.size() - 1) {
+      // This non-last component indicates file. Give up search.
+      return true;
     }
+
+    tracker_out->CopyFrom(tracker);
+    *path_out = path_out->Append(components[i]);
   }
 
-  service_metadata_->set_largest_change_id(largest_change_id);
-  PutServiceMetadataToBatch(*service_metadata_, batch.get());
-  WriteToDatabase(batch.Pass(), callback);
+  return true;
 }
 
-void MetadataDatabase::UpdateByFileResource(
-    int64 change_id,
-    const google_apis::FileResource& resource,
+void MetadataDatabase::UpdateByChangeList(
+    int64 largest_change_id,
+    ScopedVector<google_apis::ChangeResource> changes,
     const SyncStatusCallback& callback) {
-  scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+  DCHECK_LE(index_->GetLargestChangeID(), largest_change_id);
 
-  scoped_ptr<FileMetadata> file(
-      CreateFileMetadataFromFileResource(change_id, resource));
-  std::string file_id = file->file_id();
+  for (size_t i = 0; i < changes.size(); ++i) {
+    const google_apis::ChangeResource& change = *changes[i];
+    if (HasNewerFileMetadata(change.file_id(), change.change_id()))
+      continue;
 
-  // TODO(tzik): Consolidate with UpdateByChangeList.
-  MarkTrackersDirtyByFileID(file_id, batch.get());
-  if (!file->details().deleted()) {
-    MaybeAddTrackersForNewFile(*file, batch.get());
+    scoped_ptr<FileMetadata> metadata(
+        CreateFileMetadataFromChangeResource(change));
+    UpdateByFileMetadata(FROM_HERE, metadata.Pass(),
+                         UPDATE_TRACKER_FOR_UNSYNCED_FILE);
+  }
 
-    if (FindTrackersByFileID(file_id, NULL)) {
-      PutFileToBatch(*file, batch.get());
+  UpdateLargestKnownChangeID(largest_change_id);
+  index_->SetLargestChangeID(largest_change_id);
+  WriteToDatabase(callback);
+}
 
-      // Set |file| to |file_by_id_[file_id]| and delete old value.
-      FileMetadata* file_ptr = file.release();
-      std::swap(file_ptr, file_by_id_[file_id]);
-      delete file_ptr;
-    }
+void MetadataDatabase::UpdateByFileResource(
+    const google_apis::FileResource& resource,
+    const SyncStatusCallback& callback) {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+
+  scoped_ptr<FileMetadata> metadata(
+      CreateFileMetadataFromFileResource(
+          GetLargestKnownChangeID(), resource));
+  UpdateByFileMetadata(FROM_HERE, metadata.Pass(),
+                       UPDATE_TRACKER_FOR_UNSYNCED_FILE);
+  WriteToDatabase(callback);
+}
+
+void MetadataDatabase::UpdateByFileResourceList(
+    ScopedVector<google_apis::FileResource> resources,
+    const SyncStatusCallback& callback) {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+
+  for (size_t i = 0; i < resources.size(); ++i) {
+    scoped_ptr<FileMetadata> metadata(
+        CreateFileMetadataFromFileResource(
+            GetLargestKnownChangeID(), *resources[i]));
+    UpdateByFileMetadata(FROM_HERE, metadata.Pass(),
+                         UPDATE_TRACKER_FOR_UNSYNCED_FILE);
   }
+  WriteToDatabase(callback);
+}
 
-  WriteToDatabase(batch.Pass(), callback);
+void MetadataDatabase::UpdateByDeletedRemoteFile(
+    const std::string& file_id,
+    const SyncStatusCallback& callback) {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+
+  scoped_ptr<FileMetadata> metadata(
+      CreateDeletedFileMetadata(GetLargestKnownChangeID(), file_id));
+  UpdateByFileMetadata(FROM_HERE, metadata.Pass(),
+                       UPDATE_TRACKER_FOR_UNSYNCED_FILE);
+  WriteToDatabase(callback);
+}
+
+void MetadataDatabase::UpdateByDeletedRemoteFileList(
+    const FileIDList& file_ids,
+    const SyncStatusCallback& callback) {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+
+  for (FileIDList::const_iterator itr = file_ids.begin();
+       itr != file_ids.end(); ++itr) {
+    scoped_ptr<FileMetadata> metadata(
+        CreateDeletedFileMetadata(GetLargestKnownChangeID(), *itr));
+    UpdateByFileMetadata(FROM_HERE, metadata.Pass(),
+                         UPDATE_TRACKER_FOR_UNSYNCED_FILE);
+  }
+  WriteToDatabase(callback);
+}
+
+void MetadataDatabase::ReplaceActiveTrackerWithNewResource(
+    int64 parent_tracker_id,
+    const google_apis::FileResource& resource,
+    const SyncStatusCallback& callback) {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+
+  DCHECK(!index_->GetFileMetadata(resource.file_id(), NULL));
+  DCHECK(index_->GetFileTracker(parent_tracker_id, NULL));
+
+  UpdateByFileMetadata(
+      FROM_HERE,
+      CreateFileMetadataFromFileResource(GetLargestKnownChangeID(), resource),
+      UPDATE_TRACKER_FOR_SYNCED_FILE);
+
+  DCHECK(index_->GetFileMetadata(resource.file_id(), NULL));
+  DCHECK(!index_->GetFileTrackerIDsByFileID(resource.file_id()).has_active());
+
+  TrackerIDSet same_path_trackers =
+      index_->GetFileTrackerIDsByParentAndTitle(
+          parent_tracker_id, resource.title());
+  FileTracker to_be_activated;
+  if (!FilterFileTrackersByFileID(index_.get(), same_path_trackers,
+                                  resource.file_id(), &to_be_activated)) {
+    NOTREACHED();
+    worker_task_runner_->PostTask(
+        FROM_HERE,
+        base::Bind(callback, SYNC_STATUS_FAILED));
+    return;
+  }
+
+  int64 tracker_id = to_be_activated.tracker_id();
+  if (same_path_trackers.has_active()) {
+    DeactivateFileTracker(same_path_trackers.active_tracker(),
+                          MARK_ITSELF_DIRTY |
+                          MARK_SAME_FILE_ID_TRACKERS_DIRTY,
+                          index_.get());
+  }
+
+  ActivateFileTracker(tracker_id, MARK_NOTHING_DIRTY, index_.get());
+  WriteToDatabase(callback);
 }
 
 void MetadataDatabase::PopulateFolderByChildList(
     const std::string& folder_id,
     const FileIDList& child_file_ids,
     const SyncStatusCallback& callback) {
-  TrackerSet trackers;
-  if (!FindTrackersByFileID(folder_id, &trackers) ||
-      !trackers.has_active()) {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+
+  TrackerIDSet trackers = index_->GetFileTrackerIDsByFileID(folder_id);
+  if (!trackers.has_active()) {
     // It's OK that there is no folder to populate its children.
     // Inactive folders should ignore their contents updates.
-    RunSoon(FROM_HERE, base::Bind(callback, SYNC_STATUS_OK));
+    worker_task_runner_->PostTask(
+        FROM_HERE,
+        base::Bind(callback, SYNC_STATUS_OK));
     return;
   }
 
-  FileTracker* folder_tracker =
-      tracker_by_id_[trackers.active_tracker()->tracker_id()];
-  DCHECK(folder_tracker);
-  std::set<std::string> children(child_file_ids.begin(), child_file_ids.end());
+  scoped_ptr<FileTracker> folder_tracker(new FileTracker);
+  if (!index_->GetFileTracker(trackers.active_tracker(),
+                              folder_tracker.get())) {
+    NOTREACHED();
+    worker_task_runner_->PostTask(
+        FROM_HERE,
+        base::Bind(callback, SYNC_STATUS_FAILED));
+    return;
+  }
+
+  base::hash_set<std::string> children(child_file_ids.begin(),
+                                       child_file_ids.end());
 
-  std::vector<int64> known_children;
-  PushChildTrackersToContainer(trackers_by_parent_and_title_,
-                               folder_tracker->tracker_id(),
-                               std::back_inserter(known_children));
-  for (std::vector<int64>::iterator itr = known_children.begin();
-       itr != known_children.end(); ++itr)
-    children.erase(tracker_by_id_[*itr]->file_id());
+  std::vector<int64> known_children =
+      index_->GetFileTrackerIDsByParent(folder_tracker->tracker_id());
+  for (size_t i = 0; i < known_children.size(); ++i) {
+    FileTracker tracker;
+    if (!index_->GetFileTracker(known_children[i], &tracker)) {
+      NOTREACHED();
+      continue;
+    }
+    children.erase(tracker.file_id());
+  }
 
-  scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
-  for (FileIDList::const_iterator itr = child_file_ids.begin();
-       itr != child_file_ids.end(); ++itr)
-    CreateTrackerForParentAndFileID(*folder_tracker, *itr, batch.get());
+  for (base::hash_set<std::string>::const_iterator itr = children.begin();
+       itr != children.end(); ++itr)
+    CreateTrackerForParentAndFileID(*folder_tracker, *itr);
   folder_tracker->set_needs_folder_listing(false);
-  if (folder_tracker->dirty() && !ShouldKeepDirty(*folder_tracker)) {
+  if (folder_tracker->dirty() && !ShouldKeepDirty(*folder_tracker))
     folder_tracker->set_dirty(false);
-    dirty_trackers_.erase(folder_tracker);
-  }
-  PutTrackerToBatch(*folder_tracker, batch.get());
+  index_->StoreFileTracker(folder_tracker.Pass());
 
-  WriteToDatabase(batch.Pass(), callback);
+  WriteToDatabase(callback);
 }
 
 void MetadataDatabase::UpdateTracker(int64 tracker_id,
                                      const FileDetails& updated_details,
                                      const SyncStatusCallback& callback) {
-  TrackerByID::iterator found = tracker_by_id_.find(tracker_id);
-  if (found == tracker_by_id_.end()) {
-    RunSoon(FROM_HERE, base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND));
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+
+  FileTracker tracker;
+  if (!index_->GetFileTracker(tracker_id, &tracker)) {
+    worker_task_runner_->PostTask(
+        FROM_HERE,
+        base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND));
     return;
   }
 
-  FileTracker* tracker = found->second;
-  DCHECK(tracker);
+  // Check if the tracker is to be deleted.
+  if (updated_details.missing()) {
+    FileMetadata metadata;
+    if (!index_->GetFileMetadata(tracker.file_id(), &metadata) ||
+        metadata.details().missing()) {
+      // Both the tracker and metadata have the missing flag, now it's safe to
+      // delete the |tracker|.
+      RemoveFileTracker(tracker_id,
+                        MARK_SAME_FILE_ID_TRACKERS_DIRTY |
+                        MARK_SAME_PATH_TRACKERS_DIRTY,
+                        index_.get());
+      WriteToDatabase(callback);
+      return;
+    }
+  }
 
-  scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
+  // Sync-root deletion should be handled separately by SyncEngine.
+  DCHECK(tracker_id != GetSyncRootTrackerID() ||
+         (tracker.has_synced_details() &&
+          tracker.synced_details().title() == updated_details.title() &&
+          !updated_details.missing()));
+
+  if (tracker_id != GetSyncRootTrackerID()) {
+    // Check if the tracker's parent is still in |parent_tracker_ids|.
+    // If not, there should exist another tracker for the new parent, so delete
+    // old tracker.
+    FileTracker parent_tracker;
+    index_->GetFileTracker(tracker.parent_tracker_id(), &parent_tracker);
+
+    if (!HasFileAsParent(updated_details, parent_tracker.file_id())) {
+      RemoveFileTracker(tracker.tracker_id(),
+                        MARK_SAME_PATH_TRACKERS_DIRTY,
+                        index_.get());
+      WriteToDatabase(callback);
+      return;
+    }
 
-  if (updated_details.deleted()) {
-    // The update deletes the local file.
-    FileByID::iterator found = file_by_id_.find(tracker->file_id());
-    if (found == file_by_id_.end() || found->second->details().deleted()) {
-      // Both the tracker and metadata have the deleted flag, now it's safe to
-      // delete the |tracker|.
-      RemoveTracker(tracker->tracker_id(), batch.get());
+    if (tracker.has_synced_details()) {
+      // Check if the tracker was retitled.  If it was, there should exist
+      // another tracker for the new title, so delete the tracker being updated.
+      if (tracker.synced_details().title() != updated_details.title()) {
+        RemoveFileTracker(tracker.tracker_id(),
+                          MARK_SAME_FILE_ID_TRACKERS_DIRTY,
+                          index_.get());
+        WriteToDatabase(callback);
+        return;
+      }
     } else {
-      // The local file is deleted, but corresponding remote file isn't.
-      // Put the tracker back to the initial state.
-      tracker->clear_synced_details();
-      tracker->set_dirty(true);
-      tracker->set_active(false);
-      PutTrackerToBatch(*tracker, batch.get());
+      // Check if any other tracker exists has the same parent, title and
+      // file_id to the updated tracker.  If it exists, delete the tracker being
+      // updated.
+      if (FilterFileTrackersByFileID(
+              index_.get(),
+              index_->GetFileTrackerIDsByParentAndTitle(
+                  parent_tracker.tracker_id(),
+                  updated_details.title()),
+              tracker.file_id(),
+              NULL)) {
+        RemoveFileTracker(tracker.tracker_id(),
+                          MARK_NOTHING_DIRTY,
+                          index_.get());
+        WriteToDatabase(callback);
+        return;
+      }
     }
-
-    WriteToDatabase(batch.Pass(), callback);
-    return;
   }
 
-  // Check if the tracker was retitled.  If it was, update the title and its
-  // index in advance.
-  if (!tracker->has_synced_details() ||
-      tracker->synced_details().title() != updated_details.title()) {
-    UpdateTrackerTitle(tracker, updated_details.title(), batch.get());
-  }
+  scoped_ptr<FileTracker> updated_tracker = CloneFileTracker(&tracker);
+  *updated_tracker->mutable_synced_details() = updated_details;
 
-  *tracker->mutable_synced_details() = updated_details;
+  bool should_promote = false;
 
   // Activate the tracker if:
   //   - There is no active tracker that tracks |tracker->file_id()|.
   //   - There is no active tracker that has the same |parent| and |title|.
-  if (!tracker->active() && CanActivateTracker(*tracker))
-    MakeTrackerActive(tracker->tracker_id(), batch.get());
-  if (tracker->dirty() && !ShouldKeepDirty(*tracker)) {
-    tracker->set_dirty(false);
-    dirty_trackers_.erase(tracker);
+  if (!tracker.active() && CanActivateTracker(tracker)) {
+    updated_tracker->set_active(true);
+    updated_tracker->set_dirty(true);
+    updated_tracker->set_needs_folder_listing(
+        tracker.synced_details().file_kind() == FILE_KIND_FOLDER);
+    should_promote = true;
+  } else if (tracker.dirty() && !ShouldKeepDirty(tracker)) {
+    updated_tracker->set_dirty(false);
   }
-  PutTrackerToBatch(*tracker, batch.get());
+  index_->StoreFileTracker(updated_tracker.Pass());
+  if (should_promote)
+    index_->PromoteDemotedDirtyTracker(tracker_id);
 
-  WriteToDatabase(batch.Pass(), callback);
+  WriteToDatabase(callback);
 }
 
-MetadataDatabase::MetadataDatabase(base::SequencedTaskRunner* task_runner)
-    : task_runner_(task_runner), weak_ptr_factory_(this) {
-  DCHECK(task_runner);
-}
+MetadataDatabase::ActivationStatus MetadataDatabase::TryActivateTracker(
+    int64 parent_tracker_id,
+    const std::string& file_id,
+    const SyncStatusCallback& callback) {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+
+  FileMetadata metadata;
+  if (!index_->GetFileMetadata(file_id, &metadata)) {
+    NOTREACHED();
+    worker_task_runner_->PostTask(
+        FROM_HERE,
+        base::Bind(callback, SYNC_STATUS_FAILED));
+    return ACTIVATION_PENDING;
+  }
+  std::string title = metadata.details().title();
+  DCHECK(!HasInvalidTitle(title));
+
+  TrackerIDSet same_file_id_trackers =
+      index_->GetFileTrackerIDsByFileID(file_id);
+  scoped_ptr<FileTracker> tracker_to_be_activated(new FileTracker);
+  FilterFileTrackersByParentAndTitle(
+      index_.get(), same_file_id_trackers, parent_tracker_id,
+      title, tracker_to_be_activated.get());
+
+  // Check if there is another active tracker that tracks |file_id|.
+  // This can happen when the tracked file has multiple parents.
+  // In this case, report the failure to the caller.
+  if (!tracker_to_be_activated->active() && same_file_id_trackers.has_active())
+    return ACTIVATION_FAILED_ANOTHER_ACTIVE_TRACKER;
+
+  if (!tracker_to_be_activated->active()) {
+    // Check if there exists another active tracker that has the same path to
+    // the tracker.  If there is, deactivate it, assuming the caller already
+    // overrides local file with newly added file,
+    TrackerIDSet same_title_trackers =
+        index_->GetFileTrackerIDsByParentAndTitle(parent_tracker_id, title);
+    if (same_title_trackers.has_active()) {
+      RemoveAllDescendantTrackers(same_title_trackers.active_tracker(),
+                                  index_.get());
+
+      scoped_ptr<FileTracker> tracker_to_be_deactivated(new FileTracker);
+      if (index_->GetFileTracker(same_title_trackers.active_tracker(),
+                                 tracker_to_be_deactivated.get())) {
+        const std::string file_id = tracker_to_be_deactivated->file_id();
+        tracker_to_be_deactivated->set_active(false);
+        index_->StoreFileTracker(tracker_to_be_deactivated.Pass());
+
+        MarkTrackersDirtyByFileID(file_id, index_.get());
+      } else {
+        NOTREACHED();
+      }
+    }
+  }
 
-// static
-void MetadataDatabase::CreateOnTaskRunner(
-    base::SingleThreadTaskRunner* callback_runner,
-    base::SequencedTaskRunner* task_runner,
-    const base::FilePath& database_path,
-    const CreateCallback& callback) {
-  scoped_ptr<MetadataDatabase> metadata_database(
-      new MetadataDatabase(task_runner));
-  SyncStatusCode status =
-      metadata_database->InitializeOnTaskRunner(database_path);
-  if (status != SYNC_STATUS_OK)
-    metadata_database.reset();
+  tracker_to_be_activated->set_dirty(false);
+  tracker_to_be_activated->set_active(true);
+  *tracker_to_be_activated->mutable_synced_details() = metadata.details();
+  if (tracker_to_be_activated->synced_details().file_kind() ==
+      FILE_KIND_FOLDER) {
+    tracker_to_be_activated->set_needs_folder_listing(true);
+  }
+  tracker_to_be_activated->set_dirty(false);
 
-  callback_runner->PostTask(FROM_HERE, base::Bind(
-      callback, status, base::Passed(&metadata_database)));
+  index_->StoreFileTracker(tracker_to_be_activated.Pass());
+
+  WriteToDatabase(callback);
+  return ACTIVATION_PENDING;
 }
 
-// static
-SyncStatusCode MetadataDatabase::CreateForTesting(
-    scoped_ptr<leveldb::DB> db,
-    scoped_ptr<MetadataDatabase>* metadata_database_out) {
-  scoped_ptr<MetadataDatabase> metadata_database(
-      new MetadataDatabase(base::MessageLoopProxy::current()));
-  metadata_database->db_ = db.Pass();
-  SyncStatusCode status =
-      metadata_database->InitializeOnTaskRunner(base::FilePath());
-  if (status == SYNC_STATUS_OK)
-    *metadata_database_out = metadata_database.Pass();
-  return status;
+void MetadataDatabase::LowerTrackerPriority(int64 tracker_id) {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+  index_->DemoteDirtyTracker(tracker_id);
+  WriteToDatabase(base::Bind(&EmptyStatusCallback));
 }
 
-SyncStatusCode MetadataDatabase::SetLargestChangeIDForTesting(
-    int64 largest_change_id) {
-  service_metadata_->set_largest_change_id(largest_change_id);
+bool MetadataDatabase::PromoteLowerPriorityTrackersToNormal() {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+  bool promoted = index_->PromoteDemotedDirtyTrackers();
+  WriteToDatabase(base::Bind(&EmptyStatusCallback));
+  return promoted;
+}
 
-  leveldb::WriteBatch batch;
-  PutServiceMetadataToBatch(*service_metadata_, &batch);
-  return LevelDBStatusToSyncStatusCode(
-      db_->Write(leveldb::WriteOptions(), &batch));
+void MetadataDatabase::PromoteDemotedTracker(int64 tracker_id) {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+  index_->PromoteDemotedDirtyTracker(tracker_id);
+  WriteToDatabase(base::Bind(&EmptyStatusCallback));
 }
 
-SyncStatusCode MetadataDatabase::InitializeOnTaskRunner(
-    const base::FilePath& database_path) {
-  base::ThreadRestrictions::AssertIOAllowed();
-  DCHECK(task_runner_->RunsTasksOnCurrentThread());
+bool MetadataDatabase::GetNormalPriorityDirtyTracker(
+    FileTracker* tracker_out) const {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
 
-  SyncStatusCode status = SYNC_STATUS_UNKNOWN;
-  bool created = false;
-  // Open database unless |db_| is overridden for testing.
-  if (!db_) {
-    status = OpenDatabase(database_path, &db_, &created);
-    if (status != SYNC_STATUS_OK)
-      return status;
-  }
+  int64 dirty_tracker_id = index_->PickDirtyTracker();
+  if (!dirty_tracker_id)
+    return false;
 
-  if (created) {
-    status = WriteVersionInfo(db_.get());
-    if (status != SYNC_STATUS_OK)
-      return status;
-  } else {
-    status = MigrateDatabaseIfNeeded(db_.get());
-    if (status != SYNC_STATUS_OK)
-      return status;
+  if (tracker_out) {
+    if (!index_->GetFileTracker(dirty_tracker_id, tracker_out)) {
+      NOTREACHED();
+      return false;
+    }
   }
+  return true;
+}
 
-  DatabaseContents contents;
-  status = ReadDatabaseContents(db_.get(), &contents);
-  if (status != SYNC_STATUS_OK)
-    return status;
-
-  leveldb::WriteBatch batch;
-  status = InitializeServiceMetadata(&contents, &batch);
-  if (status != SYNC_STATUS_OK)
-    return status;
-
-  status = RemoveUnreachableItems(&contents, &batch);
-  if (status != SYNC_STATUS_OK)
-    return status;
-
-  status = LevelDBStatusToSyncStatusCode(
-      db_->Write(leveldb::WriteOptions(), &batch));
-  if (status != SYNC_STATUS_OK)
-    return status;
+bool MetadataDatabase::HasLowPriorityDirtyTracker() const {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+  return index_->HasDemotedDirtyTracker();
+}
 
-  BuildIndexes(&contents);
-  return status;
+bool MetadataDatabase::HasDirtyTracker() const {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+  return index_->PickDirtyTracker() != kInvalidTrackerID;
 }
 
-void MetadataDatabase::BuildIndexes(DatabaseContents* contents) {
-  service_metadata_ = contents->service_metadata.Pass();
+size_t MetadataDatabase::CountDirtyTracker() const {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+  return index_->CountDirtyTracker();
+}
 
-  for (ScopedVector<FileMetadata>::const_iterator itr =
-           contents->file_metadata.begin();
-       itr != contents->file_metadata.end();
-       ++itr) {
-    file_by_id_[(*itr)->file_id()] = *itr;
-  }
-  contents->file_metadata.weak_clear();
+bool MetadataDatabase::GetMultiParentFileTrackers(std::string* file_id_out,
+                                                  TrackerIDSet* trackers_out) {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+  DCHECK(file_id_out);
+  DCHECK(trackers_out);
 
-  for (ScopedVector<FileTracker>::const_iterator itr =
-           contents->file_trackers.begin();
-       itr != contents->file_trackers.end();
-       ++itr) {
-    FileTracker* tracker = *itr;
-    tracker_by_id_[tracker->tracker_id()] = tracker;
-    trackers_by_file_id_[tracker->file_id()].Insert(tracker);
-
-    if (IsAppRoot(*tracker))
-      app_root_by_app_id_[tracker->app_id()] = tracker;
-
-    if (tracker->parent_tracker_id()) {
-      std::string title = GetTrackerTitle(*tracker);
-      TrackerSet* trackers =
-          &trackers_by_parent_and_title_[tracker->parent_tracker_id()][title];
-      trackers->Insert(tracker);
-    }
+  std::string file_id = index_->PickMultiTrackerFileID();
+  if (file_id.empty())
+    return false;
 
-    if (tracker->dirty())
-      dirty_trackers_.insert(tracker);
+  TrackerIDSet trackers = index_->GetFileTrackerIDsByFileID(file_id);
+  if (trackers.size() <= 1) {
+    NOTREACHED();
+    return false;
   }
-  contents->file_trackers.weak_clear();
+
+  *file_id_out = file_id;
+  std::swap(*trackers_out, trackers);
+  return true;
 }
 
-void MetadataDatabase::RegisterTrackerAsAppRoot(
-    const std::string& app_id,
-    int64 tracker_id,
-    leveldb::WriteBatch* batch) {
-  FileTracker* tracker = tracker_by_id_[tracker_id];
-  DCHECK(tracker);
-  tracker->set_app_id(app_id);
-  tracker->set_tracker_kind(TRACKER_KIND_APP_ROOT);
-  app_root_by_app_id_[app_id] = tracker;
+size_t MetadataDatabase::CountFileMetadata() const {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+  return index_->CountFileMetadata();
+}
 
-  MakeTrackerActive(tracker->tracker_id(), batch);
+size_t MetadataDatabase::CountFileTracker() const {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+  return index_->CountFileTracker();
 }
 
-void MetadataDatabase::UnregisterTrackerAsAppRoot(
-    const std::string& app_id,
-    leveldb::WriteBatch* batch) {
-  FileTracker* tracker = FindAndEraseItem(&app_root_by_app_id_, app_id);
-  tracker->set_app_id(std::string());
-  tracker->set_tracker_kind(TRACKER_KIND_REGULAR);
+bool MetadataDatabase::GetConflictingTrackers(TrackerIDSet* trackers_out) {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+  DCHECK(trackers_out);
+
+  ParentIDAndTitle parent_and_title = index_->PickMultiBackingFilePath();
+  if (parent_and_title.parent_id == kInvalidTrackerID)
+    return false;
+
+  TrackerIDSet trackers = index_->GetFileTrackerIDsByParentAndTitle(
+      parent_and_title.parent_id, parent_and_title.title);
+  if (trackers.size() <= 1) {
+    NOTREACHED();
+    return false;
+  }
+
+  std::swap(*trackers_out, trackers);
+  return true;
+}
 
-  // Inactivate the tracker to drop all descendant.
-  // (Note that we set tracker_kind to TRACKER_KIND_REGULAR before calling
-  // this.)
-  MakeTrackerInactive(tracker->tracker_id(), batch);
+void MetadataDatabase::GetRegisteredAppIDs(std::vector<std::string>* app_ids) {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+  DCHECK(app_ids);
+  *app_ids = index_->GetRegisteredAppIDs();
 }
 
-void MetadataDatabase::MakeTrackerActive(int64 tracker_id,
-                                         leveldb::WriteBatch* batch) {
-  FileTracker* tracker = tracker_by_id_[tracker_id];
-  DCHECK(tracker);
-  DCHECK(!tracker->active());
+void MetadataDatabase::SweepDirtyTrackers(
+    const std::vector<std::string>& file_ids,
+    const SyncStatusCallback& callback) {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+
+  std::set<int64> tracker_ids;
+  for (size_t i = 0; i < file_ids.size(); ++i) {
+    TrackerIDSet trackers_for_file_id =
+        index_->GetFileTrackerIDsByFileID(file_ids[i]);
+    for (TrackerIDSet::iterator itr = trackers_for_file_id.begin();
+         itr != trackers_for_file_id.end(); ++itr)
+      tracker_ids.insert(*itr);
+  }
 
-  int64 parent_tracker_id = tracker->parent_tracker_id();
-  DCHECK(tracker->has_synced_details());
-  trackers_by_file_id_[tracker->file_id()].Activate(tracker);
-  if (parent_tracker_id) {
-    trackers_by_parent_and_title_[parent_tracker_id][
-        tracker->synced_details().title()].Activate(tracker);
+  for (std::set<int64>::iterator itr = tracker_ids.begin();
+       itr != tracker_ids.end(); ++itr) {
+    scoped_ptr<FileTracker> tracker(new FileTracker);
+    if (!index_->GetFileTracker(*itr, tracker.get()) ||
+        !CanClearDirty(*tracker))
+      continue;
+    tracker->set_dirty(false);
+    index_->StoreFileTracker(tracker.Pass());
   }
-  tracker->set_active(true);
-  tracker->set_needs_folder_listing(
-      tracker->synced_details().file_kind() == FILE_KIND_FOLDER);
-  tracker->set_dirty(true);
-  dirty_trackers_.insert(tracker);
 
-  PutTrackerToBatch(*tracker, batch);
+  WriteToDatabase(callback);
 }
 
-void MetadataDatabase::MakeTrackerInactive(int64 tracker_id,
-                                           leveldb::WriteBatch* batch) {
-  FileTracker* tracker = tracker_by_id_[tracker_id];
-  DCHECK(tracker);
-  DCHECK(tracker->active());
-  DCHECK_EQ(TRACKER_KIND_REGULAR, tracker->tracker_kind());
-  trackers_by_file_id_[tracker->file_id()].Inactivate(tracker);
+MetadataDatabase::MetadataDatabase(
+    base::SequencedTaskRunner* worker_task_runner,
+    const base::FilePath& database_path,
+    leveldb::Env* env_override)
+    : worker_task_runner_(worker_task_runner),
+      database_path_(database_path),
+      env_override_(env_override),
+      largest_known_change_id_(0),
+      weak_ptr_factory_(this) {
+  DCHECK(worker_task_runner);
+}
 
-  std::string title = GetTrackerTitle(*tracker);
-  int64 parent_tracker_id = tracker->parent_tracker_id();
-  if (parent_tracker_id)
-    trackers_by_parent_and_title_[parent_tracker_id][title].Inactivate(tracker);
-  tracker->set_active(false);
+// static
+void MetadataDatabase::CreateOnWorkerTaskRunner(
+    scoped_ptr<CreateParam> create_param,
+    const CreateCallback& callback) {
+  DCHECK(create_param->worker_task_runner->RunsTasksOnCurrentThread());
+
+  scoped_ptr<MetadataDatabase> metadata_database(
+      new MetadataDatabase(create_param->worker_task_runner.get(),
+                           create_param->database_path,
+                           create_param->env_override));
+  SyncStatusCode status = metadata_database->Initialize();
+  if (status != SYNC_STATUS_OK)
+    metadata_database.reset();
 
-  RemoveAllDescendantTrackers(tracker_id, batch);
-  MarkTrackersDirtyByFileID(tracker->file_id(), batch);
-  if (parent_tracker_id)
-    MarkTrackersDirtyByPath(parent_tracker_id, title, batch);
-  PutTrackerToBatch(*tracker, batch);
+  metadata_database->DetachFromSequence();
+  create_param->worker_task_runner->PostTask(
+      FROM_HERE,
+      base::Bind(
+          callback, status, base::Passed(&metadata_database)));
 }
 
-void MetadataDatabase::MakeAppRootDisabled(int64 tracker_id,
-                                           leveldb::WriteBatch* batch) {
-  FileTracker* tracker = tracker_by_id_[tracker_id];
-  DCHECK(tracker);
-  DCHECK_EQ(TRACKER_KIND_APP_ROOT, tracker->tracker_kind());
-  DCHECK(tracker->active());
+SyncStatusCode MetadataDatabase::Initialize() {
+  base::ThreadRestrictions::AssertIOAllowed();
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
 
-  // Keep the app-root tracker active (but change the tracker_kind) so that
-  // other conflicting trackers won't become active.
-  tracker->set_tracker_kind(TRACKER_KIND_DISABLED_APP_ROOT);
-  PutTrackerToBatch(*tracker, batch);
-}
+  SyncStatusCode status = SYNC_STATUS_UNKNOWN;
+  bool created = false;
+  // Open database unless |db_| is overridden for testing.
+  if (!db_) {
+    status = OpenDatabase(database_path_, env_override_, &db_, &created);
+    if (status != SYNC_STATUS_OK)
+      return status;
+  }
 
-void MetadataDatabase::MakeAppRootEnabled(int64 tracker_id,
-                                          leveldb::WriteBatch* batch) {
-  FileTracker* tracker = tracker_by_id_[tracker_id];
-  DCHECK(tracker);
-  DCHECK_EQ(TRACKER_KIND_DISABLED_APP_ROOT, tracker->tracker_kind());
-  DCHECK(tracker->active());
+  if (!created) {
+    status = MigrateDatabaseIfNeeded(db_.get());
+    if (status != SYNC_STATUS_OK)
+      return status;
+  }
 
-  tracker->set_tracker_kind(TRACKER_KIND_APP_ROOT);
-  // Mark descendant trackers as dirty to handle changes in disable period.
-  RecursiveMarkTrackerAsDirty(tracker_id, batch);
-  PutTrackerToBatch(*tracker, batch);
+  if (CommandLine::ForCurrentProcess()->HasSwitch(
+          kEnableMetadataDatabaseOnDisk)) {
+    index_ = MetadataDatabaseIndexOnDisk::Create(db_.get());
+  } else {
+    index_ = MetadataDatabaseIndex::Create(db_.get());
+  }
+
+  status = LevelDBStatusToSyncStatusCode(db_->Commit());
+  if (status != SYNC_STATUS_OK)
+    return status;
+
+  UpdateLargestKnownChangeID(index_->GetLargestChangeID());
+
+  return status;
 }
 
 void MetadataDatabase::CreateTrackerForParentAndFileID(
     const FileTracker& parent_tracker,
-    const std::string& file_id,
-    leveldb::WriteBatch* batch) {
-  int64 tracker_id = GetNextTrackerID(batch);
+    const std::string& file_id) {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+  CreateTrackerInternal(parent_tracker, file_id, NULL,
+                        UPDATE_TRACKER_FOR_UNSYNCED_FILE);
+}
+
+void MetadataDatabase::CreateTrackerForParentAndFileMetadata(
+    const FileTracker& parent_tracker,
+    const FileMetadata& file_metadata,
+    UpdateOption option) {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+  DCHECK(file_metadata.has_details());
+  CreateTrackerInternal(parent_tracker,
+                        file_metadata.file_id(),
+                        &file_metadata.details(),
+                        option);
+}
+
+void MetadataDatabase::CreateTrackerInternal(const FileTracker& parent_tracker,
+                                             const std::string& file_id,
+                                             const FileDetails* details,
+                                             UpdateOption option) {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+
+  int64 tracker_id = IncrementTrackerID();
   scoped_ptr<FileTracker> tracker(new FileTracker);
   tracker->set_tracker_id(tracker_id);
   tracker->set_parent_tracker_id(parent_tracker.tracker_id());
@@ -1132,224 +1518,92 @@ void MetadataDatabase::CreateTrackerForParentAndFileID(
   tracker->set_dirty(true);
   tracker->set_active(false);
   tracker->set_needs_folder_listing(false);
-  PutTrackerToBatch(*tracker, batch);
-
-  trackers_by_file_id_[file_id].Insert(tracker.get());
-  // Note: |trackers_by_parent_and_title_| does not map from
-  // FileMetadata::details but from FileTracker::synced_details, which is filled
-  // on tracker updated phase.  Use empty string as the title since
-  // FileTracker::synced_details is empty here.
-  trackers_by_parent_and_title_[parent_tracker.tracker_id()][std::string()]
-      .Insert(tracker.get());
-  dirty_trackers_.insert(tracker.get());
-  DCHECK(!ContainsKey(tracker_by_id_, tracker_id));
-  tracker_by_id_[tracker_id] = tracker.release();
-}
-
-void MetadataDatabase::RemoveTracker(int64 tracker_id,
-                                     leveldb::WriteBatch* batch) {
-  RemoveTrackerInternal(tracker_id, batch, false);
-}
-
-void MetadataDatabase::RemoveTrackerIgnoringSiblings(
-    int64 tracker_id,
-    leveldb::WriteBatch* batch) {
-  RemoveTrackerInternal(tracker_id, batch, true);
-}
-
-void MetadataDatabase::RemoveTrackerInternal(
-    int64 tracker_id,
-    leveldb::WriteBatch* batch,
-    bool ignoring_siblings) {
-  scoped_ptr<FileTracker> tracker(
-      FindAndEraseItem(&tracker_by_id_, tracker_id));
-  if (!tracker)
-    return;
-
-  EraseTrackerFromFileIDIndex(tracker.get(), batch);
-  if (IsAppRoot(*tracker))
-    app_root_by_app_id_.erase(tracker->app_id());
-  EraseTrackerFromPathIndex(tracker.get());
-
-  MarkTrackersDirtyByFileID(tracker->file_id(), batch);
-  if (!ignoring_siblings) {
-    MarkTrackersDirtyByPath(tracker->parent_tracker_id(),
-                            GetTrackerTitle(*tracker),
-                            batch);
+  if (details) {
+    *tracker->mutable_synced_details() = *details;
+    if (option == UPDATE_TRACKER_FOR_UNSYNCED_FILE) {
+      tracker->mutable_synced_details()->set_missing(true);
+      tracker->mutable_synced_details()->clear_md5();
+    }
   }
-  PutTrackerDeletionToBatch(tracker_id, batch);
+  index_->StoreFileTracker(tracker.Pass());
 }
 
 void MetadataDatabase::MaybeAddTrackersForNewFile(
-    const FileMetadata& file,
-    leveldb::WriteBatch* batch) {
-  std::set<int64> known_parents;
-  TrackersByFileID::iterator found = trackers_by_file_id_.find(file.file_id());
-  if (found != trackers_by_file_id_.end()) {
-    for (TrackerSet::const_iterator itr = found->second.begin();
-         itr != found->second.end(); ++itr) {
-      int64 parent_tracker_id = (*itr)->parent_tracker_id();
-      if (parent_tracker_id)
-        known_parents.insert(parent_tracker_id);
+    const FileMetadata& metadata,
+    UpdateOption option) {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+
+  std::set<int64> parents_to_exclude;
+  TrackerIDSet existing_trackers =
+      index_->GetFileTrackerIDsByFileID(metadata.file_id());
+  for (TrackerIDSet::const_iterator itr = existing_trackers.begin();
+       itr != existing_trackers.end(); ++itr) {
+    FileTracker tracker;
+    if (!index_->GetFileTracker(*itr, &tracker)) {
+      NOTREACHED();
+      continue;
     }
-  }
 
-  for (int i = 0; i < file.details().parent_folder_ids_size(); ++i) {
-    std::string parent_folder_id = file.details().parent_folder_ids(i);
-    TrackersByFileID::iterator found =
-        trackers_by_file_id_.find(parent_folder_id);
-    if (found == trackers_by_file_id_.end())
+    int64 parent_tracker_id = tracker.parent_tracker_id();
+    if (!parent_tracker_id)
       continue;
 
-    for (TrackerSet::const_iterator itr = found->second.begin();
-         itr != found->second.end(); ++itr) {
-      FileTracker* parent_tracker = *itr;
-      int64 parent_tracker_id = parent_tracker->tracker_id();
-      if (!parent_tracker->active())
-        continue;
-
-      if (ContainsKey(known_parents, parent_tracker_id))
-        continue;
-
-      CreateTrackerForParentAndFileID(*parent_tracker, file.file_id(), batch);
+    // Exclude |parent_tracker_id| if it already has a tracker that has
+    // unknown title or has the same title with |file|.
+    if (!tracker.has_synced_details() ||
+        tracker.synced_details().title() == metadata.details().title()) {
+      parents_to_exclude.insert(parent_tracker_id);
     }
   }
-}
-
-void MetadataDatabase::RemoveAllDescendantTrackers(int64 root_tracker_id,
-                                                   leveldb::WriteBatch* batch) {
-  std::vector<int64> pending_trackers;
-  PushChildTrackersToContainer(trackers_by_parent_and_title_,
-                               root_tracker_id,
-                               std::back_inserter(pending_trackers));
-
-  while (!pending_trackers.empty()) {
-    int64 tracker_id = pending_trackers.back();
-    pending_trackers.pop_back();
-    PushChildTrackersToContainer(trackers_by_parent_and_title_,
-                                 tracker_id,
-                                 std::back_inserter(pending_trackers));
-    RemoveTrackerIgnoringSiblings(tracker_id, batch);
-  }
-}
-
-void MetadataDatabase::EraseTrackerFromFileIDIndex(FileTracker* tracker,
-                                                   leveldb::WriteBatch* batch) {
-  TrackersByFileID::iterator found =
-      trackers_by_file_id_.find(tracker->file_id());
-  if (found == trackers_by_file_id_.end())
-    return;
-
-  TrackerSet* trackers = &found->second;
-  trackers->Erase(tracker);
-  if (!trackers->tracker_set().empty())
-    return;
-  trackers_by_file_id_.erase(found);
-  EraseFileFromDatabase(tracker->file_id(), batch);
-}
-
-void MetadataDatabase::EraseFileFromDatabase(const std::string& file_id,
-                                             leveldb::WriteBatch* batch) {
-  scoped_ptr<FileMetadata> file(FindAndEraseItem(&file_by_id_, file_id));
-  if (file)
-    PutFileDeletionToBatch(file_id, batch);
-}
-
-void MetadataDatabase::EraseTrackerFromPathIndex(FileTracker* tracker) {
-  TrackersByParentAndTitle::iterator found =
-      trackers_by_parent_and_title_.find(tracker->parent_tracker_id());
-  if (found == trackers_by_parent_and_title_.end())
-    return;
 
-  std::string title = GetTrackerTitle(*tracker);
-  TrackersByTitle* trackers_by_title = &found->second;
-  TrackersByTitle::iterator found_by_title = trackers_by_title->find(title);
-  TrackerSet* conflicting_trackers = &found_by_title->second;
-  conflicting_trackers->Erase(tracker);
+  for (int i = 0; i < metadata.details().parent_folder_ids_size(); ++i) {
+    std::string parent_folder_id = metadata.details().parent_folder_ids(i);
+    TrackerIDSet parent_trackers =
+        index_->GetFileTrackerIDsByFileID(parent_folder_id);
+    for (TrackerIDSet::const_iterator itr = parent_trackers.begin();
+         itr != parent_trackers.end(); ++itr) {
+      FileTracker parent_tracker;
+      index_->GetFileTracker(*itr, &parent_tracker);
+      if (!parent_tracker.active())
+        continue;
 
-  if (conflicting_trackers->tracker_set().empty()) {
-    trackers_by_title->erase(found_by_title);
-    if (trackers_by_title->empty())
-      trackers_by_parent_and_title_.erase(found);
-  }
-}
+      if (ContainsKey(parents_to_exclude, parent_tracker.tracker_id()))
+        continue;
 
-void MetadataDatabase::MarkTrackerSetDirty(
-    TrackerSet* trackers,
-    leveldb::WriteBatch* batch) {
-  for (TrackerSet::iterator itr = trackers->begin();
-       itr != trackers->end(); ++itr) {
-    FileTracker* tracker = *itr;
-    if (tracker->dirty())
-      continue;
-    tracker->set_dirty(true);
-    PutTrackerToBatch(*tracker, batch);
-    dirty_trackers_.insert(tracker);
+      CreateTrackerForParentAndFileMetadata(
+          parent_tracker, metadata, option);
+    }
   }
 }
 
-void MetadataDatabase::MarkTrackersDirtyByFileID(
-    const std::string& file_id,
-    leveldb::WriteBatch* batch) {
-  TrackersByFileID::iterator found = trackers_by_file_id_.find(file_id);
-  if (found != trackers_by_file_id_.end())
-    MarkTrackerSetDirty(&found->second, batch);
-}
-
-void MetadataDatabase::MarkTrackersDirtyByPath(int64 parent_tracker_id,
-                                               const std::string& title,
-                                               leveldb::WriteBatch* batch) {
-  TrackersByParentAndTitle::iterator found =
-      trackers_by_parent_and_title_.find(parent_tracker_id);
-  if (found == trackers_by_parent_and_title_.end()) {
-    NOTREACHED() << "parent: " << parent_tracker_id
-                 << ", title: " << title;
-    return;
-  }
-
-  TrackersByTitle::iterator itr = found->second.find(title);
-  if (itr != found->second.end())
-    MarkTrackerSetDirty(&itr->second, batch);
-}
+int64 MetadataDatabase::IncrementTrackerID() {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
 
-int64 MetadataDatabase::GetNextTrackerID(leveldb::WriteBatch* batch) {
-  int64 tracker_id = service_metadata_->next_tracker_id();
-  service_metadata_->set_next_tracker_id(tracker_id + 1);
-  PutServiceMetadataToBatch(*service_metadata_, batch);
+  int64 tracker_id = index_->GetNextTrackerID();
+  index_->SetNextTrackerID(tracker_id + 1);
   DCHECK_GT(tracker_id, 0);
   return tracker_id;
 }
 
-void MetadataDatabase::RecursiveMarkTrackerAsDirty(int64 root_tracker_id,
-                                                   leveldb::WriteBatch* batch) {
-  std::vector<int64> stack;
-  stack.push_back(root_tracker_id);
-  while (!stack.empty()) {
-    int64 tracker_id = stack.back();
-    stack.pop_back();
-    PushChildTrackersToContainer(
-        trackers_by_parent_and_title_, tracker_id, std::back_inserter(stack));
-
-    FileTracker* tracker = tracker_by_id_[tracker_id];
-    if (!tracker->dirty()) {
-      tracker->set_dirty(true);
-      PutTrackerToBatch(*tracker, batch);
-      dirty_trackers_.insert(tracker);
-    }
-  }
-}
-
 bool MetadataDatabase::CanActivateTracker(const FileTracker& tracker) {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
   DCHECK(!tracker.active());
-  DCHECK_NE(service_metadata_->sync_root_tracker_id(), tracker.tracker_id());
+  DCHECK_NE(index_->GetSyncRootTrackerID(), tracker.tracker_id());
 
   if (HasActiveTrackerForFileID(tracker.file_id()))
     return false;
 
-  if (tracker.app_id().empty())
+  if (tracker.app_id().empty() &&
+      tracker.tracker_id() != GetSyncRootTrackerID()) {
     return false;
+  }
+
   if (!tracker.has_synced_details())
     return false;
+  if (tracker.synced_details().file_kind() == FILE_KIND_UNSUPPORTED)
+    return false;
+  if (HasInvalidTitle(tracker.synced_details().title()))
+    return false;
   DCHECK(tracker.parent_tracker_id());
 
   return !HasActiveTrackerForPath(tracker.parent_tracker_id(),
@@ -1357,6 +1611,8 @@ bool MetadataDatabase::CanActivateTracker(const FileTracker& tracker) {
 }
 
 bool MetadataDatabase::ShouldKeepDirty(const FileTracker& tracker) const {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+
   if (HasDisabledAppRoot(tracker))
     return false;
 
@@ -1364,110 +1620,354 @@ bool MetadataDatabase::ShouldKeepDirty(const FileTracker& tracker) const {
   if (!tracker.has_synced_details())
     return true;
 
-  FileByID::const_iterator found = file_by_id_.find(tracker.file_id());
-  if (found == file_by_id_.end())
+  FileMetadata metadata;
+  if (!index_->GetFileMetadata(tracker.file_id(), &metadata))
     return true;
-  const FileMetadata* file = found->second;
-  DCHECK(file);
+  DCHECK(metadata.has_details());
+
+  const FileDetails& local_details = tracker.synced_details();
+  const FileDetails& remote_details = metadata.details();
 
   if (tracker.active()) {
     if (tracker.needs_folder_listing())
       return true;
-    if (tracker.synced_details().md5() != file->details().md5())
+    if (local_details.md5() != remote_details.md5())
+      return true;
+    if (local_details.missing() != remote_details.missing())
       return true;
   }
 
-  const FileDetails& local_details = tracker.synced_details();
-  const FileDetails& remote_details = file->details();
-
   if (local_details.title() != remote_details.title())
     return true;
-  if (local_details.deleted() != remote_details.deleted())
-    return true;
 
   return false;
 }
 
 bool MetadataDatabase::HasDisabledAppRoot(const FileTracker& tracker) const {
-  TrackerByAppID::const_iterator found =
-      app_root_by_app_id_.find(tracker.app_id());
-  if (found == app_root_by_app_id_.end())
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+
+  int64 app_root_tracker_id = index_->GetAppRootTracker(tracker.app_id());
+  if (app_root_tracker_id == kInvalidTrackerID)
     return false;
 
-  const FileTracker* app_root_tracker = found->second;
-  DCHECK(app_root_tracker);
-  return app_root_tracker->tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT;
+  FileTracker app_root_tracker;
+  if (!index_->GetFileTracker(app_root_tracker_id, &app_root_tracker)) {
+    NOTREACHED();
+    return false;
+  }
+  return app_root_tracker.tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT;
 }
 
 bool MetadataDatabase::HasActiveTrackerForFileID(
     const std::string& file_id) const {
-  TrackersByFileID::const_iterator found = trackers_by_file_id_.find(file_id);
-  return found != trackers_by_file_id_.end() && found->second.has_active();
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+  return index_->GetFileTrackerIDsByFileID(file_id).has_active();
 }
 
 bool MetadataDatabase::HasActiveTrackerForPath(int64 parent_tracker_id,
                                                const std::string& title) const {
-  TrackersByParentAndTitle::const_iterator found_by_parent =
-      trackers_by_parent_and_title_.find(parent_tracker_id);
-  if (found_by_parent == trackers_by_parent_and_title_.end())
-    return false;
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+  return index_->GetFileTrackerIDsByParentAndTitle(parent_tracker_id, title)
+      .has_active();
+}
+
+void MetadataDatabase::RemoveUnneededTrackersForMissingFile(
+    const std::string& file_id) {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+
+  TrackerIDSet trackers = index_->GetFileTrackerIDsByFileID(file_id);
+  for (TrackerIDSet::const_iterator itr = trackers.begin();
+       itr != trackers.end(); ++itr) {
+    FileTracker tracker;
+    if (!index_->GetFileTracker(*itr, &tracker)) {
+      NOTREACHED();
+      continue;
+    }
 
-  const TrackersByTitle& trackers_by_title = found_by_parent->second;
-  TrackersByTitle::const_iterator found = trackers_by_title.find(title);
-  return found != trackers_by_title.end() && found->second.has_active();
+    if (!tracker.has_synced_details() || tracker.synced_details().missing()) {
+      RemoveFileTracker(*itr, MARK_NOTHING_DIRTY, index_.get());
+    }
+  }
 }
 
-void MetadataDatabase::UpdateTrackerTitle(FileTracker* tracker,
-                                          const std::string& new_title,
-                                          leveldb::WriteBatch* batch) {
-  int64 parent_id = tracker->parent_tracker_id();
-  std::string old_title = GetTrackerTitle(*tracker);
-  DCHECK_NE(old_title, new_title);
-  DCHECK(!new_title.empty());
+void MetadataDatabase::UpdateByFileMetadata(
+    const tracked_objects::Location& from_where,
+    scoped_ptr<FileMetadata> metadata,
+    UpdateOption option) {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+  DCHECK(metadata);
+  DCHECK(metadata->has_details());
 
-  TrackersByTitle* trackers_by_title =
-      &trackers_by_parent_and_title_[parent_id];
-  TrackerSet* old_siblings = &(*trackers_by_title)[old_title];
-  TrackerSet* new_siblings = &(*trackers_by_title)[new_title];
+  DVLOG(1) << from_where.function_name() << ": "
+           << metadata->file_id() << " ("
+           << metadata->details().title() << ")"
+           << (metadata->details().missing() ? " deleted" : "");
 
-  old_siblings->Erase(tracker);
-  if (old_siblings->empty())
-    trackers_by_title->erase(old_title);
+  std::string file_id = metadata->file_id();
+  if (metadata->details().missing())
+    RemoveUnneededTrackersForMissingFile(file_id);
   else
-    MarkTrackerSetDirty(old_siblings, batch);
+    MaybeAddTrackersForNewFile(*metadata, option);
+
+  TrackerIDSet trackers = index_->GetFileTrackerIDsByFileID(file_id);
+  if (!trackers.empty()) {
+    index_->StoreFileMetadata(metadata.Pass());
+
+    if (option != UPDATE_TRACKER_FOR_SYNCED_FILE)
+      MarkTrackerSetDirty(trackers, index_.get());
+  }
+}
+
+void MetadataDatabase::WriteToDatabase(const SyncStatusCallback& callback) {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+
+  leveldb::Status status = db_->Commit();
+  callback.Run(LevelDBStatusToSyncStatusCode(status));
+}
+
+scoped_ptr<base::ListValue> MetadataDatabase::DumpFiles(
+    const std::string& app_id) {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+
+  scoped_ptr<base::ListValue> files(new base::ListValue);
+
+  FileTracker app_root_tracker;
+  if (!FindAppRootTracker(app_id, &app_root_tracker))
+    return files.Pass();
+
+  std::vector<int64> stack;
+  AppendContents(
+      index_->GetFileTrackerIDsByParent(app_root_tracker.tracker_id()), &stack);
+  while (!stack.empty()) {
+    int64 tracker_id = stack.back();
+    stack.pop_back();
+    AppendContents(index_->GetFileTrackerIDsByParent(tracker_id), &stack);
+
+    FileTracker tracker;
+    if (!index_->GetFileTracker(tracker_id, &tracker)) {
+      NOTREACHED();
+      continue;
+    }
+    base::DictionaryValue* file = new base::DictionaryValue;
+
+    base::FilePath path = BuildDisplayPathForTracker(tracker);
+    file->SetString("path", path.AsUTF8Unsafe());
+    if (tracker.has_synced_details()) {
+      file->SetString("title", tracker.synced_details().title());
+      file->SetString("type",
+                      FileKindToString(tracker.synced_details().file_kind()));
+    }
+
+    base::DictionaryValue* details = new base::DictionaryValue;
+    details->SetString("file_id", tracker.file_id());
+    if (tracker.has_synced_details() &&
+        tracker.synced_details().file_kind() == FILE_KIND_FILE)
+      details->SetString("md5", tracker.synced_details().md5());
+    details->SetString("active", tracker.active() ? "true" : "false");
+    details->SetString("dirty", tracker.dirty() ? "true" : "false");
+
+    file->Set("details", details);
+
+    files->Append(file);
+  }
+
+  return files.Pass();
+}
+
+scoped_ptr<base::ListValue> MetadataDatabase::DumpDatabase() {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
 
-  if (tracker->active() && new_siblings->has_active()) {
-    // Inactivate existing active tracker.
-    FileTracker* obstacle = new_siblings->active_tracker();
-    new_siblings->Inactivate(obstacle);
-    DCHECK_EQ(TRACKER_KIND_REGULAR, obstacle->tracker_kind());
+  scoped_ptr<base::ListValue> list(new base::ListValue);
+  list->Append(DumpTrackers().release());
+  list->Append(DumpMetadata().release());
+  return list.Pass();
+}
 
-    TrackerSet* same_file_id_trackers_to_obstacle =
-        &trackers_by_file_id_[obstacle->file_id()];
-    same_file_id_trackers_to_obstacle->Inactivate(obstacle);
-    MarkTrackerSetDirty(same_file_id_trackers_to_obstacle, batch);
+bool MetadataDatabase::HasNewerFileMetadata(const std::string& file_id,
+                                            int64 change_id) {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
 
-    obstacle->set_active(false);
-    PutTrackerToBatch(*obstacle, batch);
+  FileMetadata metadata;
+  if (!index_->GetFileMetadata(file_id, &metadata))
+    return false;
+  DCHECK(metadata.has_details());
+  return metadata.details().change_id() >= change_id;
+}
+
+scoped_ptr<base::ListValue> MetadataDatabase::DumpTrackers() {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+
+  scoped_ptr<base::ListValue> trackers(new base::ListValue);
+
+  // Append the first element for metadata.
+  base::DictionaryValue* metadata = new base::DictionaryValue;
+  const char *trackerKeys[] = {
+    "tracker_id", "path", "file_id", "tracker_kind", "app_id",
+    "active", "dirty", "folder_listing",
+    "title", "kind", "md5", "etag", "missing", "change_id",
+  };
+  std::vector<std::string> key_strings(
+      trackerKeys, trackerKeys + ARRAYSIZE_UNSAFE(trackerKeys));
+  base::ListValue* keys = new base::ListValue;
+  keys->AppendStrings(key_strings);
+  metadata->SetString("title", "Trackers");
+  metadata->Set("keys", keys);
+  trackers->Append(metadata);
+
+  // Append tracker data.
+  std::vector<int64> tracker_ids(index_->GetAllTrackerIDs());
+  for (std::vector<int64>::const_iterator itr = tracker_ids.begin();
+       itr != tracker_ids.end(); ++itr) {
+    const int64 tracker_id = *itr;
+    FileTracker tracker;
+    if (!index_->GetFileTracker(tracker_id, &tracker)) {
+      NOTREACHED();
+      continue;
+    }
 
-    RemoveAllDescendantTrackers(obstacle->tracker_id(), batch);
+    base::DictionaryValue* dict = new base::DictionaryValue;
+    base::FilePath path = BuildDisplayPathForTracker(tracker);
+    dict->SetString("tracker_id", base::Int64ToString(tracker_id));
+    dict->SetString("path", path.AsUTF8Unsafe());
+    dict->SetString("file_id", tracker.file_id());
+    TrackerKind tracker_kind = tracker.tracker_kind();
+    dict->SetString(
+        "tracker_kind",
+        tracker_kind == TRACKER_KIND_APP_ROOT ? "AppRoot" :
+        tracker_kind == TRACKER_KIND_DISABLED_APP_ROOT ? "Disabled App" :
+        tracker.tracker_id() == GetSyncRootTrackerID() ? "SyncRoot" :
+        "Regular");
+    dict->SetString("app_id", tracker.app_id());
+    dict->SetString("active", tracker.active() ? "true" : "false");
+    dict->SetString("dirty", tracker.dirty() ? "true" : "false");
+    dict->SetString("folder_listing",
+                    tracker.needs_folder_listing() ? "needed" : "no");
+    if (tracker.has_synced_details()) {
+      const FileDetails& details = tracker.synced_details();
+      dict->SetString("title", details.title());
+      dict->SetString("kind", FileKindToString(details.file_kind()));
+      dict->SetString("md5", details.md5());
+      dict->SetString("etag", details.etag());
+      dict->SetString("missing", details.missing() ? "true" : "false");
+      dict->SetString("change_id", base::Int64ToString(details.change_id()));
+    }
+    trackers->Append(dict);
   }
+  return trackers.Pass();
+}
+
+scoped_ptr<base::ListValue> MetadataDatabase::DumpMetadata() {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+
+  scoped_ptr<base::ListValue> files(new base::ListValue);
+
+  // Append the first element for metadata.
+  base::DictionaryValue* metadata = new base::DictionaryValue;
+  const char *fileKeys[] = {
+    "file_id", "title", "type", "md5", "etag", "missing",
+    "change_id", "parents"
+  };
+  std::vector<std::string> key_strings(
+      fileKeys, fileKeys + ARRAYSIZE_UNSAFE(fileKeys));
+  base::ListValue* keys = new base::ListValue;
+  keys->AppendStrings(key_strings);
+  metadata->SetString("title", "Metadata");
+  metadata->Set("keys", keys);
+  files->Append(metadata);
+
+  // Append metadata data.
+  std::vector<std::string> metadata_ids(index_->GetAllMetadataIDs());
+  for (std::vector<std::string>::const_iterator itr = metadata_ids.begin();
+       itr != metadata_ids.end(); ++itr) {
+    const std::string& file_id = *itr;
+    FileMetadata file;
+    if (!index_->GetFileMetadata(file_id, &file)) {
+      NOTREACHED();
+      continue;
+    }
 
-  tracker->mutable_synced_details()->set_title(new_title);
-  new_siblings->Insert(tracker);
-  PutTrackerToBatch(*tracker, batch);
+    base::DictionaryValue* dict = new base::DictionaryValue;
+    dict->SetString("file_id", file_id);
+    if (file.has_details()) {
+      const FileDetails& details = file.details();
+      dict->SetString("title", details.title());
+      dict->SetString("type", FileKindToString(details.file_kind()));
+      dict->SetString("md5", details.md5());
+      dict->SetString("etag", details.etag());
+      dict->SetString("missing", details.missing() ? "true" : "false");
+      dict->SetString("change_id", base::Int64ToString(details.change_id()));
+
+      std::vector<std::string> parents;
+      for (int i = 0; i < details.parent_folder_ids_size(); ++i)
+        parents.push_back(details.parent_folder_ids(i));
+      dict->SetString("parents", JoinString(parents, ","));
+    }
+    files->Append(dict);
+  }
+  return files.Pass();
 }
 
-void MetadataDatabase::WriteToDatabase(scoped_ptr<leveldb::WriteBatch> batch,
-                                       const SyncStatusCallback& callback) {
-  base::PostTaskAndReplyWithResult(
-      task_runner_.get(),
-      FROM_HERE,
-      base::Bind(&leveldb::DB::Write,
-                 base::Unretained(db_.get()),
-                 leveldb::WriteOptions(),
-                 base::Owned(batch.release())),
-      base::Bind(&AdaptLevelDBStatusToSyncStatusCode, callback));
+void MetadataDatabase::AttachSyncRoot(
+    const google_apis::FileResource& sync_root_folder) {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+
+  scoped_ptr<FileMetadata> sync_root_metadata =
+      CreateFileMetadataFromFileResource(
+          GetLargestKnownChangeID(), sync_root_folder);
+  scoped_ptr<FileTracker> sync_root_tracker =
+      CreateSyncRootTracker(IncrementTrackerID(), *sync_root_metadata);
+
+  index_->SetSyncRootTrackerID(sync_root_tracker->tracker_id());
+  index_->StoreFileMetadata(sync_root_metadata.Pass());
+  index_->StoreFileTracker(sync_root_tracker.Pass());
+}
+
+void MetadataDatabase::AttachInitialAppRoot(
+    const google_apis::FileResource& app_root_folder) {
+  scoped_ptr<FileMetadata> app_root_metadata =
+      CreateFileMetadataFromFileResource(
+          GetLargestKnownChangeID(), app_root_folder);
+  scoped_ptr<FileTracker> app_root_tracker =
+      CreateInitialAppRootTracker(IncrementTrackerID(),
+                                  GetSyncRootTrackerID(),
+                                  *app_root_metadata);
+
+  index_->StoreFileMetadata(app_root_metadata.Pass());
+  index_->StoreFileTracker(app_root_tracker.Pass());
+}
+
+void MetadataDatabase::DetachFromSequence() {
+  worker_sequence_checker_.DetachFromSequence();
+}
+
+bool MetadataDatabase::CanClearDirty(const FileTracker& tracker) {
+  DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
+
+  FileMetadata metadata;
+  if (!index_->GetFileMetadata(tracker.file_id(), &metadata) ||
+      !tracker.active() || !tracker.dirty() ||
+      !tracker.has_synced_details() ||
+      tracker.needs_folder_listing())
+    return false;
+
+  const FileDetails& remote_details = metadata.details();
+  const FileDetails& synced_details = tracker.synced_details();
+  if (remote_details.title() != synced_details.title() ||
+      remote_details.md5() != synced_details.md5())
+    return false;
+
+  std::set<std::string> parents;
+  for (int i = 0; i < remote_details.parent_folder_ids_size(); ++i)
+    parents.insert(remote_details.parent_folder_ids(i));
+
+  for (int i = 0; i < synced_details.parent_folder_ids_size(); ++i)
+    if (parents.erase(synced_details.parent_folder_ids(i)) != 1)
+      return false;
+
+  if (!parents.empty())
+    return false;
+
+  return true;
 }
 
 }  // namespace drive_backend