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