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.
5 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.h"
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/command_line.h"
13 #include "base/file_util.h"
14 #include "base/files/file_path.h"
15 #include "base/location.h"
16 #include "base/memory/scoped_vector.h"
17 #include "base/single_thread_task_runner.h"
18 #include "base/stl_util.h"
19 #include "base/strings/string_number_conversions.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/stringprintf.h"
22 #include "base/task_runner_util.h"
23 #include "base/thread_task_runner_handle.h"
24 #include "base/threading/thread_restrictions.h"
25 #include "chrome/browser/drive/drive_api_util.h"
26 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h"
27 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_util.h"
28 #include "chrome/browser/sync_file_system/drive_backend/leveldb_wrapper.h"
29 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.pb.h"
30 #include "chrome/browser/sync_file_system/drive_backend/metadata_database_index.h"
31 #include "chrome/browser/sync_file_system/drive_backend/metadata_database_index_interface.h"
32 #include "chrome/browser/sync_file_system/drive_backend/metadata_database_index_on_disk.h"
33 #include "chrome/browser/sync_file_system/drive_backend/metadata_db_migration_util.h"
34 #include "chrome/browser/sync_file_system/logger.h"
35 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
36 #include "google_apis/drive/drive_api_parser.h"
37 #include "third_party/leveldatabase/src/include/leveldb/db.h"
38 #include "third_party/leveldatabase/src/include/leveldb/env.h"
39 #include "third_party/leveldatabase/src/include/leveldb/status.h"
40 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
41 #include "webkit/common/fileapi/file_system_util.h"
43 namespace sync_file_system {
44 namespace drive_backend {
48 // Command line flag to enable on-disk indexing.
49 const char kEnableMetadataDatabaseOnDisk[] = "enable-syncfs-on-disk-indexing";
51 void EmptyStatusCallback(SyncStatusCode status) {}
53 std::string FileKindToString(FileKind file_kind) {
55 case FILE_KIND_UNSUPPORTED:
59 case FILE_KIND_FOLDER:
67 base::FilePath ReverseConcatPathComponents(
68 const std::vector<base::FilePath>& components) {
69 if (components.empty())
70 return base::FilePath(FILE_PATH_LITERAL("/")).NormalizePathSeparators();
72 size_t total_size = 0;
73 typedef std::vector<base::FilePath> PathComponents;
74 for (PathComponents::const_iterator itr = components.begin();
75 itr != components.end(); ++itr)
76 total_size += itr->value().size() + 1;
78 base::FilePath::StringType result;
79 result.reserve(total_size);
80 for (PathComponents::const_reverse_iterator itr = components.rbegin();
81 itr != components.rend(); ++itr) {
82 result.append(1, base::FilePath::kSeparators[0]);
83 result.append(itr->value());
86 return base::FilePath(result).NormalizePathSeparators();
89 void PopulateFileDetailsByFileResource(
90 const google_apis::FileResource& file_resource,
91 FileDetails* details) {
92 details->clear_parent_folder_ids();
93 for (std::vector<google_apis::ParentReference>::const_iterator itr =
94 file_resource.parents().begin();
95 itr != file_resource.parents().end();
97 details->add_parent_folder_ids(itr->file_id());
99 details->set_title(file_resource.title());
101 if (file_resource.IsDirectory())
102 details->set_file_kind(FILE_KIND_FOLDER);
103 else if (file_resource.IsHostedDocument())
104 details->set_file_kind(FILE_KIND_UNSUPPORTED);
106 details->set_file_kind(FILE_KIND_FILE);
108 details->set_md5(file_resource.md5_checksum());
109 details->set_etag(file_resource.etag());
110 details->set_creation_time(file_resource.created_date().ToInternalValue());
111 details->set_modification_time(
112 file_resource.modified_date().ToInternalValue());
113 details->set_missing(file_resource.labels().is_trashed());
116 scoped_ptr<FileMetadata> CreateFileMetadataFromFileResource(
118 const google_apis::FileResource& resource) {
119 scoped_ptr<FileMetadata> file(new FileMetadata);
120 file->set_file_id(resource.file_id());
122 FileDetails* details = file->mutable_details();
123 details->set_change_id(change_id);
125 if (resource.labels().is_trashed()) {
126 details->set_missing(true);
130 PopulateFileDetailsByFileResource(resource, details);
134 scoped_ptr<FileMetadata> CreateFileMetadataFromChangeResource(
135 const google_apis::ChangeResource& change) {
136 scoped_ptr<FileMetadata> file(new FileMetadata);
137 file->set_file_id(change.file_id());
139 FileDetails* details = file->mutable_details();
140 details->set_change_id(change.change_id());
142 if (change.is_deleted()) {
143 details->set_missing(true);
147 PopulateFileDetailsByFileResource(*change.file(), details);
151 scoped_ptr<FileMetadata> CreateDeletedFileMetadata(
153 const std::string& file_id) {
154 scoped_ptr<FileMetadata> file(new FileMetadata);
155 file->set_file_id(file_id);
157 FileDetails* details = file->mutable_details();
158 details->set_change_id(change_id);
159 details->set_missing(true);
163 scoped_ptr<FileTracker> CreateSyncRootTracker(
165 const FileMetadata& sync_root_metadata) {
166 scoped_ptr<FileTracker> sync_root_tracker(new FileTracker);
167 sync_root_tracker->set_tracker_id(tracker_id);
168 sync_root_tracker->set_file_id(sync_root_metadata.file_id());
169 sync_root_tracker->set_parent_tracker_id(0);
170 sync_root_tracker->set_tracker_kind(TRACKER_KIND_REGULAR);
171 sync_root_tracker->set_dirty(false);
172 sync_root_tracker->set_active(true);
173 sync_root_tracker->set_needs_folder_listing(false);
174 *sync_root_tracker->mutable_synced_details() = sync_root_metadata.details();
175 return sync_root_tracker.Pass();
178 scoped_ptr<FileTracker> CreateInitialAppRootTracker(
180 int64 parent_tracker_id,
181 const FileMetadata& app_root_metadata) {
182 scoped_ptr<FileTracker> app_root_tracker(new FileTracker);
183 app_root_tracker->set_tracker_id(tracker_id);
184 app_root_tracker->set_parent_tracker_id(parent_tracker_id);
185 app_root_tracker->set_file_id(app_root_metadata.file_id());
186 app_root_tracker->set_tracker_kind(TRACKER_KIND_REGULAR);
187 app_root_tracker->set_dirty(false);
188 app_root_tracker->set_active(false);
189 app_root_tracker->set_needs_folder_listing(false);
190 *app_root_tracker->mutable_synced_details() = app_root_metadata.details();
191 return app_root_tracker.Pass();
194 scoped_ptr<FileTracker> CloneFileTracker(const FileTracker* obj) {
196 return scoped_ptr<FileTracker>();
197 return scoped_ptr<FileTracker>(new FileTracker(*obj));
200 // Returns true if |db| has no content.
201 bool IsDatabaseEmpty(LevelDBWrapper* db) {
203 scoped_ptr<LevelDBWrapper::Iterator> itr(db->NewIterator());
205 return !itr->Valid();
208 SyncStatusCode OpenDatabase(const base::FilePath& path,
209 leveldb::Env* env_override,
210 scoped_ptr<LevelDBWrapper>* db_out,
212 base::ThreadRestrictions::AssertIOAllowed();
215 DCHECK(path.IsAbsolute());
217 leveldb::Options options;
218 options.max_open_files = 0; // Use minimum.
219 options.create_if_missing = true;
221 options.env = env_override;
222 leveldb::DB* db = NULL;
223 leveldb::Status db_status =
224 leveldb::DB::Open(options, path.AsUTF8Unsafe(), &db);
225 SyncStatusCode status = LevelDBStatusToSyncStatusCode(db_status);
226 if (status != SYNC_STATUS_OK) {
231 db_out->reset(new LevelDBWrapper(make_scoped_ptr(db)));
232 *created = IsDatabaseEmpty(db_out->get());
236 SyncStatusCode MigrateDatabaseIfNeeded(LevelDBWrapper* db) {
237 // See metadata_database_index.cc for the database schema.
238 base::ThreadRestrictions::AssertIOAllowed();
241 leveldb::Status status = db->Get(kDatabaseVersionKey, &value);
244 if (!base::StringToInt64(value, &version))
245 return SYNC_DATABASE_ERROR_FAILED;
247 if (!status.IsNotFound())
248 return SYNC_DATABASE_ERROR_FAILED;
253 drive_backend::MigrateDatabaseFromV0ToV1(db->GetLevelDB());
256 drive_backend::MigrateDatabaseFromV1ToV2(db->GetLevelDB());
259 // TODO(tzik): Migrate database from version 2 to 3.
260 // * Add sync-root folder as active, dirty and needs_folder_listing
262 // * Add app-root folders for each origins. Each app-root folder for
263 // an enabled origin should be a active, dirty and
264 // needs_folder_listing folder. And Each app-root folder for a
265 // disabled origin should be an inactive, dirty and
266 // non-needs_folder_listing folder.
267 // * Add a file metadata for each file in previous version.
269 return SYNC_DATABASE_ERROR_FAILED;
272 DCHECK_EQ(3, kCurrentDatabaseVersion);
273 // If MetadataDatabaseOnDisk is enabled, migration will be done in
274 // MetadataDatabaseOnDisk::Create().
275 // TODO(peria): Move the migration code (from v3 to v4) here.
276 return SYNC_STATUS_OK;
278 if (!CommandLine::ForCurrentProcess()->HasSwitch(
279 kEnableMetadataDatabaseOnDisk)) {
280 MigrateDatabaseFromV4ToV3(db->GetLevelDB());
282 return SYNC_STATUS_OK;
284 return SYNC_DATABASE_ERROR_FAILED;
288 bool HasInvalidTitle(const std::string& title) {
289 return title.empty() ||
290 title.find('/') != std::string::npos ||
291 title.find('\\') != std::string::npos;
294 void MarkTrackerSetDirty(const TrackerIDSet& trackers,
295 MetadataDatabaseIndexInterface* index) {
296 for (TrackerIDSet::const_iterator itr = trackers.begin();
297 itr != trackers.end(); ++itr) {
298 scoped_ptr<FileTracker> tracker(new FileTracker);
299 index->GetFileTracker(*itr, tracker.get());
300 if (tracker->dirty())
302 tracker->set_dirty(true);
303 index->StoreFileTracker(tracker.Pass());
307 void MarkTrackersDirtyByPath(int64 parent_tracker_id,
308 const std::string& title,
309 MetadataDatabaseIndexInterface* index) {
310 if (parent_tracker_id == kInvalidTrackerID || title.empty())
313 index->GetFileTrackerIDsByParentAndTitle(parent_tracker_id, title),
317 void MarkTrackersDirtyByFileID(const std::string& file_id,
318 MetadataDatabaseIndexInterface* index) {
319 MarkTrackerSetDirty(index->GetFileTrackerIDsByFileID(file_id), index);
322 void MarkTrackersDirtyRecursively(int64 root_tracker_id,
323 MetadataDatabaseIndexInterface* index) {
324 std::vector<int64> stack;
325 stack.push_back(root_tracker_id);
326 while (!stack.empty()) {
327 int64 tracker_id = stack.back();
329 AppendContents(index->GetFileTrackerIDsByParent(tracker_id), &stack);
331 scoped_ptr<FileTracker> tracker(new FileTracker);
332 index->GetFileTracker(tracker_id, tracker.get());
333 tracker->set_dirty(true);
335 index->StoreFileTracker(tracker.Pass());
339 void RemoveAllDescendantTrackers(int64 root_tracker_id,
340 MetadataDatabaseIndexInterface* index) {
341 std::vector<int64> pending_trackers;
342 AppendContents(index->GetFileTrackerIDsByParent(root_tracker_id),
345 std::vector<int64> to_be_removed;
347 // List trackers to remove.
348 while (!pending_trackers.empty()) {
349 int64 tracker_id = pending_trackers.back();
350 pending_trackers.pop_back();
351 AppendContents(index->GetFileTrackerIDsByParent(tracker_id),
353 to_be_removed.push_back(tracker_id);
356 // Remove trackers in the reversed order.
357 base::hash_set<std::string> affected_file_ids;
358 for (std::vector<int64>::reverse_iterator itr = to_be_removed.rbegin();
359 itr != to_be_removed.rend(); ++itr) {
361 index->GetFileTracker(*itr, &tracker);
362 affected_file_ids.insert(tracker.file_id());
363 index->RemoveFileTracker(*itr);
366 for (base::hash_set<std::string>::iterator itr = affected_file_ids.begin();
367 itr != affected_file_ids.end(); ++itr) {
368 TrackerIDSet trackers = index->GetFileTrackerIDsByFileID(*itr);
369 if (trackers.empty()) {
370 // Remove metadata that no longer has any tracker.
371 index->RemoveFileMetadata(*itr);
373 MarkTrackerSetDirty(trackers, index);
378 bool FilterFileTrackersByParent(
379 const MetadataDatabaseIndexInterface* index,
380 const TrackerIDSet& trackers,
381 int64 parent_tracker_id,
382 FileTracker* tracker_out) {
384 for (TrackerIDSet::const_iterator itr = trackers.begin();
385 itr != trackers.end(); ++itr) {
386 if (!index->GetFileTracker(*itr, &tracker)) {
391 if (tracker.parent_tracker_id() == parent_tracker_id) {
393 tracker_out->CopyFrom(tracker);
400 bool FilterFileTrackersByParentAndTitle(
401 const MetadataDatabaseIndexInterface* index,
402 const TrackerIDSet& trackers,
403 int64 parent_tracker_id,
404 const std::string& title,
405 FileTracker* result) {
407 for (TrackerIDSet::const_iterator itr = trackers.begin();
408 itr != trackers.end(); ++itr) {
410 if (!index->GetFileTracker(*itr, &tracker)) {
415 if (tracker.parent_tracker_id() != parent_tracker_id)
418 if (tracker.has_synced_details() &&
419 tracker.synced_details().title() != title)
422 // Prioritize trackers that has |synced_details| when |trackers| has
423 // multiple candidates.
424 if (!found || tracker.has_synced_details()) {
427 result->CopyFrom(tracker);
428 if (!result || result->has_synced_details())
436 bool FilterFileTrackersByFileID(
437 const MetadataDatabaseIndexInterface* index,
438 const TrackerIDSet& trackers,
439 const std::string& file_id,
440 FileTracker* tracker_out) {
442 for (TrackerIDSet::const_iterator itr = trackers.begin();
443 itr != trackers.end(); ++itr) {
444 if (!index->GetFileTracker(*itr, &tracker)) {
449 if (tracker.file_id() == file_id) {
451 tracker_out->CopyFrom(tracker);
458 enum DirtyingOption {
459 MARK_NOTHING_DIRTY = 0,
460 MARK_ITSELF_DIRTY = 1 << 0,
461 MARK_SAME_FILE_ID_TRACKERS_DIRTY = 1 << 1,
462 MARK_SAME_PATH_TRACKERS_DIRTY = 1 << 2,
465 void ActivateFileTracker(int64 tracker_id,
466 int dirtying_options,
467 MetadataDatabaseIndexInterface* index) {
468 DCHECK(dirtying_options == MARK_NOTHING_DIRTY ||
469 dirtying_options == MARK_ITSELF_DIRTY);
471 scoped_ptr<FileTracker> tracker(new FileTracker);
472 index->GetFileTracker(tracker_id, tracker.get());
473 tracker->set_active(true);
474 if (dirtying_options & MARK_ITSELF_DIRTY) {
475 tracker->set_dirty(true);
476 tracker->set_needs_folder_listing(
477 tracker->has_synced_details() &&
478 tracker->synced_details().file_kind() == FILE_KIND_FOLDER);
480 tracker->set_dirty(false);
481 tracker->set_needs_folder_listing(false);
484 index->StoreFileTracker(tracker.Pass());
487 void DeactivateFileTracker(int64 tracker_id,
488 int dirtying_options,
489 MetadataDatabaseIndexInterface* index) {
490 RemoveAllDescendantTrackers(tracker_id, index);
492 scoped_ptr<FileTracker> tracker(new FileTracker);
493 index->GetFileTracker(tracker_id, tracker.get());
495 if (dirtying_options & MARK_SAME_FILE_ID_TRACKERS_DIRTY)
496 MarkTrackersDirtyByFileID(tracker->file_id(), index);
497 if (dirtying_options & MARK_SAME_PATH_TRACKERS_DIRTY) {
498 MarkTrackersDirtyByPath(tracker->parent_tracker_id(),
499 GetTrackerTitle(*tracker), index);
502 tracker->set_dirty(dirtying_options & MARK_ITSELF_DIRTY);
503 tracker->set_active(false);
504 index->StoreFileTracker(tracker.Pass());
507 void RemoveFileTracker(int64 tracker_id,
508 int dirtying_options,
509 MetadataDatabaseIndexInterface* index) {
510 DCHECK(!(dirtying_options & MARK_ITSELF_DIRTY));
513 if (!index->GetFileTracker(tracker_id, &tracker))
516 std::string file_id = tracker.file_id();
517 int64 parent_tracker_id = tracker.parent_tracker_id();
518 std::string title = GetTrackerTitle(tracker);
520 RemoveAllDescendantTrackers(tracker_id, index);
521 index->RemoveFileTracker(tracker_id);
523 if (dirtying_options & MARK_SAME_FILE_ID_TRACKERS_DIRTY)
524 MarkTrackersDirtyByFileID(file_id, index);
525 if (dirtying_options & MARK_SAME_PATH_TRACKERS_DIRTY)
526 MarkTrackersDirtyByPath(parent_tracker_id, title, index);
528 if (index->GetFileTrackerIDsByFileID(file_id).empty()) {
529 index->RemoveFileMetadata(file_id);
535 struct MetadataDatabase::CreateParam {
536 scoped_refptr<base::SequencedTaskRunner> worker_task_runner;
537 base::FilePath database_path;
538 leveldb::Env* env_override;
540 CreateParam(base::SequencedTaskRunner* worker_task_runner,
541 const base::FilePath& database_path,
542 leveldb::Env* env_override)
543 : worker_task_runner(worker_task_runner),
544 database_path(database_path),
545 env_override(env_override) {
550 void MetadataDatabase::Create(base::SequencedTaskRunner* worker_task_runner,
551 const base::FilePath& database_path,
552 leveldb::Env* env_override,
553 const CreateCallback& callback) {
554 worker_task_runner->PostTask(FROM_HERE, base::Bind(
555 &MetadataDatabase::CreateOnWorkerTaskRunner,
556 base::Passed(make_scoped_ptr(new CreateParam(
564 SyncStatusCode MetadataDatabase::CreateForTesting(
565 scoped_ptr<LevelDBWrapper> db,
566 scoped_ptr<MetadataDatabase>* metadata_database_out) {
567 scoped_ptr<MetadataDatabase> metadata_database(
568 new MetadataDatabase(base::ThreadTaskRunnerHandle::Get(),
569 base::FilePath(), NULL));
570 metadata_database->db_ = db.Pass();
571 SyncStatusCode status = metadata_database->Initialize();
572 if (status == SYNC_STATUS_OK)
573 *metadata_database_out = metadata_database.Pass();
577 MetadataDatabase::~MetadataDatabase() {
578 worker_task_runner_->DeleteSoon(FROM_HERE, db_.release());
582 void MetadataDatabase::ClearDatabase(
583 scoped_ptr<MetadataDatabase> metadata_database) {
584 DCHECK(metadata_database);
585 scoped_refptr<base::SequencedTaskRunner> worker_task_runner =
586 metadata_database->worker_task_runner_;
587 base::FilePath database_path = metadata_database->database_path_;
588 DCHECK(!database_path.empty());
589 metadata_database.reset();
591 worker_task_runner->PostTask(
593 base::Bind(base::IgnoreResult(base::DeleteFile),
594 database_path, true /* recursive */));
597 int64 MetadataDatabase::GetLargestFetchedChangeID() const {
598 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
599 return index_->GetLargestChangeID();
602 int64 MetadataDatabase::GetSyncRootTrackerID() const {
603 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
604 return index_->GetSyncRootTrackerID();
607 int64 MetadataDatabase::GetLargestKnownChangeID() const {
608 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
609 DCHECK_LE(GetLargestFetchedChangeID(), largest_known_change_id_);
610 return largest_known_change_id_;
613 void MetadataDatabase::UpdateLargestKnownChangeID(int64 change_id) {
614 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
615 if (largest_known_change_id_ < change_id)
616 largest_known_change_id_ = change_id;
619 bool MetadataDatabase::HasSyncRoot() const {
620 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
621 return index_->GetSyncRootTrackerID() != kInvalidTrackerID;
624 void MetadataDatabase::PopulateInitialData(
625 int64 largest_change_id,
626 const google_apis::FileResource& sync_root_folder,
627 const ScopedVector<google_apis::FileResource>& app_root_folders,
628 const SyncStatusCallback& callback) {
629 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
631 index_->SetLargestChangeID(largest_change_id);
632 UpdateLargestKnownChangeID(largest_change_id);
634 AttachSyncRoot(sync_root_folder);
635 for (size_t i = 0; i < app_root_folders.size(); ++i)
636 AttachInitialAppRoot(*app_root_folders[i]);
638 WriteToDatabase(callback);
641 bool MetadataDatabase::IsAppEnabled(const std::string& app_id) const {
642 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
644 int64 tracker_id = index_->GetAppRootTracker(app_id);
645 if (tracker_id == kInvalidTrackerID)
649 if (!index_->GetFileTracker(tracker_id, &tracker))
651 return tracker.tracker_kind() == TRACKER_KIND_APP_ROOT;
654 void MetadataDatabase::RegisterApp(const std::string& app_id,
655 const std::string& folder_id,
656 const SyncStatusCallback& callback) {
657 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
659 if (index_->GetAppRootTracker(app_id)) {
660 // The app-root is already registered.
661 worker_task_runner_->PostTask(
663 base::Bind(callback, SYNC_STATUS_OK));
667 TrackerIDSet trackers = index_->GetFileTrackerIDsByFileID(folder_id);
668 if (trackers.empty()) {
669 worker_task_runner_->PostTask(
671 base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND));
675 if (trackers.has_active()) {
676 // The folder is tracked by another tracker.
677 util::Log(logging::LOG_WARNING, FROM_HERE,
678 "Failed to register App for %s", app_id.c_str());
679 worker_task_runner_->PostTask(
681 base::Bind(callback, SYNC_STATUS_HAS_CONFLICT));
685 int64 sync_root_tracker_id = index_->GetSyncRootTrackerID();
686 if (!sync_root_tracker_id) {
687 util::Log(logging::LOG_WARNING, FROM_HERE,
688 "Sync-root needs to be set up before registering app-root");
689 worker_task_runner_->PostTask(
691 base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND));
695 scoped_ptr<FileTracker> tracker(new FileTracker);
696 if (!FilterFileTrackersByParent(index_.get(), trackers,
697 sync_root_tracker_id, tracker.get())) {
698 worker_task_runner_->PostTask(
700 base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND));
704 tracker->set_app_id(app_id);
705 tracker->set_tracker_kind(TRACKER_KIND_APP_ROOT);
706 tracker->set_active(true);
707 tracker->set_needs_folder_listing(true);
708 tracker->set_dirty(true);
710 index_->StoreFileTracker(tracker.Pass());
711 WriteToDatabase(callback);
714 void MetadataDatabase::DisableApp(const std::string& app_id,
715 const SyncStatusCallback& callback) {
716 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
718 int64 tracker_id = index_->GetAppRootTracker(app_id);
719 scoped_ptr<FileTracker> tracker(new FileTracker);
720 if (!index_->GetFileTracker(tracker_id, tracker.get())) {
721 callback.Run(SYNC_DATABASE_ERROR_NOT_FOUND);
725 if (tracker->tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT) {
726 callback.Run(SYNC_STATUS_OK);
730 DCHECK_EQ(TRACKER_KIND_APP_ROOT, tracker->tracker_kind());
731 DCHECK(tracker->active());
733 // Keep the app-root tracker active (but change the tracker_kind) so that
734 // other conflicting trackers won't become active.
735 tracker->set_tracker_kind(TRACKER_KIND_DISABLED_APP_ROOT);
737 index_->StoreFileTracker(tracker.Pass());
738 WriteToDatabase(callback);
741 void MetadataDatabase::EnableApp(const std::string& app_id,
742 const SyncStatusCallback& callback) {
743 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
745 int64 tracker_id = index_->GetAppRootTracker(app_id);
746 scoped_ptr<FileTracker> tracker(new FileTracker);
747 if (!index_->GetFileTracker(tracker_id, tracker.get())) {
748 callback.Run(SYNC_DATABASE_ERROR_NOT_FOUND);
752 if (tracker->tracker_kind() == TRACKER_KIND_APP_ROOT) {
753 callback.Run(SYNC_STATUS_OK);
757 DCHECK_EQ(TRACKER_KIND_DISABLED_APP_ROOT, tracker->tracker_kind());
758 DCHECK(tracker->active());
760 tracker->set_tracker_kind(TRACKER_KIND_APP_ROOT);
761 index_->StoreFileTracker(tracker.Pass());
763 MarkTrackersDirtyRecursively(tracker_id, index_.get());
764 WriteToDatabase(callback);
767 void MetadataDatabase::UnregisterApp(const std::string& app_id,
768 const SyncStatusCallback& callback) {
769 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
771 int64 tracker_id = index_->GetAppRootTracker(app_id);
772 scoped_ptr<FileTracker> tracker(new FileTracker);
773 if (!index_->GetFileTracker(tracker_id, tracker.get()) ||
774 tracker->tracker_kind() == TRACKER_KIND_REGULAR) {
775 worker_task_runner_->PostTask(
777 base::Bind(callback, SYNC_STATUS_OK));
781 RemoveAllDescendantTrackers(tracker_id, index_.get());
783 tracker->clear_app_id();
784 tracker->set_tracker_kind(TRACKER_KIND_REGULAR);
785 tracker->set_active(false);
786 tracker->set_dirty(true);
788 index_->StoreFileTracker(tracker.Pass());
789 WriteToDatabase(callback);
792 bool MetadataDatabase::FindAppRootTracker(const std::string& app_id,
793 FileTracker* tracker_out) const {
794 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
796 int64 app_root_tracker_id = index_->GetAppRootTracker(app_id);
797 if (!app_root_tracker_id)
801 !index_->GetFileTracker(app_root_tracker_id, tracker_out)) {
809 bool MetadataDatabase::FindFileByFileID(const std::string& file_id,
810 FileMetadata* metadata_out) const {
811 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
812 return index_->GetFileMetadata(file_id, metadata_out);
815 bool MetadataDatabase::FindTrackersByFileID(const std::string& file_id,
816 TrackerIDSet* trackers_out) const {
817 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
819 TrackerIDSet trackers = index_->GetFileTrackerIDsByFileID(file_id);
820 if (trackers.empty())
824 std::swap(trackers, *trackers_out);
828 bool MetadataDatabase::FindTrackersByParentAndTitle(
829 int64 parent_tracker_id,
830 const std::string& title,
831 TrackerIDSet* trackers_out) const {
832 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
834 TrackerIDSet trackers =
835 index_->GetFileTrackerIDsByParentAndTitle(parent_tracker_id, title);
836 if (trackers.empty())
840 std::swap(trackers, *trackers_out);
844 bool MetadataDatabase::FindTrackerByTrackerID(int64 tracker_id,
845 FileTracker* tracker_out) const {
846 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
847 return index_->GetFileTracker(tracker_id, tracker_out);
850 bool MetadataDatabase::BuildPathForTracker(int64 tracker_id,
851 base::FilePath* path) const {
852 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
855 if (!FindTrackerByTrackerID(tracker_id, ¤t) || !current.active())
858 std::vector<base::FilePath> components;
859 while (!IsAppRoot(current)) {
860 std::string title = GetTrackerTitle(current);
863 components.push_back(base::FilePath::FromUTF8Unsafe(title));
864 if (!FindTrackerByTrackerID(current.parent_tracker_id(), ¤t) ||
870 *path = ReverseConcatPathComponents(components);
875 base::FilePath MetadataDatabase::BuildDisplayPathForTracker(
876 const FileTracker& tracker) const {
877 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
880 if (tracker.active()) {
881 BuildPathForTracker(tracker.tracker_id(), &path);
884 BuildPathForTracker(tracker.parent_tracker_id(), &path);
885 if (tracker.has_synced_details()) {
887 base::FilePath::FromUTF8Unsafe(tracker.synced_details().title()));
889 path = path.Append(FILE_PATH_LITERAL("<unknown>"));
894 bool MetadataDatabase::FindNearestActiveAncestor(
895 const std::string& app_id,
896 const base::FilePath& full_path,
897 FileTracker* tracker_out,
898 base::FilePath* path_out) const {
899 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
903 if (full_path.IsAbsolute() ||
904 !FindAppRootTracker(app_id, tracker_out) ||
905 tracker_out->tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT) {
909 std::vector<base::FilePath::StringType> components;
910 full_path.GetComponents(&components);
913 for (size_t i = 0; i < components.size(); ++i) {
914 const std::string title = base::FilePath(components[i]).AsUTF8Unsafe();
915 TrackerIDSet trackers;
916 if (!FindTrackersByParentAndTitle(
917 tracker_out->tracker_id(), title, &trackers) ||
918 !trackers.has_active()) {
923 index_->GetFileTracker(trackers.active_tracker(), &tracker);
925 DCHECK(tracker.has_synced_details());
926 const FileDetails& details = tracker.synced_details();
927 if (details.file_kind() != FILE_KIND_FOLDER && i != components.size() - 1) {
928 // This non-last component indicates file. Give up search.
932 tracker_out->CopyFrom(tracker);
933 *path_out = path_out->Append(components[i]);
939 void MetadataDatabase::UpdateByChangeList(
940 int64 largest_change_id,
941 ScopedVector<google_apis::ChangeResource> changes,
942 const SyncStatusCallback& callback) {
943 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
944 DCHECK_LE(index_->GetLargestChangeID(), largest_change_id);
946 for (size_t i = 0; i < changes.size(); ++i) {
947 const google_apis::ChangeResource& change = *changes[i];
948 if (HasNewerFileMetadata(change.file_id(), change.change_id()))
951 scoped_ptr<FileMetadata> metadata(
952 CreateFileMetadataFromChangeResource(change));
953 UpdateByFileMetadata(FROM_HERE, metadata.Pass(),
954 UPDATE_TRACKER_FOR_UNSYNCED_FILE);
957 UpdateLargestKnownChangeID(largest_change_id);
958 index_->SetLargestChangeID(largest_change_id);
959 WriteToDatabase(callback);
962 void MetadataDatabase::UpdateByFileResource(
963 const google_apis::FileResource& resource,
964 const SyncStatusCallback& callback) {
965 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
967 scoped_ptr<FileMetadata> metadata(
968 CreateFileMetadataFromFileResource(
969 GetLargestKnownChangeID(), resource));
970 UpdateByFileMetadata(FROM_HERE, metadata.Pass(),
971 UPDATE_TRACKER_FOR_UNSYNCED_FILE);
972 WriteToDatabase(callback);
975 void MetadataDatabase::UpdateByFileResourceList(
976 ScopedVector<google_apis::FileResource> resources,
977 const SyncStatusCallback& callback) {
978 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
980 for (size_t i = 0; i < resources.size(); ++i) {
981 scoped_ptr<FileMetadata> metadata(
982 CreateFileMetadataFromFileResource(
983 GetLargestKnownChangeID(), *resources[i]));
984 UpdateByFileMetadata(FROM_HERE, metadata.Pass(),
985 UPDATE_TRACKER_FOR_UNSYNCED_FILE);
987 WriteToDatabase(callback);
990 void MetadataDatabase::UpdateByDeletedRemoteFile(
991 const std::string& file_id,
992 const SyncStatusCallback& callback) {
993 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
995 scoped_ptr<FileMetadata> metadata(
996 CreateDeletedFileMetadata(GetLargestKnownChangeID(), file_id));
997 UpdateByFileMetadata(FROM_HERE, metadata.Pass(),
998 UPDATE_TRACKER_FOR_UNSYNCED_FILE);
999 WriteToDatabase(callback);
1002 void MetadataDatabase::UpdateByDeletedRemoteFileList(
1003 const FileIDList& file_ids,
1004 const SyncStatusCallback& callback) {
1005 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
1007 for (FileIDList::const_iterator itr = file_ids.begin();
1008 itr != file_ids.end(); ++itr) {
1009 scoped_ptr<FileMetadata> metadata(
1010 CreateDeletedFileMetadata(GetLargestKnownChangeID(), *itr));
1011 UpdateByFileMetadata(FROM_HERE, metadata.Pass(),
1012 UPDATE_TRACKER_FOR_UNSYNCED_FILE);
1014 WriteToDatabase(callback);
1017 void MetadataDatabase::ReplaceActiveTrackerWithNewResource(
1018 int64 parent_tracker_id,
1019 const google_apis::FileResource& resource,
1020 const SyncStatusCallback& callback) {
1021 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
1023 DCHECK(!index_->GetFileMetadata(resource.file_id(), NULL));
1024 DCHECK(index_->GetFileTracker(parent_tracker_id, NULL));
1026 UpdateByFileMetadata(
1028 CreateFileMetadataFromFileResource(GetLargestKnownChangeID(), resource),
1029 UPDATE_TRACKER_FOR_SYNCED_FILE);
1031 DCHECK(index_->GetFileMetadata(resource.file_id(), NULL));
1032 DCHECK(!index_->GetFileTrackerIDsByFileID(resource.file_id()).has_active());
1034 TrackerIDSet same_path_trackers =
1035 index_->GetFileTrackerIDsByParentAndTitle(
1036 parent_tracker_id, resource.title());
1037 FileTracker to_be_activated;
1038 if (!FilterFileTrackersByFileID(index_.get(), same_path_trackers,
1039 resource.file_id(), &to_be_activated)) {
1041 worker_task_runner_->PostTask(
1043 base::Bind(callback, SYNC_STATUS_FAILED));
1047 int64 tracker_id = to_be_activated.tracker_id();
1048 if (same_path_trackers.has_active()) {
1049 DeactivateFileTracker(same_path_trackers.active_tracker(),
1051 MARK_SAME_FILE_ID_TRACKERS_DIRTY,
1055 ActivateFileTracker(tracker_id, MARK_NOTHING_DIRTY, index_.get());
1056 WriteToDatabase(callback);
1059 void MetadataDatabase::PopulateFolderByChildList(
1060 const std::string& folder_id,
1061 const FileIDList& child_file_ids,
1062 const SyncStatusCallback& callback) {
1063 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
1065 TrackerIDSet trackers = index_->GetFileTrackerIDsByFileID(folder_id);
1066 if (!trackers.has_active()) {
1067 // It's OK that there is no folder to populate its children.
1068 // Inactive folders should ignore their contents updates.
1069 worker_task_runner_->PostTask(
1071 base::Bind(callback, SYNC_STATUS_OK));
1075 scoped_ptr<FileTracker> folder_tracker(new FileTracker);
1076 if (!index_->GetFileTracker(trackers.active_tracker(),
1077 folder_tracker.get())) {
1079 worker_task_runner_->PostTask(
1081 base::Bind(callback, SYNC_STATUS_FAILED));
1085 base::hash_set<std::string> children(child_file_ids.begin(),
1086 child_file_ids.end());
1088 std::vector<int64> known_children =
1089 index_->GetFileTrackerIDsByParent(folder_tracker->tracker_id());
1090 for (size_t i = 0; i < known_children.size(); ++i) {
1091 FileTracker tracker;
1092 if (!index_->GetFileTracker(known_children[i], &tracker)) {
1096 children.erase(tracker.file_id());
1099 for (base::hash_set<std::string>::const_iterator itr = children.begin();
1100 itr != children.end(); ++itr)
1101 CreateTrackerForParentAndFileID(*folder_tracker, *itr);
1102 folder_tracker->set_needs_folder_listing(false);
1103 if (folder_tracker->dirty() && !ShouldKeepDirty(*folder_tracker))
1104 folder_tracker->set_dirty(false);
1105 index_->StoreFileTracker(folder_tracker.Pass());
1107 WriteToDatabase(callback);
1110 void MetadataDatabase::UpdateTracker(int64 tracker_id,
1111 const FileDetails& updated_details,
1112 const SyncStatusCallback& callback) {
1113 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
1115 FileTracker tracker;
1116 if (!index_->GetFileTracker(tracker_id, &tracker)) {
1117 worker_task_runner_->PostTask(
1119 base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND));
1123 // Check if the tracker is to be deleted.
1124 if (updated_details.missing()) {
1125 FileMetadata metadata;
1126 if (!index_->GetFileMetadata(tracker.file_id(), &metadata) ||
1127 metadata.details().missing()) {
1128 // Both the tracker and metadata have the missing flag, now it's safe to
1129 // delete the |tracker|.
1130 RemoveFileTracker(tracker_id,
1131 MARK_SAME_FILE_ID_TRACKERS_DIRTY |
1132 MARK_SAME_PATH_TRACKERS_DIRTY,
1134 WriteToDatabase(callback);
1139 // Sync-root deletion should be handled separately by SyncEngine.
1140 DCHECK(tracker_id != GetSyncRootTrackerID() ||
1141 (tracker.has_synced_details() &&
1142 tracker.synced_details().title() == updated_details.title() &&
1143 !updated_details.missing()));
1145 if (tracker_id != GetSyncRootTrackerID()) {
1146 // Check if the tracker's parent is still in |parent_tracker_ids|.
1147 // If not, there should exist another tracker for the new parent, so delete
1149 FileTracker parent_tracker;
1150 index_->GetFileTracker(tracker.parent_tracker_id(), &parent_tracker);
1152 if (!HasFileAsParent(updated_details, parent_tracker.file_id())) {
1153 RemoveFileTracker(tracker.tracker_id(),
1154 MARK_SAME_PATH_TRACKERS_DIRTY,
1156 WriteToDatabase(callback);
1160 if (tracker.has_synced_details()) {
1161 // Check if the tracker was retitled. If it was, there should exist
1162 // another tracker for the new title, so delete the tracker being updated.
1163 if (tracker.synced_details().title() != updated_details.title()) {
1164 RemoveFileTracker(tracker.tracker_id(),
1165 MARK_SAME_FILE_ID_TRACKERS_DIRTY,
1167 WriteToDatabase(callback);
1171 // Check if any other tracker exists has the same parent, title and
1172 // file_id to the updated tracker. If it exists, delete the tracker being
1174 if (FilterFileTrackersByFileID(
1176 index_->GetFileTrackerIDsByParentAndTitle(
1177 parent_tracker.tracker_id(),
1178 updated_details.title()),
1181 RemoveFileTracker(tracker.tracker_id(),
1184 WriteToDatabase(callback);
1190 scoped_ptr<FileTracker> updated_tracker = CloneFileTracker(&tracker);
1191 *updated_tracker->mutable_synced_details() = updated_details;
1193 bool should_promote = false;
1195 // Activate the tracker if:
1196 // - There is no active tracker that tracks |tracker->file_id()|.
1197 // - There is no active tracker that has the same |parent| and |title|.
1198 if (!tracker.active() && CanActivateTracker(tracker)) {
1199 updated_tracker->set_active(true);
1200 updated_tracker->set_dirty(true);
1201 updated_tracker->set_needs_folder_listing(
1202 tracker.synced_details().file_kind() == FILE_KIND_FOLDER);
1203 should_promote = true;
1204 } else if (tracker.dirty() && !ShouldKeepDirty(tracker)) {
1205 updated_tracker->set_dirty(false);
1207 index_->StoreFileTracker(updated_tracker.Pass());
1209 index_->PromoteDemotedDirtyTracker(tracker_id);
1211 WriteToDatabase(callback);
1214 MetadataDatabase::ActivationStatus MetadataDatabase::TryActivateTracker(
1215 int64 parent_tracker_id,
1216 const std::string& file_id,
1217 const SyncStatusCallback& callback) {
1218 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
1220 FileMetadata metadata;
1221 if (!index_->GetFileMetadata(file_id, &metadata)) {
1223 worker_task_runner_->PostTask(
1225 base::Bind(callback, SYNC_STATUS_FAILED));
1226 return ACTIVATION_PENDING;
1228 std::string title = metadata.details().title();
1229 DCHECK(!HasInvalidTitle(title));
1231 TrackerIDSet same_file_id_trackers =
1232 index_->GetFileTrackerIDsByFileID(file_id);
1233 scoped_ptr<FileTracker> tracker_to_be_activated(new FileTracker);
1234 FilterFileTrackersByParentAndTitle(
1235 index_.get(), same_file_id_trackers, parent_tracker_id,
1236 title, tracker_to_be_activated.get());
1238 // Check if there is another active tracker that tracks |file_id|.
1239 // This can happen when the tracked file has multiple parents.
1240 // In this case, report the failure to the caller.
1241 if (!tracker_to_be_activated->active() && same_file_id_trackers.has_active())
1242 return ACTIVATION_FAILED_ANOTHER_ACTIVE_TRACKER;
1244 if (!tracker_to_be_activated->active()) {
1245 // Check if there exists another active tracker that has the same path to
1246 // the tracker. If there is, deactivate it, assuming the caller already
1247 // overrides local file with newly added file,
1248 TrackerIDSet same_title_trackers =
1249 index_->GetFileTrackerIDsByParentAndTitle(parent_tracker_id, title);
1250 if (same_title_trackers.has_active()) {
1251 RemoveAllDescendantTrackers(same_title_trackers.active_tracker(),
1254 scoped_ptr<FileTracker> tracker_to_be_deactivated(new FileTracker);
1255 if (index_->GetFileTracker(same_title_trackers.active_tracker(),
1256 tracker_to_be_deactivated.get())) {
1257 const std::string file_id = tracker_to_be_deactivated->file_id();
1258 tracker_to_be_deactivated->set_active(false);
1259 index_->StoreFileTracker(tracker_to_be_deactivated.Pass());
1261 MarkTrackersDirtyByFileID(file_id, index_.get());
1268 tracker_to_be_activated->set_dirty(false);
1269 tracker_to_be_activated->set_active(true);
1270 *tracker_to_be_activated->mutable_synced_details() = metadata.details();
1271 if (tracker_to_be_activated->synced_details().file_kind() ==
1273 tracker_to_be_activated->set_needs_folder_listing(true);
1275 tracker_to_be_activated->set_dirty(false);
1277 index_->StoreFileTracker(tracker_to_be_activated.Pass());
1279 WriteToDatabase(callback);
1280 return ACTIVATION_PENDING;
1283 void MetadataDatabase::LowerTrackerPriority(int64 tracker_id) {
1284 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
1285 index_->DemoteDirtyTracker(tracker_id);
1286 WriteToDatabase(base::Bind(&EmptyStatusCallback));
1289 bool MetadataDatabase::PromoteLowerPriorityTrackersToNormal() {
1290 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
1291 bool promoted = index_->PromoteDemotedDirtyTrackers();
1292 WriteToDatabase(base::Bind(&EmptyStatusCallback));
1296 void MetadataDatabase::PromoteDemotedTracker(int64 tracker_id) {
1297 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
1298 index_->PromoteDemotedDirtyTracker(tracker_id);
1299 WriteToDatabase(base::Bind(&EmptyStatusCallback));
1302 bool MetadataDatabase::GetNormalPriorityDirtyTracker(
1303 FileTracker* tracker_out) const {
1304 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
1306 int64 dirty_tracker_id = index_->PickDirtyTracker();
1307 if (!dirty_tracker_id)
1311 if (!index_->GetFileTracker(dirty_tracker_id, tracker_out)) {
1319 bool MetadataDatabase::HasLowPriorityDirtyTracker() const {
1320 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
1321 return index_->HasDemotedDirtyTracker();
1324 bool MetadataDatabase::HasDirtyTracker() const {
1325 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
1326 return index_->PickDirtyTracker() != kInvalidTrackerID;
1329 size_t MetadataDatabase::CountDirtyTracker() const {
1330 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
1331 return index_->CountDirtyTracker();
1334 bool MetadataDatabase::GetMultiParentFileTrackers(std::string* file_id_out,
1335 TrackerIDSet* trackers_out) {
1336 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
1337 DCHECK(file_id_out);
1338 DCHECK(trackers_out);
1340 std::string file_id = index_->PickMultiTrackerFileID();
1341 if (file_id.empty())
1344 TrackerIDSet trackers = index_->GetFileTrackerIDsByFileID(file_id);
1345 if (trackers.size() <= 1) {
1350 *file_id_out = file_id;
1351 std::swap(*trackers_out, trackers);
1355 size_t MetadataDatabase::CountFileMetadata() const {
1356 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
1357 return index_->CountFileMetadata();
1360 size_t MetadataDatabase::CountFileTracker() const {
1361 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
1362 return index_->CountFileTracker();
1365 bool MetadataDatabase::GetConflictingTrackers(TrackerIDSet* trackers_out) {
1366 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
1367 DCHECK(trackers_out);
1369 ParentIDAndTitle parent_and_title = index_->PickMultiBackingFilePath();
1370 if (parent_and_title.parent_id == kInvalidTrackerID)
1373 TrackerIDSet trackers = index_->GetFileTrackerIDsByParentAndTitle(
1374 parent_and_title.parent_id, parent_and_title.title);
1375 if (trackers.size() <= 1) {
1380 std::swap(*trackers_out, trackers);
1384 void MetadataDatabase::GetRegisteredAppIDs(std::vector<std::string>* app_ids) {
1385 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
1387 *app_ids = index_->GetRegisteredAppIDs();
1390 void MetadataDatabase::SweepDirtyTrackers(
1391 const std::vector<std::string>& file_ids,
1392 const SyncStatusCallback& callback) {
1393 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
1395 std::set<int64> tracker_ids;
1396 for (size_t i = 0; i < file_ids.size(); ++i) {
1397 TrackerIDSet trackers_for_file_id =
1398 index_->GetFileTrackerIDsByFileID(file_ids[i]);
1399 for (TrackerIDSet::iterator itr = trackers_for_file_id.begin();
1400 itr != trackers_for_file_id.end(); ++itr)
1401 tracker_ids.insert(*itr);
1404 for (std::set<int64>::iterator itr = tracker_ids.begin();
1405 itr != tracker_ids.end(); ++itr) {
1406 scoped_ptr<FileTracker> tracker(new FileTracker);
1407 if (!index_->GetFileTracker(*itr, tracker.get()) ||
1408 !CanClearDirty(*tracker))
1410 tracker->set_dirty(false);
1411 index_->StoreFileTracker(tracker.Pass());
1414 WriteToDatabase(callback);
1417 MetadataDatabase::MetadataDatabase(
1418 base::SequencedTaskRunner* worker_task_runner,
1419 const base::FilePath& database_path,
1420 leveldb::Env* env_override)
1421 : worker_task_runner_(worker_task_runner),
1422 database_path_(database_path),
1423 env_override_(env_override),
1424 largest_known_change_id_(0),
1425 weak_ptr_factory_(this) {
1426 DCHECK(worker_task_runner);
1430 void MetadataDatabase::CreateOnWorkerTaskRunner(
1431 scoped_ptr<CreateParam> create_param,
1432 const CreateCallback& callback) {
1433 DCHECK(create_param->worker_task_runner->RunsTasksOnCurrentThread());
1435 scoped_ptr<MetadataDatabase> metadata_database(
1436 new MetadataDatabase(create_param->worker_task_runner.get(),
1437 create_param->database_path,
1438 create_param->env_override));
1439 SyncStatusCode status = metadata_database->Initialize();
1440 if (status != SYNC_STATUS_OK)
1441 metadata_database.reset();
1443 metadata_database->DetachFromSequence();
1444 create_param->worker_task_runner->PostTask(
1447 callback, status, base::Passed(&metadata_database)));
1450 SyncStatusCode MetadataDatabase::Initialize() {
1451 base::ThreadRestrictions::AssertIOAllowed();
1452 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
1454 SyncStatusCode status = SYNC_STATUS_UNKNOWN;
1455 bool created = false;
1456 // Open database unless |db_| is overridden for testing.
1458 status = OpenDatabase(database_path_, env_override_, &db_, &created);
1459 if (status != SYNC_STATUS_OK)
1464 status = MigrateDatabaseIfNeeded(db_.get());
1465 if (status != SYNC_STATUS_OK)
1469 if (CommandLine::ForCurrentProcess()->HasSwitch(
1470 kEnableMetadataDatabaseOnDisk)) {
1471 index_ = MetadataDatabaseIndexOnDisk::Create(db_.get());
1473 index_ = MetadataDatabaseIndex::Create(db_.get());
1476 status = LevelDBStatusToSyncStatusCode(db_->Commit());
1477 if (status != SYNC_STATUS_OK)
1480 UpdateLargestKnownChangeID(index_->GetLargestChangeID());
1485 void MetadataDatabase::CreateTrackerForParentAndFileID(
1486 const FileTracker& parent_tracker,
1487 const std::string& file_id) {
1488 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
1489 CreateTrackerInternal(parent_tracker, file_id, NULL,
1490 UPDATE_TRACKER_FOR_UNSYNCED_FILE);
1493 void MetadataDatabase::CreateTrackerForParentAndFileMetadata(
1494 const FileTracker& parent_tracker,
1495 const FileMetadata& file_metadata,
1496 UpdateOption option) {
1497 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
1498 DCHECK(file_metadata.has_details());
1499 CreateTrackerInternal(parent_tracker,
1500 file_metadata.file_id(),
1501 &file_metadata.details(),
1505 void MetadataDatabase::CreateTrackerInternal(const FileTracker& parent_tracker,
1506 const std::string& file_id,
1507 const FileDetails* details,
1508 UpdateOption option) {
1509 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
1511 int64 tracker_id = IncrementTrackerID();
1512 scoped_ptr<FileTracker> tracker(new FileTracker);
1513 tracker->set_tracker_id(tracker_id);
1514 tracker->set_parent_tracker_id(parent_tracker.tracker_id());
1515 tracker->set_file_id(file_id);
1516 tracker->set_app_id(parent_tracker.app_id());
1517 tracker->set_tracker_kind(TRACKER_KIND_REGULAR);
1518 tracker->set_dirty(true);
1519 tracker->set_active(false);
1520 tracker->set_needs_folder_listing(false);
1522 *tracker->mutable_synced_details() = *details;
1523 if (option == UPDATE_TRACKER_FOR_UNSYNCED_FILE) {
1524 tracker->mutable_synced_details()->set_missing(true);
1525 tracker->mutable_synced_details()->clear_md5();
1528 index_->StoreFileTracker(tracker.Pass());
1531 void MetadataDatabase::MaybeAddTrackersForNewFile(
1532 const FileMetadata& metadata,
1533 UpdateOption option) {
1534 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
1536 std::set<int64> parents_to_exclude;
1537 TrackerIDSet existing_trackers =
1538 index_->GetFileTrackerIDsByFileID(metadata.file_id());
1539 for (TrackerIDSet::const_iterator itr = existing_trackers.begin();
1540 itr != existing_trackers.end(); ++itr) {
1541 FileTracker tracker;
1542 if (!index_->GetFileTracker(*itr, &tracker)) {
1547 int64 parent_tracker_id = tracker.parent_tracker_id();
1548 if (!parent_tracker_id)
1551 // Exclude |parent_tracker_id| if it already has a tracker that has
1552 // unknown title or has the same title with |file|.
1553 if (!tracker.has_synced_details() ||
1554 tracker.synced_details().title() == metadata.details().title()) {
1555 parents_to_exclude.insert(parent_tracker_id);
1559 for (int i = 0; i < metadata.details().parent_folder_ids_size(); ++i) {
1560 std::string parent_folder_id = metadata.details().parent_folder_ids(i);
1561 TrackerIDSet parent_trackers =
1562 index_->GetFileTrackerIDsByFileID(parent_folder_id);
1563 for (TrackerIDSet::const_iterator itr = parent_trackers.begin();
1564 itr != parent_trackers.end(); ++itr) {
1565 FileTracker parent_tracker;
1566 index_->GetFileTracker(*itr, &parent_tracker);
1567 if (!parent_tracker.active())
1570 if (ContainsKey(parents_to_exclude, parent_tracker.tracker_id()))
1573 CreateTrackerForParentAndFileMetadata(
1574 parent_tracker, metadata, option);
1579 int64 MetadataDatabase::IncrementTrackerID() {
1580 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
1582 int64 tracker_id = index_->GetNextTrackerID();
1583 index_->SetNextTrackerID(tracker_id + 1);
1584 DCHECK_GT(tracker_id, 0);
1588 bool MetadataDatabase::CanActivateTracker(const FileTracker& tracker) {
1589 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
1590 DCHECK(!tracker.active());
1591 DCHECK_NE(index_->GetSyncRootTrackerID(), tracker.tracker_id());
1593 if (HasActiveTrackerForFileID(tracker.file_id()))
1596 if (tracker.app_id().empty() &&
1597 tracker.tracker_id() != GetSyncRootTrackerID()) {
1601 if (!tracker.has_synced_details())
1603 if (tracker.synced_details().file_kind() == FILE_KIND_UNSUPPORTED)
1605 if (HasInvalidTitle(tracker.synced_details().title()))
1607 DCHECK(tracker.parent_tracker_id());
1609 return !HasActiveTrackerForPath(tracker.parent_tracker_id(),
1610 tracker.synced_details().title());
1613 bool MetadataDatabase::ShouldKeepDirty(const FileTracker& tracker) const {
1614 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
1616 if (HasDisabledAppRoot(tracker))
1619 DCHECK(tracker.dirty());
1620 if (!tracker.has_synced_details())
1623 FileMetadata metadata;
1624 if (!index_->GetFileMetadata(tracker.file_id(), &metadata))
1626 DCHECK(metadata.has_details());
1628 const FileDetails& local_details = tracker.synced_details();
1629 const FileDetails& remote_details = metadata.details();
1631 if (tracker.active()) {
1632 if (tracker.needs_folder_listing())
1634 if (local_details.md5() != remote_details.md5())
1636 if (local_details.missing() != remote_details.missing())
1640 if (local_details.title() != remote_details.title())
1646 bool MetadataDatabase::HasDisabledAppRoot(const FileTracker& tracker) const {
1647 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
1649 int64 app_root_tracker_id = index_->GetAppRootTracker(tracker.app_id());
1650 if (app_root_tracker_id == kInvalidTrackerID)
1653 FileTracker app_root_tracker;
1654 if (!index_->GetFileTracker(app_root_tracker_id, &app_root_tracker)) {
1658 return app_root_tracker.tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT;
1661 bool MetadataDatabase::HasActiveTrackerForFileID(
1662 const std::string& file_id) const {
1663 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
1664 return index_->GetFileTrackerIDsByFileID(file_id).has_active();
1667 bool MetadataDatabase::HasActiveTrackerForPath(int64 parent_tracker_id,
1668 const std::string& title) const {
1669 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
1670 return index_->GetFileTrackerIDsByParentAndTitle(parent_tracker_id, title)
1674 void MetadataDatabase::RemoveUnneededTrackersForMissingFile(
1675 const std::string& file_id) {
1676 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
1678 TrackerIDSet trackers = index_->GetFileTrackerIDsByFileID(file_id);
1679 for (TrackerIDSet::const_iterator itr = trackers.begin();
1680 itr != trackers.end(); ++itr) {
1681 FileTracker tracker;
1682 if (!index_->GetFileTracker(*itr, &tracker)) {
1687 if (!tracker.has_synced_details() || tracker.synced_details().missing()) {
1688 RemoveFileTracker(*itr, MARK_NOTHING_DIRTY, index_.get());
1693 void MetadataDatabase::UpdateByFileMetadata(
1694 const tracked_objects::Location& from_where,
1695 scoped_ptr<FileMetadata> metadata,
1696 UpdateOption option) {
1697 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
1699 DCHECK(metadata->has_details());
1701 DVLOG(1) << from_where.function_name() << ": "
1702 << metadata->file_id() << " ("
1703 << metadata->details().title() << ")"
1704 << (metadata->details().missing() ? " deleted" : "");
1706 std::string file_id = metadata->file_id();
1707 if (metadata->details().missing())
1708 RemoveUnneededTrackersForMissingFile(file_id);
1710 MaybeAddTrackersForNewFile(*metadata, option);
1712 TrackerIDSet trackers = index_->GetFileTrackerIDsByFileID(file_id);
1713 if (!trackers.empty()) {
1714 index_->StoreFileMetadata(metadata.Pass());
1716 if (option != UPDATE_TRACKER_FOR_SYNCED_FILE)
1717 MarkTrackerSetDirty(trackers, index_.get());
1721 void MetadataDatabase::WriteToDatabase(const SyncStatusCallback& callback) {
1722 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
1724 leveldb::Status status = db_->Commit();
1725 callback.Run(LevelDBStatusToSyncStatusCode(status));
1728 scoped_ptr<base::ListValue> MetadataDatabase::DumpFiles(
1729 const std::string& app_id) {
1730 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
1732 scoped_ptr<base::ListValue> files(new base::ListValue);
1734 FileTracker app_root_tracker;
1735 if (!FindAppRootTracker(app_id, &app_root_tracker))
1736 return files.Pass();
1738 std::vector<int64> stack;
1740 index_->GetFileTrackerIDsByParent(app_root_tracker.tracker_id()), &stack);
1741 while (!stack.empty()) {
1742 int64 tracker_id = stack.back();
1744 AppendContents(index_->GetFileTrackerIDsByParent(tracker_id), &stack);
1746 FileTracker tracker;
1747 if (!index_->GetFileTracker(tracker_id, &tracker)) {
1751 base::DictionaryValue* file = new base::DictionaryValue;
1753 base::FilePath path = BuildDisplayPathForTracker(tracker);
1754 file->SetString("path", path.AsUTF8Unsafe());
1755 if (tracker.has_synced_details()) {
1756 file->SetString("title", tracker.synced_details().title());
1757 file->SetString("type",
1758 FileKindToString(tracker.synced_details().file_kind()));
1761 base::DictionaryValue* details = new base::DictionaryValue;
1762 details->SetString("file_id", tracker.file_id());
1763 if (tracker.has_synced_details() &&
1764 tracker.synced_details().file_kind() == FILE_KIND_FILE)
1765 details->SetString("md5", tracker.synced_details().md5());
1766 details->SetString("active", tracker.active() ? "true" : "false");
1767 details->SetString("dirty", tracker.dirty() ? "true" : "false");
1769 file->Set("details", details);
1771 files->Append(file);
1774 return files.Pass();
1777 scoped_ptr<base::ListValue> MetadataDatabase::DumpDatabase() {
1778 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
1780 scoped_ptr<base::ListValue> list(new base::ListValue);
1781 list->Append(DumpTrackers().release());
1782 list->Append(DumpMetadata().release());
1786 bool MetadataDatabase::HasNewerFileMetadata(const std::string& file_id,
1788 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
1790 FileMetadata metadata;
1791 if (!index_->GetFileMetadata(file_id, &metadata))
1793 DCHECK(metadata.has_details());
1794 return metadata.details().change_id() >= change_id;
1797 scoped_ptr<base::ListValue> MetadataDatabase::DumpTrackers() {
1798 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
1800 scoped_ptr<base::ListValue> trackers(new base::ListValue);
1802 // Append the first element for metadata.
1803 base::DictionaryValue* metadata = new base::DictionaryValue;
1804 const char *trackerKeys[] = {
1805 "tracker_id", "path", "file_id", "tracker_kind", "app_id",
1806 "active", "dirty", "folder_listing",
1807 "title", "kind", "md5", "etag", "missing", "change_id",
1809 std::vector<std::string> key_strings(
1810 trackerKeys, trackerKeys + ARRAYSIZE_UNSAFE(trackerKeys));
1811 base::ListValue* keys = new base::ListValue;
1812 keys->AppendStrings(key_strings);
1813 metadata->SetString("title", "Trackers");
1814 metadata->Set("keys", keys);
1815 trackers->Append(metadata);
1817 // Append tracker data.
1818 std::vector<int64> tracker_ids(index_->GetAllTrackerIDs());
1819 for (std::vector<int64>::const_iterator itr = tracker_ids.begin();
1820 itr != tracker_ids.end(); ++itr) {
1821 const int64 tracker_id = *itr;
1822 FileTracker tracker;
1823 if (!index_->GetFileTracker(tracker_id, &tracker)) {
1828 base::DictionaryValue* dict = new base::DictionaryValue;
1829 base::FilePath path = BuildDisplayPathForTracker(tracker);
1830 dict->SetString("tracker_id", base::Int64ToString(tracker_id));
1831 dict->SetString("path", path.AsUTF8Unsafe());
1832 dict->SetString("file_id", tracker.file_id());
1833 TrackerKind tracker_kind = tracker.tracker_kind();
1836 tracker_kind == TRACKER_KIND_APP_ROOT ? "AppRoot" :
1837 tracker_kind == TRACKER_KIND_DISABLED_APP_ROOT ? "Disabled App" :
1838 tracker.tracker_id() == GetSyncRootTrackerID() ? "SyncRoot" :
1840 dict->SetString("app_id", tracker.app_id());
1841 dict->SetString("active", tracker.active() ? "true" : "false");
1842 dict->SetString("dirty", tracker.dirty() ? "true" : "false");
1843 dict->SetString("folder_listing",
1844 tracker.needs_folder_listing() ? "needed" : "no");
1845 if (tracker.has_synced_details()) {
1846 const FileDetails& details = tracker.synced_details();
1847 dict->SetString("title", details.title());
1848 dict->SetString("kind", FileKindToString(details.file_kind()));
1849 dict->SetString("md5", details.md5());
1850 dict->SetString("etag", details.etag());
1851 dict->SetString("missing", details.missing() ? "true" : "false");
1852 dict->SetString("change_id", base::Int64ToString(details.change_id()));
1854 trackers->Append(dict);
1856 return trackers.Pass();
1859 scoped_ptr<base::ListValue> MetadataDatabase::DumpMetadata() {
1860 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
1862 scoped_ptr<base::ListValue> files(new base::ListValue);
1864 // Append the first element for metadata.
1865 base::DictionaryValue* metadata = new base::DictionaryValue;
1866 const char *fileKeys[] = {
1867 "file_id", "title", "type", "md5", "etag", "missing",
1868 "change_id", "parents"
1870 std::vector<std::string> key_strings(
1871 fileKeys, fileKeys + ARRAYSIZE_UNSAFE(fileKeys));
1872 base::ListValue* keys = new base::ListValue;
1873 keys->AppendStrings(key_strings);
1874 metadata->SetString("title", "Metadata");
1875 metadata->Set("keys", keys);
1876 files->Append(metadata);
1878 // Append metadata data.
1879 std::vector<std::string> metadata_ids(index_->GetAllMetadataIDs());
1880 for (std::vector<std::string>::const_iterator itr = metadata_ids.begin();
1881 itr != metadata_ids.end(); ++itr) {
1882 const std::string& file_id = *itr;
1884 if (!index_->GetFileMetadata(file_id, &file)) {
1889 base::DictionaryValue* dict = new base::DictionaryValue;
1890 dict->SetString("file_id", file_id);
1891 if (file.has_details()) {
1892 const FileDetails& details = file.details();
1893 dict->SetString("title", details.title());
1894 dict->SetString("type", FileKindToString(details.file_kind()));
1895 dict->SetString("md5", details.md5());
1896 dict->SetString("etag", details.etag());
1897 dict->SetString("missing", details.missing() ? "true" : "false");
1898 dict->SetString("change_id", base::Int64ToString(details.change_id()));
1900 std::vector<std::string> parents;
1901 for (int i = 0; i < details.parent_folder_ids_size(); ++i)
1902 parents.push_back(details.parent_folder_ids(i));
1903 dict->SetString("parents", JoinString(parents, ","));
1905 files->Append(dict);
1907 return files.Pass();
1910 void MetadataDatabase::AttachSyncRoot(
1911 const google_apis::FileResource& sync_root_folder) {
1912 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
1914 scoped_ptr<FileMetadata> sync_root_metadata =
1915 CreateFileMetadataFromFileResource(
1916 GetLargestKnownChangeID(), sync_root_folder);
1917 scoped_ptr<FileTracker> sync_root_tracker =
1918 CreateSyncRootTracker(IncrementTrackerID(), *sync_root_metadata);
1920 index_->SetSyncRootTrackerID(sync_root_tracker->tracker_id());
1921 index_->StoreFileMetadata(sync_root_metadata.Pass());
1922 index_->StoreFileTracker(sync_root_tracker.Pass());
1925 void MetadataDatabase::AttachInitialAppRoot(
1926 const google_apis::FileResource& app_root_folder) {
1927 scoped_ptr<FileMetadata> app_root_metadata =
1928 CreateFileMetadataFromFileResource(
1929 GetLargestKnownChangeID(), app_root_folder);
1930 scoped_ptr<FileTracker> app_root_tracker =
1931 CreateInitialAppRootTracker(IncrementTrackerID(),
1932 GetSyncRootTrackerID(),
1933 *app_root_metadata);
1935 index_->StoreFileMetadata(app_root_metadata.Pass());
1936 index_->StoreFileTracker(app_root_tracker.Pass());
1939 void MetadataDatabase::DetachFromSequence() {
1940 worker_sequence_checker_.DetachFromSequence();
1943 bool MetadataDatabase::CanClearDirty(const FileTracker& tracker) {
1944 DCHECK(worker_sequence_checker_.CalledOnValidSequencedThread());
1946 FileMetadata metadata;
1947 if (!index_->GetFileMetadata(tracker.file_id(), &metadata) ||
1948 !tracker.active() || !tracker.dirty() ||
1949 !tracker.has_synced_details() ||
1950 tracker.needs_folder_listing())
1953 const FileDetails& remote_details = metadata.details();
1954 const FileDetails& synced_details = tracker.synced_details();
1955 if (remote_details.title() != synced_details.title() ||
1956 remote_details.md5() != synced_details.md5())
1959 std::set<std::string> parents;
1960 for (int i = 0; i < remote_details.parent_folder_ids_size(); ++i)
1961 parents.insert(remote_details.parent_folder_ids(i));
1963 for (int i = 0; i < synced_details.parent_folder_ids_size(); ++i)
1964 if (parents.erase(synced_details.parent_folder_ids(i)) != 1)
1967 if (!parents.empty())
1973 } // namespace drive_backend
1974 } // namespace sync_file_system