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