Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / sync_file_system / drive_backend / metadata_database.cc
blobf23471f2f09353822d42f8b568e187fd31e7ce52
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"
7 #include <algorithm>
8 #include <stack>
10 #include "base/bind.h"
11 #include "base/command_line.h"
12 #include "base/files/file_path.h"
13 #include "base/files/file_util.h"
14 #include "base/location.h"
15 #include "base/memory/scoped_vector.h"
16 #include "base/single_thread_task_runner.h"
17 #include "base/stl_util.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/string_util.h"
20 #include "base/strings/stringprintf.h"
21 #include "base/task_runner_util.h"
22 #include "base/thread_task_runner_handle.h"
23 #include "base/threading/thread_restrictions.h"
24 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h"
25 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_util.h"
26 #include "chrome/browser/sync_file_system/drive_backend/leveldb_wrapper.h"
27 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.pb.h"
28 #include "chrome/browser/sync_file_system/drive_backend/metadata_database_index.h"
29 #include "chrome/browser/sync_file_system/drive_backend/metadata_database_index_interface.h"
30 #include "chrome/browser/sync_file_system/drive_backend/metadata_database_index_on_disk.h"
31 #include "chrome/browser/sync_file_system/drive_backend/metadata_db_migration_util.h"
32 #include "chrome/browser/sync_file_system/logger.h"
33 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
34 #include "components/drive/drive_api_util.h"
35 #include "google_apis/drive/drive_api_parser.h"
36 #include "storage/common/fileapi/file_system_util.h"
37 #include "third_party/leveldatabase/env_chromium.h"
38 #include "third_party/leveldatabase/src/include/leveldb/db.h"
39 #include "third_party/leveldatabase/src/include/leveldb/env.h"
40 #include "third_party/leveldatabase/src/include/leveldb/status.h"
41 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
43 namespace sync_file_system {
44 namespace drive_backend {
46 namespace {
48 // Command line flag to disable on-disk indexing.
49 const char kDisableMetadataDatabaseOnDisk[] = "disable-syncfs-on-disk-indexing";
51 std::string FileKindToString(FileKind file_kind) {
52 switch (file_kind) {
53 case FILE_KIND_UNSUPPORTED:
54 return "unsupported";
55 case FILE_KIND_FILE:
56 return "file";
57 case FILE_KIND_FOLDER:
58 return "folder";
61 NOTREACHED();
62 return "unknown";
65 base::FilePath ReverseConcatPathComponents(
66 const std::vector<base::FilePath>& components) {
67 if (components.empty())
68 return base::FilePath(FILE_PATH_LITERAL("/")).NormalizePathSeparators();
70 size_t total_size = 0;
71 typedef std::vector<base::FilePath> PathComponents;
72 for (PathComponents::const_iterator itr = components.begin();
73 itr != components.end(); ++itr)
74 total_size += itr->value().size() + 1;
76 base::FilePath::StringType result;
77 result.reserve(total_size);
78 for (PathComponents::const_reverse_iterator itr = components.rbegin();
79 itr != components.rend(); ++itr) {
80 result.append(1, base::FilePath::kSeparators[0]);
81 result.append(itr->value());
84 return base::FilePath(result).NormalizePathSeparators();
87 void PopulateFileDetailsByFileResource(
88 const google_apis::FileResource& file_resource,
89 FileDetails* details) {
90 details->clear_parent_folder_ids();
91 for (std::vector<google_apis::ParentReference>::const_iterator itr =
92 file_resource.parents().begin();
93 itr != file_resource.parents().end();
94 ++itr) {
95 details->add_parent_folder_ids(itr->file_id());
97 details->set_title(file_resource.title());
99 if (file_resource.IsDirectory())
100 details->set_file_kind(FILE_KIND_FOLDER);
101 else if (file_resource.IsHostedDocument())
102 details->set_file_kind(FILE_KIND_UNSUPPORTED);
103 else
104 details->set_file_kind(FILE_KIND_FILE);
106 details->set_md5(file_resource.md5_checksum());
107 details->set_etag(file_resource.etag());
108 details->set_creation_time(file_resource.created_date().ToInternalValue());
109 details->set_modification_time(
110 file_resource.modified_date().ToInternalValue());
111 details->set_missing(file_resource.labels().is_trashed());
114 scoped_ptr<FileMetadata> CreateFileMetadataFromFileResource(
115 int64 change_id,
116 const google_apis::FileResource& resource) {
117 scoped_ptr<FileMetadata> file(new FileMetadata);
118 file->set_file_id(resource.file_id());
120 FileDetails* details = file->mutable_details();
121 details->set_change_id(change_id);
123 if (resource.labels().is_trashed()) {
124 details->set_missing(true);
125 return file.Pass();
128 PopulateFileDetailsByFileResource(resource, details);
129 return file.Pass();
132 scoped_ptr<FileMetadata> CreateFileMetadataFromChangeResource(
133 const google_apis::ChangeResource& change) {
134 scoped_ptr<FileMetadata> file(new FileMetadata);
135 file->set_file_id(change.file_id());
137 FileDetails* details = file->mutable_details();
138 details->set_change_id(change.change_id());
140 if (change.is_deleted()) {
141 details->set_missing(true);
142 return file.Pass();
145 PopulateFileDetailsByFileResource(*change.file(), details);
146 return file.Pass();
149 scoped_ptr<FileMetadata> CreateDeletedFileMetadata(
150 int64 change_id,
151 const std::string& file_id) {
152 scoped_ptr<FileMetadata> file(new FileMetadata);
153 file->set_file_id(file_id);
155 FileDetails* details = file->mutable_details();
156 details->set_change_id(change_id);
157 details->set_missing(true);
158 return file.Pass();
161 scoped_ptr<FileTracker> CreateSyncRootTracker(
162 int64 tracker_id,
163 const FileMetadata& sync_root_metadata) {
164 scoped_ptr<FileTracker> sync_root_tracker(new FileTracker);
165 sync_root_tracker->set_tracker_id(tracker_id);
166 sync_root_tracker->set_file_id(sync_root_metadata.file_id());
167 sync_root_tracker->set_parent_tracker_id(0);
168 sync_root_tracker->set_tracker_kind(TRACKER_KIND_REGULAR);
169 sync_root_tracker->set_dirty(false);
170 sync_root_tracker->set_active(true);
171 sync_root_tracker->set_needs_folder_listing(false);
172 *sync_root_tracker->mutable_synced_details() = sync_root_metadata.details();
173 return sync_root_tracker.Pass();
176 scoped_ptr<FileTracker> CreateInitialAppRootTracker(
177 int64 tracker_id,
178 int64 parent_tracker_id,
179 const FileMetadata& app_root_metadata) {
180 scoped_ptr<FileTracker> app_root_tracker(new FileTracker);
181 app_root_tracker->set_tracker_id(tracker_id);
182 app_root_tracker->set_parent_tracker_id(parent_tracker_id);
183 app_root_tracker->set_file_id(app_root_metadata.file_id());
184 app_root_tracker->set_tracker_kind(TRACKER_KIND_REGULAR);
185 app_root_tracker->set_dirty(false);
186 app_root_tracker->set_active(false);
187 app_root_tracker->set_needs_folder_listing(false);
188 *app_root_tracker->mutable_synced_details() = app_root_metadata.details();
189 return app_root_tracker.Pass();
192 scoped_ptr<FileTracker> CloneFileTracker(const FileTracker* obj) {
193 if (!obj)
194 return nullptr;
195 return scoped_ptr<FileTracker>(new FileTracker(*obj));
198 // Returns true if |db| has no content.
199 bool IsDatabaseEmpty(LevelDBWrapper* db) {
200 DCHECK(db);
201 scoped_ptr<LevelDBWrapper::Iterator> itr(db->NewIterator());
202 itr->SeekToFirst();
203 return !itr->Valid();
206 SyncStatusCode OpenDatabase(const base::FilePath& path,
207 leveldb::Env* env_override,
208 scoped_ptr<LevelDBWrapper>* db_out,
209 bool* created) {
210 base::ThreadRestrictions::AssertIOAllowed();
211 DCHECK(db_out);
212 DCHECK(created);
213 DCHECK(path.IsAbsolute());
215 leveldb::Options options;
216 options.max_open_files = 0; // Use minimum.
217 options.create_if_missing = true;
218 options.paranoid_checks = true;
219 options.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue;
220 if (env_override)
221 options.env = env_override;
222 leveldb::DB* db = nullptr;
223 leveldb::Status db_status =
224 leveldb::DB::Open(options, path.AsUTF8Unsafe(), &db);
225 UMA_HISTOGRAM_ENUMERATION("SyncFileSystem.Database.Open",
226 leveldb_env::GetLevelDBStatusUMAValue(db_status),
227 leveldb_env::LEVELDB_STATUS_MAX);
228 SyncStatusCode status = LevelDBStatusToSyncStatusCode(db_status);
229 if (status != SYNC_STATUS_OK) {
230 delete db;
231 return status;
234 db_out->reset(new LevelDBWrapper(make_scoped_ptr(db)));
235 *created = IsDatabaseEmpty(db_out->get());
236 return status;
239 SyncStatusCode MigrateDatabaseIfNeeded(LevelDBWrapper* db) {
240 // See metadata_database_index.cc for the database schema.
241 base::ThreadRestrictions::AssertIOAllowed();
242 DCHECK(db);
243 std::string value;
244 leveldb::Status status = db->Get(kDatabaseVersionKey, &value);
245 int64 version = 0;
246 if (status.ok()) {
247 if (!base::StringToInt64(value, &version))
248 return SYNC_DATABASE_ERROR_FAILED;
249 } else {
250 if (!status.IsNotFound())
251 return SYNC_DATABASE_ERROR_FAILED;
254 switch (version) {
255 case 0:
256 case 1:
257 case 2:
258 // Drop all data in old database and refetch them from the remote service.
259 NOTREACHED();
260 return SYNC_DATABASE_ERROR_FAILED;
261 case 3:
262 DCHECK_EQ(3, kCurrentDatabaseVersion);
263 // If MetadataDatabaseOnDisk is enabled, migration will be done in
264 // MetadataDatabaseOnDisk::Create().
265 // TODO(peria): Move the migration code (from v3 to v4) here.
266 return SYNC_STATUS_OK;
267 case 4:
268 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
269 kDisableMetadataDatabaseOnDisk)) {
270 MigrateDatabaseFromV4ToV3(db->GetLevelDB());
272 return SYNC_STATUS_OK;
273 default:
274 return SYNC_DATABASE_ERROR_FAILED;
278 bool HasInvalidTitle(const std::string& title) {
279 return title.empty() ||
280 title.find('/') != std::string::npos ||
281 title.find('\\') != std::string::npos;
284 void MarkTrackerSetDirty(const TrackerIDSet& trackers,
285 MetadataDatabaseIndexInterface* index) {
286 for (TrackerIDSet::const_iterator itr = trackers.begin();
287 itr != trackers.end(); ++itr) {
288 scoped_ptr<FileTracker> tracker(new FileTracker);
289 index->GetFileTracker(*itr, tracker.get());
290 if (tracker->dirty())
291 continue;
292 tracker->set_dirty(true);
293 index->StoreFileTracker(tracker.Pass());
297 void MarkTrackersDirtyByPath(int64 parent_tracker_id,
298 const std::string& title,
299 MetadataDatabaseIndexInterface* index) {
300 if (parent_tracker_id == kInvalidTrackerID || title.empty())
301 return;
302 MarkTrackerSetDirty(
303 index->GetFileTrackerIDsByParentAndTitle(parent_tracker_id, title),
304 index);
307 void MarkTrackersDirtyByFileID(const std::string& file_id,
308 MetadataDatabaseIndexInterface* index) {
309 MarkTrackerSetDirty(index->GetFileTrackerIDsByFileID(file_id), index);
312 void MarkTrackersDirtyRecursively(int64 root_tracker_id,
313 MetadataDatabaseIndexInterface* index) {
314 std::vector<int64> stack;
315 stack.push_back(root_tracker_id);
316 while (!stack.empty()) {
317 int64 tracker_id = stack.back();
318 stack.pop_back();
319 AppendContents(index->GetFileTrackerIDsByParent(tracker_id), &stack);
321 scoped_ptr<FileTracker> tracker(new FileTracker);
322 index->GetFileTracker(tracker_id, tracker.get());
323 tracker->set_dirty(true);
325 index->StoreFileTracker(tracker.Pass());
329 void RemoveAllDescendantTrackers(int64 root_tracker_id,
330 MetadataDatabaseIndexInterface* index) {
331 std::vector<int64> pending_trackers;
332 AppendContents(index->GetFileTrackerIDsByParent(root_tracker_id),
333 &pending_trackers);
335 std::vector<int64> to_be_removed;
337 // List trackers to remove.
338 while (!pending_trackers.empty()) {
339 int64 tracker_id = pending_trackers.back();
340 pending_trackers.pop_back();
341 AppendContents(index->GetFileTrackerIDsByParent(tracker_id),
342 &pending_trackers);
343 to_be_removed.push_back(tracker_id);
346 // Remove trackers in the reversed order.
347 base::hash_set<std::string> affected_file_ids;
348 for (std::vector<int64>::reverse_iterator itr = to_be_removed.rbegin();
349 itr != to_be_removed.rend(); ++itr) {
350 FileTracker tracker;
351 index->GetFileTracker(*itr, &tracker);
352 affected_file_ids.insert(tracker.file_id());
353 index->RemoveFileTracker(*itr);
356 for (base::hash_set<std::string>::iterator itr = affected_file_ids.begin();
357 itr != affected_file_ids.end(); ++itr) {
358 TrackerIDSet trackers = index->GetFileTrackerIDsByFileID(*itr);
359 if (trackers.empty()) {
360 // Remove metadata that no longer has any tracker.
361 index->RemoveFileMetadata(*itr);
362 } else {
363 MarkTrackerSetDirty(trackers, index);
368 bool FilterFileTrackersByParent(
369 const MetadataDatabaseIndexInterface* index,
370 const TrackerIDSet& trackers,
371 int64 parent_tracker_id,
372 FileTracker* tracker_out) {
373 FileTracker tracker;
374 for (TrackerIDSet::const_iterator itr = trackers.begin();
375 itr != trackers.end(); ++itr) {
376 if (!index->GetFileTracker(*itr, &tracker)) {
377 NOTREACHED();
378 continue;
381 if (tracker.parent_tracker_id() == parent_tracker_id) {
382 if (tracker_out)
383 tracker_out->CopyFrom(tracker);
384 return true;
387 return false;
390 bool FilterFileTrackersByParentAndTitle(
391 const MetadataDatabaseIndexInterface* index,
392 const TrackerIDSet& trackers,
393 int64 parent_tracker_id,
394 const std::string& title,
395 FileTracker* result) {
396 bool found = false;
397 for (TrackerIDSet::const_iterator itr = trackers.begin();
398 itr != trackers.end(); ++itr) {
399 FileTracker tracker;
400 if (!index->GetFileTracker(*itr, &tracker)) {
401 NOTREACHED();
402 continue;
405 if (tracker.parent_tracker_id() != parent_tracker_id)
406 continue;
408 if (tracker.has_synced_details() &&
409 tracker.synced_details().title() != title)
410 continue;
412 // Prioritize trackers that has |synced_details| when |trackers| has
413 // multiple candidates.
414 if (!found || tracker.has_synced_details()) {
415 found = true;
416 if (result)
417 result->CopyFrom(tracker);
418 if (!result || result->has_synced_details())
419 return found;
423 return found;
426 bool FilterFileTrackersByFileID(
427 const MetadataDatabaseIndexInterface* index,
428 const TrackerIDSet& trackers,
429 const std::string& file_id,
430 FileTracker* tracker_out) {
431 FileTracker tracker;
432 for (TrackerIDSet::const_iterator itr = trackers.begin();
433 itr != trackers.end(); ++itr) {
434 if (!index->GetFileTracker(*itr, &tracker)) {
435 NOTREACHED();
436 continue;
439 if (tracker.file_id() == file_id) {
440 if (tracker_out)
441 tracker_out->CopyFrom(tracker);
442 return true;
445 return false;
448 enum DirtyingOption {
449 MARK_NOTHING_DIRTY = 0,
450 MARK_ITSELF_DIRTY = 1 << 0,
451 MARK_SAME_FILE_ID_TRACKERS_DIRTY = 1 << 1,
452 MARK_SAME_PATH_TRACKERS_DIRTY = 1 << 2,
455 void ActivateFileTracker(int64 tracker_id,
456 int dirtying_options,
457 MetadataDatabaseIndexInterface* index) {
458 DCHECK(dirtying_options == MARK_NOTHING_DIRTY ||
459 dirtying_options == MARK_ITSELF_DIRTY);
461 scoped_ptr<FileTracker> tracker(new FileTracker);
462 index->GetFileTracker(tracker_id, tracker.get());
463 tracker->set_active(true);
464 if (dirtying_options & MARK_ITSELF_DIRTY) {
465 tracker->set_dirty(true);
466 tracker->set_needs_folder_listing(
467 tracker->has_synced_details() &&
468 tracker->synced_details().file_kind() == FILE_KIND_FOLDER);
469 } else {
470 tracker->set_dirty(false);
471 tracker->set_needs_folder_listing(false);
474 index->StoreFileTracker(tracker.Pass());
477 void DeactivateFileTracker(int64 tracker_id,
478 int dirtying_options,
479 MetadataDatabaseIndexInterface* index) {
480 RemoveAllDescendantTrackers(tracker_id, index);
482 scoped_ptr<FileTracker> tracker(new FileTracker);
483 index->GetFileTracker(tracker_id, tracker.get());
485 if (dirtying_options & MARK_SAME_FILE_ID_TRACKERS_DIRTY)
486 MarkTrackersDirtyByFileID(tracker->file_id(), index);
487 if (dirtying_options & MARK_SAME_PATH_TRACKERS_DIRTY) {
488 MarkTrackersDirtyByPath(tracker->parent_tracker_id(),
489 GetTrackerTitle(*tracker), index);
492 tracker->set_dirty(dirtying_options & MARK_ITSELF_DIRTY);
493 tracker->set_active(false);
494 index->StoreFileTracker(tracker.Pass());
497 void RemoveFileTracker(int64 tracker_id,
498 int dirtying_options,
499 MetadataDatabaseIndexInterface* index) {
500 DCHECK(!(dirtying_options & MARK_ITSELF_DIRTY));
502 FileTracker tracker;
503 if (!index->GetFileTracker(tracker_id, &tracker))
504 return;
506 std::string file_id = tracker.file_id();
507 int64 parent_tracker_id = tracker.parent_tracker_id();
508 std::string title = GetTrackerTitle(tracker);
510 RemoveAllDescendantTrackers(tracker_id, index);
511 index->RemoveFileTracker(tracker_id);
513 if (dirtying_options & MARK_SAME_FILE_ID_TRACKERS_DIRTY)
514 MarkTrackersDirtyByFileID(file_id, index);
515 if (dirtying_options & MARK_SAME_PATH_TRACKERS_DIRTY)
516 MarkTrackersDirtyByPath(parent_tracker_id, title, index);
518 if (index->GetFileTrackerIDsByFileID(file_id).empty()) {
519 index->RemoveFileMetadata(file_id);
523 } // namespace
525 // static
526 scoped_ptr<MetadataDatabase> MetadataDatabase::Create(
527 const base::FilePath& database_path,
528 leveldb::Env* env_override,
529 SyncStatusCode* status_out) {
530 bool enable_on_disk_index =
531 !base::CommandLine::ForCurrentProcess()->HasSwitch(
532 kDisableMetadataDatabaseOnDisk);
533 return CreateInternal(database_path, env_override, enable_on_disk_index,
534 status_out);
537 // static
538 scoped_ptr<MetadataDatabase> MetadataDatabase::CreateInternal(
539 const base::FilePath& database_path,
540 leveldb::Env* env_override,
541 bool enable_on_disk_index,
542 SyncStatusCode* status_out) {
543 scoped_ptr<MetadataDatabase> metadata_database(
544 new MetadataDatabase(database_path,
545 enable_on_disk_index,
546 env_override));
548 SyncStatusCode status = metadata_database->Initialize();
549 if (status == SYNC_DATABASE_ERROR_FAILED) {
550 // Delete the previous instance to avoid creating a LevelDB instance for
551 // the same path.
552 metadata_database.reset();
554 metadata_database.reset(
555 new MetadataDatabase(database_path,
556 enable_on_disk_index,
557 env_override));
558 status = metadata_database->Initialize();
561 if (status != SYNC_STATUS_OK)
562 metadata_database.reset();
564 *status_out = status;
565 return metadata_database.Pass();
568 // static
569 SyncStatusCode MetadataDatabase::CreateForTesting(
570 scoped_ptr<LevelDBWrapper> db,
571 bool enable_on_disk_index,
572 scoped_ptr<MetadataDatabase>* metadata_database_out) {
573 scoped_ptr<MetadataDatabase> metadata_database(
574 new MetadataDatabase(base::FilePath(),
575 enable_on_disk_index,
576 nullptr));
577 metadata_database->db_ = db.Pass();
578 SyncStatusCode status = metadata_database->Initialize();
579 if (status == SYNC_STATUS_OK)
580 *metadata_database_out = metadata_database.Pass();
581 return status;
584 MetadataDatabase::~MetadataDatabase() {
587 // static
588 void MetadataDatabase::ClearDatabase(
589 scoped_ptr<MetadataDatabase> metadata_database) {
590 DCHECK(metadata_database);
591 base::FilePath database_path = metadata_database->database_path_;
592 DCHECK(!database_path.empty());
593 metadata_database.reset();
595 base::DeleteFile(database_path, true /* recursive */);
598 int64 MetadataDatabase::GetLargestFetchedChangeID() const {
599 return index_->GetLargestChangeID();
602 int64 MetadataDatabase::GetSyncRootTrackerID() const {
603 return index_->GetSyncRootTrackerID();
606 int64 MetadataDatabase::GetLargestKnownChangeID() const {
607 DCHECK_LE(GetLargestFetchedChangeID(), largest_known_change_id_);
608 return largest_known_change_id_;
611 void MetadataDatabase::UpdateLargestKnownChangeID(int64 change_id) {
612 if (largest_known_change_id_ < change_id)
613 largest_known_change_id_ = change_id;
616 bool MetadataDatabase::HasSyncRoot() const {
617 return index_->GetSyncRootTrackerID() != kInvalidTrackerID;
620 SyncStatusCode MetadataDatabase::PopulateInitialData(
621 int64 largest_change_id,
622 const google_apis::FileResource& sync_root_folder,
623 const ScopedVector<google_apis::FileResource>& app_root_folders) {
624 index_->SetLargestChangeID(largest_change_id);
625 UpdateLargestKnownChangeID(largest_change_id);
627 AttachSyncRoot(sync_root_folder);
628 for (size_t i = 0; i < app_root_folders.size(); ++i)
629 AttachInitialAppRoot(*app_root_folders[i]);
631 return WriteToDatabase();
634 bool MetadataDatabase::IsAppEnabled(const std::string& app_id) const {
635 int64 tracker_id = index_->GetAppRootTracker(app_id);
636 if (tracker_id == kInvalidTrackerID)
637 return false;
639 FileTracker tracker;
640 if (!index_->GetFileTracker(tracker_id, &tracker))
641 return false;
642 return tracker.tracker_kind() == TRACKER_KIND_APP_ROOT;
645 SyncStatusCode MetadataDatabase::RegisterApp(const std::string& app_id,
646 const std::string& folder_id) {
647 if (index_->GetAppRootTracker(app_id)) {
648 // The app-root is already registered.
649 return SYNC_STATUS_OK;
652 TrackerIDSet trackers = index_->GetFileTrackerIDsByFileID(folder_id);
653 if (trackers.empty()) {
654 return SYNC_DATABASE_ERROR_NOT_FOUND;
657 if (trackers.has_active()) {
658 // The folder is tracked by another tracker.
659 util::Log(logging::LOG_WARNING, FROM_HERE,
660 "Failed to register App for %s", app_id.c_str());
661 return SYNC_STATUS_HAS_CONFLICT;
664 int64 sync_root_tracker_id = index_->GetSyncRootTrackerID();
665 if (!sync_root_tracker_id) {
666 util::Log(logging::LOG_WARNING, FROM_HERE,
667 "Sync-root needs to be set up before registering app-root");
668 return SYNC_DATABASE_ERROR_NOT_FOUND;
671 scoped_ptr<FileTracker> tracker(new FileTracker);
672 if (!FilterFileTrackersByParent(index_.get(), trackers,
673 sync_root_tracker_id, tracker.get())) {
674 return SYNC_DATABASE_ERROR_NOT_FOUND;
677 tracker->set_app_id(app_id);
678 tracker->set_tracker_kind(TRACKER_KIND_APP_ROOT);
679 tracker->set_active(true);
680 tracker->set_needs_folder_listing(true);
681 tracker->set_dirty(true);
683 index_->StoreFileTracker(tracker.Pass());
684 return WriteToDatabase();
687 SyncStatusCode MetadataDatabase::DisableApp(const std::string& app_id) {
688 int64 tracker_id = index_->GetAppRootTracker(app_id);
689 scoped_ptr<FileTracker> tracker(new FileTracker);
690 if (!index_->GetFileTracker(tracker_id, tracker.get())) {
691 return SYNC_DATABASE_ERROR_NOT_FOUND;
694 if (tracker->tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT) {
695 return SYNC_STATUS_OK;
698 DCHECK_EQ(TRACKER_KIND_APP_ROOT, tracker->tracker_kind());
699 DCHECK(tracker->active());
701 // Keep the app-root tracker active (but change the tracker_kind) so that
702 // other conflicting trackers won't become active.
703 tracker->set_tracker_kind(TRACKER_KIND_DISABLED_APP_ROOT);
705 index_->StoreFileTracker(tracker.Pass());
706 return WriteToDatabase();
709 SyncStatusCode MetadataDatabase::EnableApp(const std::string& app_id) {
710 int64 tracker_id = index_->GetAppRootTracker(app_id);
711 scoped_ptr<FileTracker> tracker(new FileTracker);
712 if (!index_->GetFileTracker(tracker_id, tracker.get())) {
713 return SYNC_DATABASE_ERROR_NOT_FOUND;
716 if (tracker->tracker_kind() == TRACKER_KIND_APP_ROOT) {
717 return SYNC_STATUS_OK;
720 DCHECK_EQ(TRACKER_KIND_DISABLED_APP_ROOT, tracker->tracker_kind());
721 DCHECK(tracker->active());
723 tracker->set_tracker_kind(TRACKER_KIND_APP_ROOT);
724 index_->StoreFileTracker(tracker.Pass());
726 MarkTrackersDirtyRecursively(tracker_id, index_.get());
727 return WriteToDatabase();
730 SyncStatusCode MetadataDatabase::UnregisterApp(const std::string& app_id) {
731 int64 tracker_id = index_->GetAppRootTracker(app_id);
732 scoped_ptr<FileTracker> tracker(new FileTracker);
733 if (!index_->GetFileTracker(tracker_id, tracker.get()) ||
734 tracker->tracker_kind() == TRACKER_KIND_REGULAR) {
735 return SYNC_STATUS_OK;
738 RemoveAllDescendantTrackers(tracker_id, index_.get());
740 tracker->clear_app_id();
741 tracker->set_tracker_kind(TRACKER_KIND_REGULAR);
742 tracker->set_active(false);
743 tracker->set_dirty(true);
745 index_->StoreFileTracker(tracker.Pass());
746 return WriteToDatabase();
749 bool MetadataDatabase::FindAppRootTracker(const std::string& app_id,
750 FileTracker* tracker_out) const {
751 int64 app_root_tracker_id = index_->GetAppRootTracker(app_id);
752 if (!app_root_tracker_id)
753 return false;
755 if (tracker_out &&
756 !index_->GetFileTracker(app_root_tracker_id, tracker_out)) {
757 NOTREACHED();
758 return false;
761 return true;
764 bool MetadataDatabase::FindFileByFileID(const std::string& file_id,
765 FileMetadata* metadata_out) const {
766 return index_->GetFileMetadata(file_id, metadata_out);
769 bool MetadataDatabase::FindTrackersByFileID(const std::string& file_id,
770 TrackerIDSet* trackers_out) const {
771 TrackerIDSet trackers = index_->GetFileTrackerIDsByFileID(file_id);
772 if (trackers.empty())
773 return false;
775 if (trackers_out)
776 std::swap(trackers, *trackers_out);
777 return true;
780 bool MetadataDatabase::FindTrackersByParentAndTitle(
781 int64 parent_tracker_id,
782 const std::string& title,
783 TrackerIDSet* trackers_out) const {
784 TrackerIDSet trackers =
785 index_->GetFileTrackerIDsByParentAndTitle(parent_tracker_id, title);
786 if (trackers.empty())
787 return false;
789 if (trackers_out)
790 std::swap(trackers, *trackers_out);
791 return true;
794 bool MetadataDatabase::FindTrackerByTrackerID(int64 tracker_id,
795 FileTracker* tracker_out) const {
796 return index_->GetFileTracker(tracker_id, tracker_out);
799 bool MetadataDatabase::BuildPathForTracker(int64 tracker_id,
800 base::FilePath* path) const {
801 FileTracker current;
802 if (!FindTrackerByTrackerID(tracker_id, &current) || !current.active())
803 return false;
805 std::vector<base::FilePath> components;
806 while (!IsAppRoot(current)) {
807 std::string title = GetTrackerTitle(current);
808 if (title.empty())
809 return false;
810 components.push_back(base::FilePath::FromUTF8Unsafe(title));
811 if (!FindTrackerByTrackerID(current.parent_tracker_id(), &current) ||
812 !current.active())
813 return false;
816 if (path)
817 *path = ReverseConcatPathComponents(components);
819 return true;
822 base::FilePath MetadataDatabase::BuildDisplayPathForTracker(
823 const FileTracker& tracker) const {
824 base::FilePath path;
825 if (tracker.active()) {
826 BuildPathForTracker(tracker.tracker_id(), &path);
827 return path;
829 BuildPathForTracker(tracker.parent_tracker_id(), &path);
830 if (tracker.has_synced_details()) {
831 path = path.Append(
832 base::FilePath::FromUTF8Unsafe(tracker.synced_details().title()));
833 } else {
834 path = path.Append(FILE_PATH_LITERAL("<unknown>"));
836 return path;
839 bool MetadataDatabase::FindNearestActiveAncestor(
840 const std::string& app_id,
841 const base::FilePath& full_path,
842 FileTracker* tracker_out,
843 base::FilePath* path_out) const {
844 DCHECK(tracker_out);
845 DCHECK(path_out);
847 if (full_path.IsAbsolute() ||
848 !FindAppRootTracker(app_id, tracker_out) ||
849 tracker_out->tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT) {
850 return false;
853 std::vector<base::FilePath::StringType> components;
854 full_path.GetComponents(&components);
855 path_out->clear();
857 for (size_t i = 0; i < components.size(); ++i) {
858 const std::string title = base::FilePath(components[i]).AsUTF8Unsafe();
859 TrackerIDSet trackers;
860 if (!FindTrackersByParentAndTitle(
861 tracker_out->tracker_id(), title, &trackers) ||
862 !trackers.has_active()) {
863 return true;
866 FileTracker tracker;
867 index_->GetFileTracker(trackers.active_tracker(), &tracker);
869 DCHECK(tracker.has_synced_details());
870 const FileDetails& details = tracker.synced_details();
871 if (details.file_kind() != FILE_KIND_FOLDER && i != components.size() - 1) {
872 // This non-last component indicates file. Give up search.
873 return true;
876 tracker_out->CopyFrom(tracker);
877 *path_out = path_out->Append(components[i]);
880 return true;
883 SyncStatusCode MetadataDatabase::UpdateByChangeList(
884 int64 largest_change_id,
885 ScopedVector<google_apis::ChangeResource> changes) {
886 DCHECK_LE(index_->GetLargestChangeID(), largest_change_id);
888 for (size_t i = 0; i < changes.size(); ++i) {
889 const google_apis::ChangeResource& change = *changes[i];
890 if (HasNewerFileMetadata(change.file_id(), change.change_id()))
891 continue;
893 scoped_ptr<FileMetadata> metadata(
894 CreateFileMetadataFromChangeResource(change));
895 UpdateByFileMetadata(FROM_HERE, metadata.Pass(),
896 UPDATE_TRACKER_FOR_UNSYNCED_FILE);
899 UpdateLargestKnownChangeID(largest_change_id);
900 index_->SetLargestChangeID(largest_change_id);
901 return WriteToDatabase();
904 SyncStatusCode MetadataDatabase::UpdateByFileResource(
905 const google_apis::FileResource& resource) {
906 scoped_ptr<FileMetadata> metadata(
907 CreateFileMetadataFromFileResource(
908 GetLargestKnownChangeID(), resource));
909 UpdateByFileMetadata(FROM_HERE, metadata.Pass(),
910 UPDATE_TRACKER_FOR_UNSYNCED_FILE);
911 return WriteToDatabase();
914 SyncStatusCode MetadataDatabase::UpdateByFileResourceList(
915 ScopedVector<google_apis::FileResource> resources) {
916 for (size_t i = 0; i < resources.size(); ++i) {
917 scoped_ptr<FileMetadata> metadata(
918 CreateFileMetadataFromFileResource(
919 GetLargestKnownChangeID(), *resources[i]));
920 UpdateByFileMetadata(FROM_HERE, metadata.Pass(),
921 UPDATE_TRACKER_FOR_UNSYNCED_FILE);
923 return WriteToDatabase();
926 SyncStatusCode MetadataDatabase::UpdateByDeletedRemoteFile(
927 const std::string& file_id) {
928 scoped_ptr<FileMetadata> metadata(
929 CreateDeletedFileMetadata(GetLargestKnownChangeID(), file_id));
930 UpdateByFileMetadata(FROM_HERE, metadata.Pass(),
931 UPDATE_TRACKER_FOR_UNSYNCED_FILE);
932 return WriteToDatabase();
935 SyncStatusCode MetadataDatabase::UpdateByDeletedRemoteFileList(
936 const FileIDList& file_ids) {
937 for (FileIDList::const_iterator itr = file_ids.begin();
938 itr != file_ids.end(); ++itr) {
939 scoped_ptr<FileMetadata> metadata(
940 CreateDeletedFileMetadata(GetLargestKnownChangeID(), *itr));
941 UpdateByFileMetadata(FROM_HERE, metadata.Pass(),
942 UPDATE_TRACKER_FOR_UNSYNCED_FILE);
944 return WriteToDatabase();
947 SyncStatusCode MetadataDatabase::ReplaceActiveTrackerWithNewResource(
948 int64 parent_tracker_id,
949 const google_apis::FileResource& resource) {
950 DCHECK(!index_->GetFileMetadata(resource.file_id(), nullptr));
951 DCHECK(index_->GetFileTracker(parent_tracker_id, nullptr));
953 UpdateByFileMetadata(
954 FROM_HERE,
955 CreateFileMetadataFromFileResource(GetLargestKnownChangeID(), resource),
956 UPDATE_TRACKER_FOR_SYNCED_FILE);
958 DCHECK(index_->GetFileMetadata(resource.file_id(), nullptr));
959 DCHECK(!index_->GetFileTrackerIDsByFileID(resource.file_id()).has_active());
961 TrackerIDSet same_path_trackers =
962 index_->GetFileTrackerIDsByParentAndTitle(
963 parent_tracker_id, resource.title());
964 FileTracker to_be_activated;
965 if (!FilterFileTrackersByFileID(index_.get(), same_path_trackers,
966 resource.file_id(), &to_be_activated)) {
967 NOTREACHED();
968 return SYNC_STATUS_FAILED;
971 int64 tracker_id = to_be_activated.tracker_id();
972 if (same_path_trackers.has_active()) {
973 DeactivateFileTracker(same_path_trackers.active_tracker(),
974 MARK_ITSELF_DIRTY |
975 MARK_SAME_FILE_ID_TRACKERS_DIRTY,
976 index_.get());
979 ActivateFileTracker(tracker_id, MARK_NOTHING_DIRTY, index_.get());
980 return WriteToDatabase();
983 SyncStatusCode MetadataDatabase::PopulateFolderByChildList(
984 const std::string& folder_id,
985 const FileIDList& child_file_ids) {
986 TrackerIDSet trackers = index_->GetFileTrackerIDsByFileID(folder_id);
987 if (!trackers.has_active()) {
988 // It's OK that there is no folder to populate its children.
989 // Inactive folders should ignore their contents updates.
990 return SYNC_STATUS_OK;
993 scoped_ptr<FileTracker> folder_tracker(new FileTracker);
994 if (!index_->GetFileTracker(trackers.active_tracker(),
995 folder_tracker.get())) {
996 NOTREACHED();
997 return SYNC_STATUS_FAILED;
1000 base::hash_set<std::string> children(child_file_ids.begin(),
1001 child_file_ids.end());
1003 std::vector<int64> known_children =
1004 index_->GetFileTrackerIDsByParent(folder_tracker->tracker_id());
1005 for (size_t i = 0; i < known_children.size(); ++i) {
1006 FileTracker tracker;
1007 if (!index_->GetFileTracker(known_children[i], &tracker)) {
1008 NOTREACHED();
1009 continue;
1011 children.erase(tracker.file_id());
1014 for (base::hash_set<std::string>::const_iterator itr = children.begin();
1015 itr != children.end(); ++itr)
1016 CreateTrackerForParentAndFileID(*folder_tracker, *itr);
1017 folder_tracker->set_needs_folder_listing(false);
1018 if (folder_tracker->dirty() && !ShouldKeepDirty(*folder_tracker))
1019 folder_tracker->set_dirty(false);
1020 index_->StoreFileTracker(folder_tracker.Pass());
1022 return WriteToDatabase();
1025 SyncStatusCode MetadataDatabase::UpdateTracker(
1026 int64 tracker_id,
1027 const FileDetails& updated_details) {
1028 FileTracker tracker;
1029 if (!index_->GetFileTracker(tracker_id, &tracker)) {
1030 return SYNC_DATABASE_ERROR_NOT_FOUND;
1033 // Check if the tracker is to be deleted.
1034 if (updated_details.missing()) {
1035 FileMetadata metadata;
1036 if (!index_->GetFileMetadata(tracker.file_id(), &metadata) ||
1037 metadata.details().missing()) {
1038 // Both the tracker and metadata have the missing flag, now it's safe to
1039 // delete the |tracker|.
1040 RemoveFileTracker(tracker_id,
1041 MARK_SAME_FILE_ID_TRACKERS_DIRTY |
1042 MARK_SAME_PATH_TRACKERS_DIRTY,
1043 index_.get());
1044 return WriteToDatabase();
1048 // Sync-root deletion should be handled separately by SyncEngine.
1049 DCHECK(tracker_id != GetSyncRootTrackerID() ||
1050 (tracker.has_synced_details() &&
1051 tracker.synced_details().title() == updated_details.title() &&
1052 !updated_details.missing()));
1054 if (tracker_id != GetSyncRootTrackerID()) {
1055 // Check if the tracker's parent is still in |parent_tracker_ids|.
1056 // If not, there should exist another tracker for the new parent, so delete
1057 // old tracker.
1058 FileTracker parent_tracker;
1059 index_->GetFileTracker(tracker.parent_tracker_id(), &parent_tracker);
1061 if (!HasFileAsParent(updated_details, parent_tracker.file_id())) {
1062 RemoveFileTracker(tracker.tracker_id(),
1063 MARK_SAME_PATH_TRACKERS_DIRTY,
1064 index_.get());
1065 return WriteToDatabase();
1068 if (tracker.has_synced_details()) {
1069 // Check if the tracker was retitled. If it was, there should exist
1070 // another tracker for the new title, so delete the tracker being updated.
1071 if (tracker.synced_details().title() != updated_details.title()) {
1072 RemoveFileTracker(tracker.tracker_id(),
1073 MARK_SAME_FILE_ID_TRACKERS_DIRTY,
1074 index_.get());
1075 return WriteToDatabase();
1077 } else {
1078 // Check if any other tracker exists has the same parent, title and
1079 // file_id to the updated tracker. If it exists, delete the tracker being
1080 // updated.
1081 if (FilterFileTrackersByFileID(
1082 index_.get(),
1083 index_->GetFileTrackerIDsByParentAndTitle(
1084 parent_tracker.tracker_id(),
1085 updated_details.title()),
1086 tracker.file_id(),
1087 nullptr)) {
1088 RemoveFileTracker(tracker.tracker_id(),
1089 MARK_NOTHING_DIRTY,
1090 index_.get());
1091 return WriteToDatabase();
1096 scoped_ptr<FileTracker> updated_tracker = CloneFileTracker(&tracker);
1097 *updated_tracker->mutable_synced_details() = updated_details;
1099 bool should_promote = false;
1101 // Activate the tracker if:
1102 // - There is no active tracker that tracks |tracker->file_id()|.
1103 // - There is no active tracker that has the same |parent| and |title|.
1104 if (!tracker.active() && CanActivateTracker(tracker)) {
1105 updated_tracker->set_active(true);
1106 updated_tracker->set_dirty(true);
1107 updated_tracker->set_needs_folder_listing(
1108 tracker.synced_details().file_kind() == FILE_KIND_FOLDER);
1109 should_promote = true;
1110 } else if (tracker.dirty() && !ShouldKeepDirty(tracker)) {
1111 updated_tracker->set_dirty(false);
1113 index_->StoreFileTracker(updated_tracker.Pass());
1114 if (should_promote)
1115 index_->PromoteDemotedDirtyTracker(tracker_id);
1117 return WriteToDatabase();
1120 MetadataDatabase::ActivationStatus MetadataDatabase::TryActivateTracker(
1121 int64 parent_tracker_id,
1122 const std::string& file_id,
1123 SyncStatusCode* status_out) {
1124 FileMetadata metadata;
1125 if (!index_->GetFileMetadata(file_id, &metadata)) {
1126 NOTREACHED();
1127 *status_out = SYNC_STATUS_FAILED;
1128 return ACTIVATION_PENDING;
1130 std::string title = metadata.details().title();
1131 DCHECK(!HasInvalidTitle(title));
1133 TrackerIDSet same_file_id_trackers =
1134 index_->GetFileTrackerIDsByFileID(file_id);
1135 scoped_ptr<FileTracker> tracker_to_be_activated(new FileTracker);
1136 FilterFileTrackersByParentAndTitle(
1137 index_.get(), same_file_id_trackers, parent_tracker_id,
1138 title, tracker_to_be_activated.get());
1140 // Check if there is another active tracker that tracks |file_id|.
1141 // This can happen when the tracked file has multiple parents.
1142 // In this case, report the failure to the caller.
1143 if (!tracker_to_be_activated->active() && same_file_id_trackers.has_active())
1144 return ACTIVATION_FAILED_ANOTHER_ACTIVE_TRACKER;
1146 if (!tracker_to_be_activated->active()) {
1147 // Check if there exists another active tracker that has the same path to
1148 // the tracker. If there is, deactivate it, assuming the caller already
1149 // overrides local file with newly added file,
1150 TrackerIDSet same_title_trackers =
1151 index_->GetFileTrackerIDsByParentAndTitle(parent_tracker_id, title);
1152 if (same_title_trackers.has_active()) {
1153 RemoveAllDescendantTrackers(same_title_trackers.active_tracker(),
1154 index_.get());
1156 scoped_ptr<FileTracker> tracker_to_be_deactivated(new FileTracker);
1157 if (index_->GetFileTracker(same_title_trackers.active_tracker(),
1158 tracker_to_be_deactivated.get())) {
1159 const std::string file_id = tracker_to_be_deactivated->file_id();
1160 tracker_to_be_deactivated->set_active(false);
1161 index_->StoreFileTracker(tracker_to_be_deactivated.Pass());
1163 MarkTrackersDirtyByFileID(file_id, index_.get());
1164 } else {
1165 NOTREACHED();
1170 tracker_to_be_activated->set_dirty(false);
1171 tracker_to_be_activated->set_active(true);
1172 *tracker_to_be_activated->mutable_synced_details() = metadata.details();
1173 if (tracker_to_be_activated->synced_details().file_kind() ==
1174 FILE_KIND_FOLDER) {
1175 tracker_to_be_activated->set_needs_folder_listing(true);
1177 tracker_to_be_activated->set_dirty(false);
1179 index_->StoreFileTracker(tracker_to_be_activated.Pass());
1181 *status_out = WriteToDatabase();
1182 return ACTIVATION_PENDING;
1185 void MetadataDatabase::DemoteTracker(int64 tracker_id) {
1186 index_->DemoteDirtyTracker(tracker_id);
1187 WriteToDatabase();
1190 bool MetadataDatabase::PromoteDemotedTrackers() {
1191 bool promoted = index_->PromoteDemotedDirtyTrackers();
1192 WriteToDatabase();
1193 return promoted;
1196 void MetadataDatabase::PromoteDemotedTracker(int64 tracker_id) {
1197 index_->PromoteDemotedDirtyTracker(tracker_id);
1198 WriteToDatabase();
1201 bool MetadataDatabase::GetDirtyTracker(
1202 FileTracker* tracker_out) const {
1203 int64 dirty_tracker_id = index_->PickDirtyTracker();
1204 if (!dirty_tracker_id)
1205 return false;
1207 if (tracker_out) {
1208 if (!index_->GetFileTracker(dirty_tracker_id, tracker_out)) {
1209 NOTREACHED();
1210 return false;
1213 return true;
1216 bool MetadataDatabase::HasDemotedDirtyTracker() const {
1217 return index_->HasDemotedDirtyTracker();
1220 bool MetadataDatabase::HasDirtyTracker() const {
1221 return index_->PickDirtyTracker() != kInvalidTrackerID;
1224 size_t MetadataDatabase::CountDirtyTracker() const {
1225 return index_->CountDirtyTracker();
1228 bool MetadataDatabase::GetMultiParentFileTrackers(std::string* file_id_out,
1229 TrackerIDSet* trackers_out) {
1230 DCHECK(file_id_out);
1231 DCHECK(trackers_out);
1233 std::string file_id = index_->PickMultiTrackerFileID();
1234 if (file_id.empty())
1235 return false;
1237 TrackerIDSet trackers = index_->GetFileTrackerIDsByFileID(file_id);
1238 if (trackers.size() <= 1) {
1239 NOTREACHED();
1240 return false;
1243 *file_id_out = file_id;
1244 std::swap(*trackers_out, trackers);
1245 return true;
1248 size_t MetadataDatabase::CountFileMetadata() const {
1249 return index_->CountFileMetadata();
1252 size_t MetadataDatabase::CountFileTracker() const {
1253 return index_->CountFileTracker();
1256 bool MetadataDatabase::GetConflictingTrackers(TrackerIDSet* trackers_out) {
1257 DCHECK(trackers_out);
1259 ParentIDAndTitle parent_and_title = index_->PickMultiBackingFilePath();
1260 if (parent_and_title.parent_id == kInvalidTrackerID)
1261 return false;
1263 TrackerIDSet trackers = index_->GetFileTrackerIDsByParentAndTitle(
1264 parent_and_title.parent_id, parent_and_title.title);
1265 if (trackers.size() <= 1) {
1266 NOTREACHED();
1267 return false;
1270 std::swap(*trackers_out, trackers);
1271 return true;
1274 void MetadataDatabase::GetRegisteredAppIDs(std::vector<std::string>* app_ids) {
1275 DCHECK(app_ids);
1276 *app_ids = index_->GetRegisteredAppIDs();
1279 SyncStatusCode MetadataDatabase::SweepDirtyTrackers(
1280 const std::vector<std::string>& file_ids) {
1281 std::set<int64> tracker_ids;
1282 for (size_t i = 0; i < file_ids.size(); ++i) {
1283 TrackerIDSet trackers_for_file_id =
1284 index_->GetFileTrackerIDsByFileID(file_ids[i]);
1285 for (TrackerIDSet::iterator itr = trackers_for_file_id.begin();
1286 itr != trackers_for_file_id.end(); ++itr)
1287 tracker_ids.insert(*itr);
1290 for (std::set<int64>::iterator itr = tracker_ids.begin();
1291 itr != tracker_ids.end(); ++itr) {
1292 scoped_ptr<FileTracker> tracker(new FileTracker);
1293 if (!index_->GetFileTracker(*itr, tracker.get()) ||
1294 !CanClearDirty(*tracker))
1295 continue;
1296 tracker->set_dirty(false);
1297 index_->StoreFileTracker(tracker.Pass());
1300 return WriteToDatabase();
1303 MetadataDatabase::MetadataDatabase(
1304 const base::FilePath& database_path,
1305 bool enable_on_disk_index,
1306 leveldb::Env* env_override)
1307 : database_path_(database_path),
1308 env_override_(env_override),
1309 enable_on_disk_index_(enable_on_disk_index),
1310 largest_known_change_id_(0),
1311 weak_ptr_factory_(this) {
1314 SyncStatusCode MetadataDatabase::Initialize() {
1315 SyncStatusCode status = SYNC_STATUS_UNKNOWN;
1316 bool created = false;
1317 // Open database unless |db_| is overridden for testing.
1318 if (!db_) {
1319 status = OpenDatabase(database_path_, env_override_, &db_, &created);
1320 if (status != SYNC_STATUS_OK)
1321 return status;
1324 if (!created) {
1325 status = MigrateDatabaseIfNeeded(db_.get());
1326 if (status != SYNC_STATUS_OK)
1327 return status;
1330 if (enable_on_disk_index_) {
1331 index_ = MetadataDatabaseIndexOnDisk::Create(db_.get());
1332 } else {
1333 index_ = MetadataDatabaseIndex::Create(db_.get());
1335 if (!index_) {
1336 // Delete all entries in |db_| to reset it.
1337 // TODO(peria): Make LevelDBWrapper::DestroyDB() to avoid a full scan.
1338 scoped_ptr<LevelDBWrapper::Iterator> itr = db_->NewIterator();
1339 for (itr->SeekToFirst(); itr->Valid();)
1340 itr->Delete();
1341 db_->Commit();
1343 return SYNC_DATABASE_ERROR_FAILED;
1346 status = LevelDBStatusToSyncStatusCode(db_->Commit());
1347 if (status != SYNC_STATUS_OK)
1348 return status;
1350 UpdateLargestKnownChangeID(index_->GetLargestChangeID());
1352 return status;
1355 void MetadataDatabase::CreateTrackerForParentAndFileID(
1356 const FileTracker& parent_tracker,
1357 const std::string& file_id) {
1358 CreateTrackerInternal(parent_tracker, file_id, nullptr,
1359 UPDATE_TRACKER_FOR_UNSYNCED_FILE);
1362 void MetadataDatabase::CreateTrackerForParentAndFileMetadata(
1363 const FileTracker& parent_tracker,
1364 const FileMetadata& file_metadata,
1365 UpdateOption option) {
1366 DCHECK(file_metadata.has_details());
1367 CreateTrackerInternal(parent_tracker,
1368 file_metadata.file_id(),
1369 &file_metadata.details(),
1370 option);
1373 void MetadataDatabase::CreateTrackerInternal(const FileTracker& parent_tracker,
1374 const std::string& file_id,
1375 const FileDetails* details,
1376 UpdateOption option) {
1377 int64 tracker_id = IncrementTrackerID();
1378 scoped_ptr<FileTracker> tracker(new FileTracker);
1379 tracker->set_tracker_id(tracker_id);
1380 tracker->set_parent_tracker_id(parent_tracker.tracker_id());
1381 tracker->set_file_id(file_id);
1382 tracker->set_app_id(parent_tracker.app_id());
1383 tracker->set_tracker_kind(TRACKER_KIND_REGULAR);
1384 tracker->set_dirty(true);
1385 tracker->set_active(false);
1386 tracker->set_needs_folder_listing(false);
1387 if (details) {
1388 *tracker->mutable_synced_details() = *details;
1389 if (option == UPDATE_TRACKER_FOR_UNSYNCED_FILE) {
1390 tracker->mutable_synced_details()->set_missing(true);
1391 tracker->mutable_synced_details()->clear_md5();
1394 index_->StoreFileTracker(tracker.Pass());
1397 void MetadataDatabase::MaybeAddTrackersForNewFile(
1398 const FileMetadata& metadata,
1399 UpdateOption option) {
1400 std::set<int64> parents_to_exclude;
1401 TrackerIDSet existing_trackers =
1402 index_->GetFileTrackerIDsByFileID(metadata.file_id());
1403 for (TrackerIDSet::const_iterator itr = existing_trackers.begin();
1404 itr != existing_trackers.end(); ++itr) {
1405 FileTracker tracker;
1406 if (!index_->GetFileTracker(*itr, &tracker)) {
1407 NOTREACHED();
1408 continue;
1411 int64 parent_tracker_id = tracker.parent_tracker_id();
1412 if (!parent_tracker_id)
1413 continue;
1415 // Exclude |parent_tracker_id| if it already has a tracker that has
1416 // unknown title or has the same title with |file|.
1417 if (!tracker.has_synced_details() ||
1418 tracker.synced_details().title() == metadata.details().title()) {
1419 parents_to_exclude.insert(parent_tracker_id);
1423 for (int i = 0; i < metadata.details().parent_folder_ids_size(); ++i) {
1424 std::string parent_folder_id = metadata.details().parent_folder_ids(i);
1425 TrackerIDSet parent_trackers =
1426 index_->GetFileTrackerIDsByFileID(parent_folder_id);
1427 for (TrackerIDSet::const_iterator itr = parent_trackers.begin();
1428 itr != parent_trackers.end(); ++itr) {
1429 FileTracker parent_tracker;
1430 index_->GetFileTracker(*itr, &parent_tracker);
1431 if (!parent_tracker.active())
1432 continue;
1434 if (ContainsKey(parents_to_exclude, parent_tracker.tracker_id()))
1435 continue;
1437 CreateTrackerForParentAndFileMetadata(
1438 parent_tracker, metadata, option);
1443 int64 MetadataDatabase::IncrementTrackerID() {
1444 int64 tracker_id = index_->GetNextTrackerID();
1445 index_->SetNextTrackerID(tracker_id + 1);
1446 DCHECK_GT(tracker_id, 0);
1447 return tracker_id;
1450 bool MetadataDatabase::CanActivateTracker(const FileTracker& tracker) {
1451 DCHECK(!tracker.active());
1452 DCHECK_NE(index_->GetSyncRootTrackerID(), tracker.tracker_id());
1454 if (HasActiveTrackerForFileID(tracker.file_id()))
1455 return false;
1457 if (tracker.app_id().empty() &&
1458 tracker.tracker_id() != GetSyncRootTrackerID()) {
1459 return false;
1462 if (!tracker.has_synced_details())
1463 return false;
1464 if (tracker.synced_details().file_kind() == FILE_KIND_UNSUPPORTED)
1465 return false;
1466 if (HasInvalidTitle(tracker.synced_details().title()))
1467 return false;
1468 DCHECK(tracker.parent_tracker_id());
1470 return !HasActiveTrackerForPath(tracker.parent_tracker_id(),
1471 tracker.synced_details().title());
1474 bool MetadataDatabase::ShouldKeepDirty(const FileTracker& tracker) const {
1475 if (HasDisabledAppRoot(tracker))
1476 return false;
1478 DCHECK(tracker.dirty());
1479 if (!tracker.has_synced_details())
1480 return true;
1482 FileMetadata metadata;
1483 if (!index_->GetFileMetadata(tracker.file_id(), &metadata))
1484 return true;
1485 DCHECK(metadata.has_details());
1487 const FileDetails& local_details = tracker.synced_details();
1488 const FileDetails& remote_details = metadata.details();
1490 if (tracker.active()) {
1491 if (tracker.needs_folder_listing())
1492 return true;
1493 if (local_details.md5() != remote_details.md5())
1494 return true;
1495 if (local_details.missing() != remote_details.missing())
1496 return true;
1499 if (local_details.title() != remote_details.title())
1500 return true;
1502 return false;
1505 bool MetadataDatabase::HasDisabledAppRoot(const FileTracker& tracker) const {
1506 int64 app_root_tracker_id = index_->GetAppRootTracker(tracker.app_id());
1507 if (app_root_tracker_id == kInvalidTrackerID)
1508 return false;
1510 FileTracker app_root_tracker;
1511 if (!index_->GetFileTracker(app_root_tracker_id, &app_root_tracker)) {
1512 NOTREACHED();
1513 return false;
1515 return app_root_tracker.tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT;
1518 bool MetadataDatabase::HasActiveTrackerForFileID(
1519 const std::string& file_id) const {
1520 return index_->GetFileTrackerIDsByFileID(file_id).has_active();
1523 bool MetadataDatabase::HasActiveTrackerForPath(int64 parent_tracker_id,
1524 const std::string& title) const {
1525 return index_->GetFileTrackerIDsByParentAndTitle(parent_tracker_id, title)
1526 .has_active();
1529 void MetadataDatabase::RemoveUnneededTrackersForMissingFile(
1530 const std::string& file_id) {
1531 TrackerIDSet trackers = index_->GetFileTrackerIDsByFileID(file_id);
1532 for (TrackerIDSet::const_iterator itr = trackers.begin();
1533 itr != trackers.end(); ++itr) {
1534 FileTracker tracker;
1535 if (!index_->GetFileTracker(*itr, &tracker)) {
1536 NOTREACHED();
1537 continue;
1540 if (!tracker.has_synced_details() || tracker.synced_details().missing()) {
1541 RemoveFileTracker(*itr, MARK_NOTHING_DIRTY, index_.get());
1546 void MetadataDatabase::UpdateByFileMetadata(
1547 const tracked_objects::Location& from_where,
1548 scoped_ptr<FileMetadata> metadata,
1549 UpdateOption option) {
1550 DCHECK(metadata);
1551 DCHECK(metadata->has_details());
1553 DVLOG(1) << from_where.function_name() << ": "
1554 << metadata->file_id() << " ("
1555 << metadata->details().title() << ")"
1556 << (metadata->details().missing() ? " deleted" : "");
1558 std::string file_id = metadata->file_id();
1559 if (metadata->details().missing())
1560 RemoveUnneededTrackersForMissingFile(file_id);
1561 else
1562 MaybeAddTrackersForNewFile(*metadata, option);
1564 TrackerIDSet trackers = index_->GetFileTrackerIDsByFileID(file_id);
1565 if (!trackers.empty()) {
1566 index_->StoreFileMetadata(metadata.Pass());
1568 if (option != UPDATE_TRACKER_FOR_SYNCED_FILE)
1569 MarkTrackerSetDirty(trackers, index_.get());
1574 SyncStatusCode MetadataDatabase::WriteToDatabase() {
1575 return LevelDBStatusToSyncStatusCode(db_->Commit());
1578 scoped_ptr<base::ListValue> MetadataDatabase::DumpFiles(
1579 const std::string& app_id) {
1580 scoped_ptr<base::ListValue> files(new base::ListValue);
1582 FileTracker app_root_tracker;
1583 if (!FindAppRootTracker(app_id, &app_root_tracker))
1584 return files.Pass();
1586 std::vector<int64> stack;
1587 AppendContents(
1588 index_->GetFileTrackerIDsByParent(app_root_tracker.tracker_id()), &stack);
1589 while (!stack.empty()) {
1590 int64 tracker_id = stack.back();
1591 stack.pop_back();
1592 AppendContents(index_->GetFileTrackerIDsByParent(tracker_id), &stack);
1594 FileTracker tracker;
1595 if (!index_->GetFileTracker(tracker_id, &tracker)) {
1596 NOTREACHED();
1597 continue;
1599 base::DictionaryValue* file = new base::DictionaryValue;
1601 base::FilePath path = BuildDisplayPathForTracker(tracker);
1602 file->SetString("path", path.AsUTF8Unsafe());
1603 if (tracker.has_synced_details()) {
1604 file->SetString("title", tracker.synced_details().title());
1605 file->SetString("type",
1606 FileKindToString(tracker.synced_details().file_kind()));
1609 base::DictionaryValue* details = new base::DictionaryValue;
1610 details->SetString("file_id", tracker.file_id());
1611 if (tracker.has_synced_details() &&
1612 tracker.synced_details().file_kind() == FILE_KIND_FILE)
1613 details->SetString("md5", tracker.synced_details().md5());
1614 details->SetString("active", tracker.active() ? "true" : "false");
1615 details->SetString("dirty", tracker.dirty() ? "true" : "false");
1617 file->Set("details", details);
1619 files->Append(file);
1622 return files.Pass();
1625 scoped_ptr<base::ListValue> MetadataDatabase::DumpDatabase() {
1626 scoped_ptr<base::ListValue> list(new base::ListValue);
1627 list->Append(DumpTrackers().release());
1628 list->Append(DumpMetadata().release());
1629 return list.Pass();
1632 bool MetadataDatabase::HasNewerFileMetadata(const std::string& file_id,
1633 int64 change_id) {
1634 FileMetadata metadata;
1635 if (!index_->GetFileMetadata(file_id, &metadata))
1636 return false;
1637 DCHECK(metadata.has_details());
1638 return metadata.details().change_id() >= change_id;
1641 scoped_ptr<base::ListValue> MetadataDatabase::DumpTrackers() {
1642 scoped_ptr<base::ListValue> trackers(new base::ListValue);
1644 // Append the first element for metadata.
1645 base::DictionaryValue* metadata = new base::DictionaryValue;
1646 const char *trackerKeys[] = {
1647 "tracker_id", "path", "file_id", "tracker_kind", "app_id",
1648 "active", "dirty", "folder_listing", "demoted",
1649 "title", "kind", "md5", "etag", "missing", "change_id",
1651 std::vector<std::string> key_strings(
1652 trackerKeys, trackerKeys + arraysize(trackerKeys));
1653 base::ListValue* keys = new base::ListValue;
1654 keys->AppendStrings(key_strings);
1655 metadata->SetString("title", "Trackers");
1656 metadata->Set("keys", keys);
1657 trackers->Append(metadata);
1659 // Append tracker data.
1660 std::vector<int64> tracker_ids(index_->GetAllTrackerIDs());
1661 for (std::vector<int64>::const_iterator itr = tracker_ids.begin();
1662 itr != tracker_ids.end(); ++itr) {
1663 const int64 tracker_id = *itr;
1664 FileTracker tracker;
1665 if (!index_->GetFileTracker(tracker_id, &tracker)) {
1666 NOTREACHED();
1667 continue;
1670 base::DictionaryValue* dict = new base::DictionaryValue;
1671 base::FilePath path = BuildDisplayPathForTracker(tracker);
1672 dict->SetString("tracker_id", base::Int64ToString(tracker_id));
1673 dict->SetString("path", path.AsUTF8Unsafe());
1674 dict->SetString("file_id", tracker.file_id());
1675 TrackerKind tracker_kind = tracker.tracker_kind();
1676 dict->SetString(
1677 "tracker_kind",
1678 tracker_kind == TRACKER_KIND_APP_ROOT ? "AppRoot" :
1679 tracker_kind == TRACKER_KIND_DISABLED_APP_ROOT ? "Disabled App" :
1680 tracker.tracker_id() == GetSyncRootTrackerID() ? "SyncRoot" :
1681 "Regular");
1682 dict->SetString("app_id", tracker.app_id());
1683 dict->SetString("active", tracker.active() ? "true" : "false");
1684 dict->SetString("dirty", tracker.dirty() ? "true" : "false");
1685 dict->SetString("folder_listing",
1686 tracker.needs_folder_listing() ? "needed" : "no");
1688 bool is_demoted = index_->IsDemotedDirtyTracker(tracker.tracker_id());
1689 dict->SetString("demoted", is_demoted ? "true" : "false");
1690 if (tracker.has_synced_details()) {
1691 const FileDetails& details = tracker.synced_details();
1692 dict->SetString("title", details.title());
1693 dict->SetString("kind", FileKindToString(details.file_kind()));
1694 dict->SetString("md5", details.md5());
1695 dict->SetString("etag", details.etag());
1696 dict->SetString("missing", details.missing() ? "true" : "false");
1697 dict->SetString("change_id", base::Int64ToString(details.change_id()));
1699 trackers->Append(dict);
1701 return trackers.Pass();
1704 scoped_ptr<base::ListValue> MetadataDatabase::DumpMetadata() {
1705 scoped_ptr<base::ListValue> files(new base::ListValue);
1707 // Append the first element for metadata.
1708 base::DictionaryValue* metadata = new base::DictionaryValue;
1709 const char *fileKeys[] = {
1710 "file_id", "title", "type", "md5", "etag", "missing",
1711 "change_id", "parents"
1713 std::vector<std::string> key_strings(
1714 fileKeys, fileKeys + arraysize(fileKeys));
1715 base::ListValue* keys = new base::ListValue;
1716 keys->AppendStrings(key_strings);
1717 metadata->SetString("title", "Metadata");
1718 metadata->Set("keys", keys);
1719 files->Append(metadata);
1721 // Append metadata data.
1722 std::vector<std::string> metadata_ids(index_->GetAllMetadataIDs());
1723 for (std::vector<std::string>::const_iterator itr = metadata_ids.begin();
1724 itr != metadata_ids.end(); ++itr) {
1725 const std::string& file_id = *itr;
1726 FileMetadata file;
1727 if (!index_->GetFileMetadata(file_id, &file)) {
1728 NOTREACHED();
1729 continue;
1732 base::DictionaryValue* dict = new base::DictionaryValue;
1733 dict->SetString("file_id", file_id);
1734 if (file.has_details()) {
1735 const FileDetails& details = file.details();
1736 dict->SetString("title", details.title());
1737 dict->SetString("type", FileKindToString(details.file_kind()));
1738 dict->SetString("md5", details.md5());
1739 dict->SetString("etag", details.etag());
1740 dict->SetString("missing", details.missing() ? "true" : "false");
1741 dict->SetString("change_id", base::Int64ToString(details.change_id()));
1743 std::vector<std::string> parents;
1744 for (int i = 0; i < details.parent_folder_ids_size(); ++i)
1745 parents.push_back(details.parent_folder_ids(i));
1746 dict->SetString("parents", base::JoinString(parents, ","));
1748 files->Append(dict);
1750 return files.Pass();
1753 void MetadataDatabase::AttachSyncRoot(
1754 const google_apis::FileResource& sync_root_folder) {
1755 scoped_ptr<FileMetadata> sync_root_metadata =
1756 CreateFileMetadataFromFileResource(
1757 GetLargestKnownChangeID(), sync_root_folder);
1758 scoped_ptr<FileTracker> sync_root_tracker =
1759 CreateSyncRootTracker(IncrementTrackerID(), *sync_root_metadata);
1761 index_->SetSyncRootTrackerID(sync_root_tracker->tracker_id());
1762 index_->StoreFileMetadata(sync_root_metadata.Pass());
1763 index_->StoreFileTracker(sync_root_tracker.Pass());
1766 void MetadataDatabase::AttachInitialAppRoot(
1767 const google_apis::FileResource& app_root_folder) {
1768 scoped_ptr<FileMetadata> app_root_metadata =
1769 CreateFileMetadataFromFileResource(
1770 GetLargestKnownChangeID(), app_root_folder);
1771 scoped_ptr<FileTracker> app_root_tracker =
1772 CreateInitialAppRootTracker(IncrementTrackerID(),
1773 GetSyncRootTrackerID(),
1774 *app_root_metadata);
1776 index_->StoreFileMetadata(app_root_metadata.Pass());
1777 index_->StoreFileTracker(app_root_tracker.Pass());
1780 bool MetadataDatabase::CanClearDirty(const FileTracker& tracker) {
1781 FileMetadata metadata;
1782 if (!index_->GetFileMetadata(tracker.file_id(), &metadata) ||
1783 !tracker.active() || !tracker.dirty() ||
1784 !tracker.has_synced_details() ||
1785 tracker.needs_folder_listing())
1786 return false;
1788 const FileDetails& remote_details = metadata.details();
1789 const FileDetails& synced_details = tracker.synced_details();
1790 if (remote_details.title() != synced_details.title() ||
1791 remote_details.md5() != synced_details.md5() ||
1792 remote_details.missing() != synced_details.missing())
1793 return false;
1795 std::set<std::string> parents;
1796 for (int i = 0; i < remote_details.parent_folder_ids_size(); ++i)
1797 parents.insert(remote_details.parent_folder_ids(i));
1799 for (int i = 0; i < synced_details.parent_folder_ids_size(); ++i)
1800 if (parents.erase(synced_details.parent_folder_ids(i)) != 1)
1801 return false;
1803 if (!parents.empty())
1804 return false;
1806 return true;
1809 } // namespace drive_backend
1810 } // namespace sync_file_system