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