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, ¤t) || !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(), ¤t) ||
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