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