Check USB device path access when prompting users to select a device.
[chromium-blink-merge.git] / chrome / browser / sync_file_system / drive_backend / metadata_database.cc
blob5d31cf498069020b5428e8968df836fb8a9b5ba1
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/drive/drive_api_util.h"
25 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h"
26 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_util.h"
27 #include "chrome/browser/sync_file_system/drive_backend/leveldb_wrapper.h"
28 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.pb.h"
29 #include "chrome/browser/sync_file_system/drive_backend/metadata_database_index.h"
30 #include "chrome/browser/sync_file_system/drive_backend/metadata_database_index_interface.h"
31 #include "chrome/browser/sync_file_system/drive_backend/metadata_database_index_on_disk.h"
32 #include "chrome/browser/sync_file_system/drive_backend/metadata_db_migration_util.h"
33 #include "chrome/browser/sync_file_system/logger.h"
34 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
35 #include "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.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue;
219 if (env_override)
220 options.env = env_override;
221 leveldb::DB* db = nullptr;
222 leveldb::Status db_status =
223 leveldb::DB::Open(options, path.AsUTF8Unsafe(), &db);
224 SyncStatusCode status = LevelDBStatusToSyncStatusCode(db_status);
225 if (status != SYNC_STATUS_OK) {
226 delete db;
227 return status;
230 db_out->reset(new LevelDBWrapper(make_scoped_ptr(db)));
231 *created = IsDatabaseEmpty(db_out->get());
232 return status;
235 SyncStatusCode MigrateDatabaseIfNeeded(LevelDBWrapper* db) {
236 // See metadata_database_index.cc for the database schema.
237 base::ThreadRestrictions::AssertIOAllowed();
238 DCHECK(db);
239 std::string value;
240 leveldb::Status status = db->Get(kDatabaseVersionKey, &value);
241 int64 version = 0;
242 if (status.ok()) {
243 if (!base::StringToInt64(value, &version))
244 return SYNC_DATABASE_ERROR_FAILED;
245 } else {
246 if (!status.IsNotFound())
247 return SYNC_DATABASE_ERROR_FAILED;
250 switch (version) {
251 case 0:
252 case 1:
253 case 2:
254 // Drop all data in old database and refetch them from the remote service.
255 NOTREACHED();
256 return SYNC_DATABASE_ERROR_FAILED;
257 case 3:
258 DCHECK_EQ(3, kCurrentDatabaseVersion);
259 // If MetadataDatabaseOnDisk is enabled, migration will be done in
260 // MetadataDatabaseOnDisk::Create().
261 // TODO(peria): Move the migration code (from v3 to v4) here.
262 return SYNC_STATUS_OK;
263 case 4:
264 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
265 kDisableMetadataDatabaseOnDisk)) {
266 MigrateDatabaseFromV4ToV3(db->GetLevelDB());
268 return SYNC_STATUS_OK;
269 default:
270 return SYNC_DATABASE_ERROR_FAILED;
274 bool HasInvalidTitle(const std::string& title) {
275 return title.empty() ||
276 title.find('/') != std::string::npos ||
277 title.find('\\') != std::string::npos;
280 void MarkTrackerSetDirty(const TrackerIDSet& trackers,
281 MetadataDatabaseIndexInterface* index) {
282 for (TrackerIDSet::const_iterator itr = trackers.begin();
283 itr != trackers.end(); ++itr) {
284 scoped_ptr<FileTracker> tracker(new FileTracker);
285 index->GetFileTracker(*itr, tracker.get());
286 if (tracker->dirty())
287 continue;
288 tracker->set_dirty(true);
289 index->StoreFileTracker(tracker.Pass());
293 void MarkTrackersDirtyByPath(int64 parent_tracker_id,
294 const std::string& title,
295 MetadataDatabaseIndexInterface* index) {
296 if (parent_tracker_id == kInvalidTrackerID || title.empty())
297 return;
298 MarkTrackerSetDirty(
299 index->GetFileTrackerIDsByParentAndTitle(parent_tracker_id, title),
300 index);
303 void MarkTrackersDirtyByFileID(const std::string& file_id,
304 MetadataDatabaseIndexInterface* index) {
305 MarkTrackerSetDirty(index->GetFileTrackerIDsByFileID(file_id), index);
308 void MarkTrackersDirtyRecursively(int64 root_tracker_id,
309 MetadataDatabaseIndexInterface* index) {
310 std::vector<int64> stack;
311 stack.push_back(root_tracker_id);
312 while (!stack.empty()) {
313 int64 tracker_id = stack.back();
314 stack.pop_back();
315 AppendContents(index->GetFileTrackerIDsByParent(tracker_id), &stack);
317 scoped_ptr<FileTracker> tracker(new FileTracker);
318 index->GetFileTracker(tracker_id, tracker.get());
319 tracker->set_dirty(true);
321 index->StoreFileTracker(tracker.Pass());
325 void RemoveAllDescendantTrackers(int64 root_tracker_id,
326 MetadataDatabaseIndexInterface* index) {
327 std::vector<int64> pending_trackers;
328 AppendContents(index->GetFileTrackerIDsByParent(root_tracker_id),
329 &pending_trackers);
331 std::vector<int64> to_be_removed;
333 // List trackers to remove.
334 while (!pending_trackers.empty()) {
335 int64 tracker_id = pending_trackers.back();
336 pending_trackers.pop_back();
337 AppendContents(index->GetFileTrackerIDsByParent(tracker_id),
338 &pending_trackers);
339 to_be_removed.push_back(tracker_id);
342 // Remove trackers in the reversed order.
343 base::hash_set<std::string> affected_file_ids;
344 for (std::vector<int64>::reverse_iterator itr = to_be_removed.rbegin();
345 itr != to_be_removed.rend(); ++itr) {
346 FileTracker tracker;
347 index->GetFileTracker(*itr, &tracker);
348 affected_file_ids.insert(tracker.file_id());
349 index->RemoveFileTracker(*itr);
352 for (base::hash_set<std::string>::iterator itr = affected_file_ids.begin();
353 itr != affected_file_ids.end(); ++itr) {
354 TrackerIDSet trackers = index->GetFileTrackerIDsByFileID(*itr);
355 if (trackers.empty()) {
356 // Remove metadata that no longer has any tracker.
357 index->RemoveFileMetadata(*itr);
358 } else {
359 MarkTrackerSetDirty(trackers, index);
364 bool FilterFileTrackersByParent(
365 const MetadataDatabaseIndexInterface* index,
366 const TrackerIDSet& trackers,
367 int64 parent_tracker_id,
368 FileTracker* tracker_out) {
369 FileTracker tracker;
370 for (TrackerIDSet::const_iterator itr = trackers.begin();
371 itr != trackers.end(); ++itr) {
372 if (!index->GetFileTracker(*itr, &tracker)) {
373 NOTREACHED();
374 continue;
377 if (tracker.parent_tracker_id() == parent_tracker_id) {
378 if (tracker_out)
379 tracker_out->CopyFrom(tracker);
380 return true;
383 return false;
386 bool FilterFileTrackersByParentAndTitle(
387 const MetadataDatabaseIndexInterface* index,
388 const TrackerIDSet& trackers,
389 int64 parent_tracker_id,
390 const std::string& title,
391 FileTracker* result) {
392 bool found = false;
393 for (TrackerIDSet::const_iterator itr = trackers.begin();
394 itr != trackers.end(); ++itr) {
395 FileTracker tracker;
396 if (!index->GetFileTracker(*itr, &tracker)) {
397 NOTREACHED();
398 continue;
401 if (tracker.parent_tracker_id() != parent_tracker_id)
402 continue;
404 if (tracker.has_synced_details() &&
405 tracker.synced_details().title() != title)
406 continue;
408 // Prioritize trackers that has |synced_details| when |trackers| has
409 // multiple candidates.
410 if (!found || tracker.has_synced_details()) {
411 found = true;
412 if (result)
413 result->CopyFrom(tracker);
414 if (!result || result->has_synced_details())
415 return found;
419 return found;
422 bool FilterFileTrackersByFileID(
423 const MetadataDatabaseIndexInterface* index,
424 const TrackerIDSet& trackers,
425 const std::string& file_id,
426 FileTracker* tracker_out) {
427 FileTracker tracker;
428 for (TrackerIDSet::const_iterator itr = trackers.begin();
429 itr != trackers.end(); ++itr) {
430 if (!index->GetFileTracker(*itr, &tracker)) {
431 NOTREACHED();
432 continue;
435 if (tracker.file_id() == file_id) {
436 if (tracker_out)
437 tracker_out->CopyFrom(tracker);
438 return true;
441 return false;
444 enum DirtyingOption {
445 MARK_NOTHING_DIRTY = 0,
446 MARK_ITSELF_DIRTY = 1 << 0,
447 MARK_SAME_FILE_ID_TRACKERS_DIRTY = 1 << 1,
448 MARK_SAME_PATH_TRACKERS_DIRTY = 1 << 2,
451 void ActivateFileTracker(int64 tracker_id,
452 int dirtying_options,
453 MetadataDatabaseIndexInterface* index) {
454 DCHECK(dirtying_options == MARK_NOTHING_DIRTY ||
455 dirtying_options == MARK_ITSELF_DIRTY);
457 scoped_ptr<FileTracker> tracker(new FileTracker);
458 index->GetFileTracker(tracker_id, tracker.get());
459 tracker->set_active(true);
460 if (dirtying_options & MARK_ITSELF_DIRTY) {
461 tracker->set_dirty(true);
462 tracker->set_needs_folder_listing(
463 tracker->has_synced_details() &&
464 tracker->synced_details().file_kind() == FILE_KIND_FOLDER);
465 } else {
466 tracker->set_dirty(false);
467 tracker->set_needs_folder_listing(false);
470 index->StoreFileTracker(tracker.Pass());
473 void DeactivateFileTracker(int64 tracker_id,
474 int dirtying_options,
475 MetadataDatabaseIndexInterface* index) {
476 RemoveAllDescendantTrackers(tracker_id, index);
478 scoped_ptr<FileTracker> tracker(new FileTracker);
479 index->GetFileTracker(tracker_id, tracker.get());
481 if (dirtying_options & MARK_SAME_FILE_ID_TRACKERS_DIRTY)
482 MarkTrackersDirtyByFileID(tracker->file_id(), index);
483 if (dirtying_options & MARK_SAME_PATH_TRACKERS_DIRTY) {
484 MarkTrackersDirtyByPath(tracker->parent_tracker_id(),
485 GetTrackerTitle(*tracker), index);
488 tracker->set_dirty(dirtying_options & MARK_ITSELF_DIRTY);
489 tracker->set_active(false);
490 index->StoreFileTracker(tracker.Pass());
493 void RemoveFileTracker(int64 tracker_id,
494 int dirtying_options,
495 MetadataDatabaseIndexInterface* index) {
496 DCHECK(!(dirtying_options & MARK_ITSELF_DIRTY));
498 FileTracker tracker;
499 if (!index->GetFileTracker(tracker_id, &tracker))
500 return;
502 std::string file_id = tracker.file_id();
503 int64 parent_tracker_id = tracker.parent_tracker_id();
504 std::string title = GetTrackerTitle(tracker);
506 RemoveAllDescendantTrackers(tracker_id, index);
507 index->RemoveFileTracker(tracker_id);
509 if (dirtying_options & MARK_SAME_FILE_ID_TRACKERS_DIRTY)
510 MarkTrackersDirtyByFileID(file_id, index);
511 if (dirtying_options & MARK_SAME_PATH_TRACKERS_DIRTY)
512 MarkTrackersDirtyByPath(parent_tracker_id, title, index);
514 if (index->GetFileTrackerIDsByFileID(file_id).empty()) {
515 index->RemoveFileMetadata(file_id);
519 } // namespace
521 // static
522 scoped_ptr<MetadataDatabase> MetadataDatabase::Create(
523 const base::FilePath& database_path,
524 leveldb::Env* env_override,
525 SyncStatusCode* status_out) {
526 bool enable_on_disk_index =
527 !base::CommandLine::ForCurrentProcess()->HasSwitch(
528 kDisableMetadataDatabaseOnDisk);
529 return CreateInternal(database_path, env_override, enable_on_disk_index,
530 status_out);
533 // static
534 scoped_ptr<MetadataDatabase> MetadataDatabase::CreateInternal(
535 const base::FilePath& database_path,
536 leveldb::Env* env_override,
537 bool enable_on_disk_index,
538 SyncStatusCode* status_out) {
539 scoped_ptr<MetadataDatabase> metadata_database(
540 new MetadataDatabase(database_path,
541 enable_on_disk_index,
542 env_override));
544 SyncStatusCode status = metadata_database->Initialize();
545 if (status == SYNC_DATABASE_ERROR_FAILED) {
546 // Delete the previous instance to avoid creating a LevelDB instance for
547 // the same path.
548 metadata_database.reset();
550 metadata_database.reset(
551 new MetadataDatabase(database_path,
552 enable_on_disk_index,
553 env_override));
554 status = metadata_database->Initialize();
557 if (status != SYNC_STATUS_OK)
558 metadata_database.reset();
560 *status_out = status;
561 return metadata_database.Pass();
564 // static
565 SyncStatusCode MetadataDatabase::CreateForTesting(
566 scoped_ptr<LevelDBWrapper> db,
567 bool enable_on_disk_index,
568 scoped_ptr<MetadataDatabase>* metadata_database_out) {
569 scoped_ptr<MetadataDatabase> metadata_database(
570 new MetadataDatabase(base::FilePath(),
571 enable_on_disk_index,
572 nullptr));
573 metadata_database->db_ = db.Pass();
574 SyncStatusCode status = metadata_database->Initialize();
575 if (status == SYNC_STATUS_OK)
576 *metadata_database_out = metadata_database.Pass();
577 return status;
580 MetadataDatabase::~MetadataDatabase() {
583 // static
584 void MetadataDatabase::ClearDatabase(
585 scoped_ptr<MetadataDatabase> metadata_database) {
586 DCHECK(metadata_database);
587 base::FilePath database_path = metadata_database->database_path_;
588 DCHECK(!database_path.empty());
589 metadata_database.reset();
591 base::DeleteFile(database_path, true /* recursive */);
594 int64 MetadataDatabase::GetLargestFetchedChangeID() const {
595 return index_->GetLargestChangeID();
598 int64 MetadataDatabase::GetSyncRootTrackerID() const {
599 return index_->GetSyncRootTrackerID();
602 int64 MetadataDatabase::GetLargestKnownChangeID() const {
603 DCHECK_LE(GetLargestFetchedChangeID(), largest_known_change_id_);
604 return largest_known_change_id_;
607 void MetadataDatabase::UpdateLargestKnownChangeID(int64 change_id) {
608 if (largest_known_change_id_ < change_id)
609 largest_known_change_id_ = change_id;
612 bool MetadataDatabase::HasSyncRoot() const {
613 return index_->GetSyncRootTrackerID() != kInvalidTrackerID;
616 SyncStatusCode MetadataDatabase::PopulateInitialData(
617 int64 largest_change_id,
618 const google_apis::FileResource& sync_root_folder,
619 const ScopedVector<google_apis::FileResource>& app_root_folders) {
620 index_->SetLargestChangeID(largest_change_id);
621 UpdateLargestKnownChangeID(largest_change_id);
623 AttachSyncRoot(sync_root_folder);
624 for (size_t i = 0; i < app_root_folders.size(); ++i)
625 AttachInitialAppRoot(*app_root_folders[i]);
627 return WriteToDatabase();
630 bool MetadataDatabase::IsAppEnabled(const std::string& app_id) const {
631 int64 tracker_id = index_->GetAppRootTracker(app_id);
632 if (tracker_id == kInvalidTrackerID)
633 return false;
635 FileTracker tracker;
636 if (!index_->GetFileTracker(tracker_id, &tracker))
637 return false;
638 return tracker.tracker_kind() == TRACKER_KIND_APP_ROOT;
641 SyncStatusCode MetadataDatabase::RegisterApp(const std::string& app_id,
642 const std::string& folder_id) {
643 if (index_->GetAppRootTracker(app_id)) {
644 // The app-root is already registered.
645 return SYNC_STATUS_OK;
648 TrackerIDSet trackers = index_->GetFileTrackerIDsByFileID(folder_id);
649 if (trackers.empty()) {
650 return SYNC_DATABASE_ERROR_NOT_FOUND;
653 if (trackers.has_active()) {
654 // The folder is tracked by another tracker.
655 util::Log(logging::LOG_WARNING, FROM_HERE,
656 "Failed to register App for %s", app_id.c_str());
657 return SYNC_STATUS_HAS_CONFLICT;
660 int64 sync_root_tracker_id = index_->GetSyncRootTrackerID();
661 if (!sync_root_tracker_id) {
662 util::Log(logging::LOG_WARNING, FROM_HERE,
663 "Sync-root needs to be set up before registering app-root");
664 return SYNC_DATABASE_ERROR_NOT_FOUND;
667 scoped_ptr<FileTracker> tracker(new FileTracker);
668 if (!FilterFileTrackersByParent(index_.get(), trackers,
669 sync_root_tracker_id, tracker.get())) {
670 return SYNC_DATABASE_ERROR_NOT_FOUND;
673 tracker->set_app_id(app_id);
674 tracker->set_tracker_kind(TRACKER_KIND_APP_ROOT);
675 tracker->set_active(true);
676 tracker->set_needs_folder_listing(true);
677 tracker->set_dirty(true);
679 index_->StoreFileTracker(tracker.Pass());
680 return WriteToDatabase();
683 SyncStatusCode MetadataDatabase::DisableApp(const std::string& app_id) {
684 int64 tracker_id = index_->GetAppRootTracker(app_id);
685 scoped_ptr<FileTracker> tracker(new FileTracker);
686 if (!index_->GetFileTracker(tracker_id, tracker.get())) {
687 return SYNC_DATABASE_ERROR_NOT_FOUND;
690 if (tracker->tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT) {
691 return SYNC_STATUS_OK;
694 DCHECK_EQ(TRACKER_KIND_APP_ROOT, tracker->tracker_kind());
695 DCHECK(tracker->active());
697 // Keep the app-root tracker active (but change the tracker_kind) so that
698 // other conflicting trackers won't become active.
699 tracker->set_tracker_kind(TRACKER_KIND_DISABLED_APP_ROOT);
701 index_->StoreFileTracker(tracker.Pass());
702 return WriteToDatabase();
705 SyncStatusCode MetadataDatabase::EnableApp(const std::string& app_id) {
706 int64 tracker_id = index_->GetAppRootTracker(app_id);
707 scoped_ptr<FileTracker> tracker(new FileTracker);
708 if (!index_->GetFileTracker(tracker_id, tracker.get())) {
709 return SYNC_DATABASE_ERROR_NOT_FOUND;
712 if (tracker->tracker_kind() == TRACKER_KIND_APP_ROOT) {
713 return SYNC_STATUS_OK;
716 DCHECK_EQ(TRACKER_KIND_DISABLED_APP_ROOT, tracker->tracker_kind());
717 DCHECK(tracker->active());
719 tracker->set_tracker_kind(TRACKER_KIND_APP_ROOT);
720 index_->StoreFileTracker(tracker.Pass());
722 MarkTrackersDirtyRecursively(tracker_id, index_.get());
723 return WriteToDatabase();
726 SyncStatusCode MetadataDatabase::UnregisterApp(const std::string& app_id) {
727 int64 tracker_id = index_->GetAppRootTracker(app_id);
728 scoped_ptr<FileTracker> tracker(new FileTracker);
729 if (!index_->GetFileTracker(tracker_id, tracker.get()) ||
730 tracker->tracker_kind() == TRACKER_KIND_REGULAR) {
731 return SYNC_STATUS_OK;
734 RemoveAllDescendantTrackers(tracker_id, index_.get());
736 tracker->clear_app_id();
737 tracker->set_tracker_kind(TRACKER_KIND_REGULAR);
738 tracker->set_active(false);
739 tracker->set_dirty(true);
741 index_->StoreFileTracker(tracker.Pass());
742 return WriteToDatabase();
745 bool MetadataDatabase::FindAppRootTracker(const std::string& app_id,
746 FileTracker* tracker_out) const {
747 int64 app_root_tracker_id = index_->GetAppRootTracker(app_id);
748 if (!app_root_tracker_id)
749 return false;
751 if (tracker_out &&
752 !index_->GetFileTracker(app_root_tracker_id, tracker_out)) {
753 NOTREACHED();
754 return false;
757 return true;
760 bool MetadataDatabase::FindFileByFileID(const std::string& file_id,
761 FileMetadata* metadata_out) const {
762 return index_->GetFileMetadata(file_id, metadata_out);
765 bool MetadataDatabase::FindTrackersByFileID(const std::string& file_id,
766 TrackerIDSet* trackers_out) const {
767 TrackerIDSet trackers = index_->GetFileTrackerIDsByFileID(file_id);
768 if (trackers.empty())
769 return false;
771 if (trackers_out)
772 std::swap(trackers, *trackers_out);
773 return true;
776 bool MetadataDatabase::FindTrackersByParentAndTitle(
777 int64 parent_tracker_id,
778 const std::string& title,
779 TrackerIDSet* trackers_out) const {
780 TrackerIDSet trackers =
781 index_->GetFileTrackerIDsByParentAndTitle(parent_tracker_id, title);
782 if (trackers.empty())
783 return false;
785 if (trackers_out)
786 std::swap(trackers, *trackers_out);
787 return true;
790 bool MetadataDatabase::FindTrackerByTrackerID(int64 tracker_id,
791 FileTracker* tracker_out) const {
792 return index_->GetFileTracker(tracker_id, tracker_out);
795 bool MetadataDatabase::BuildPathForTracker(int64 tracker_id,
796 base::FilePath* path) const {
797 FileTracker current;
798 if (!FindTrackerByTrackerID(tracker_id, &current) || !current.active())
799 return false;
801 std::vector<base::FilePath> components;
802 while (!IsAppRoot(current)) {
803 std::string title = GetTrackerTitle(current);
804 if (title.empty())
805 return false;
806 components.push_back(base::FilePath::FromUTF8Unsafe(title));
807 if (!FindTrackerByTrackerID(current.parent_tracker_id(), &current) ||
808 !current.active())
809 return false;
812 if (path)
813 *path = ReverseConcatPathComponents(components);
815 return true;
818 base::FilePath MetadataDatabase::BuildDisplayPathForTracker(
819 const FileTracker& tracker) const {
820 base::FilePath path;
821 if (tracker.active()) {
822 BuildPathForTracker(tracker.tracker_id(), &path);
823 return path;
825 BuildPathForTracker(tracker.parent_tracker_id(), &path);
826 if (tracker.has_synced_details()) {
827 path = path.Append(
828 base::FilePath::FromUTF8Unsafe(tracker.synced_details().title()));
829 } else {
830 path = path.Append(FILE_PATH_LITERAL("<unknown>"));
832 return path;
835 bool MetadataDatabase::FindNearestActiveAncestor(
836 const std::string& app_id,
837 const base::FilePath& full_path,
838 FileTracker* tracker_out,
839 base::FilePath* path_out) const {
840 DCHECK(tracker_out);
841 DCHECK(path_out);
843 if (full_path.IsAbsolute() ||
844 !FindAppRootTracker(app_id, tracker_out) ||
845 tracker_out->tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT) {
846 return false;
849 std::vector<base::FilePath::StringType> components;
850 full_path.GetComponents(&components);
851 path_out->clear();
853 for (size_t i = 0; i < components.size(); ++i) {
854 const std::string title = base::FilePath(components[i]).AsUTF8Unsafe();
855 TrackerIDSet trackers;
856 if (!FindTrackersByParentAndTitle(
857 tracker_out->tracker_id(), title, &trackers) ||
858 !trackers.has_active()) {
859 return true;
862 FileTracker tracker;
863 index_->GetFileTracker(trackers.active_tracker(), &tracker);
865 DCHECK(tracker.has_synced_details());
866 const FileDetails& details = tracker.synced_details();
867 if (details.file_kind() != FILE_KIND_FOLDER && i != components.size() - 1) {
868 // This non-last component indicates file. Give up search.
869 return true;
872 tracker_out->CopyFrom(tracker);
873 *path_out = path_out->Append(components[i]);
876 return true;
879 SyncStatusCode MetadataDatabase::UpdateByChangeList(
880 int64 largest_change_id,
881 ScopedVector<google_apis::ChangeResource> changes) {
882 DCHECK_LE(index_->GetLargestChangeID(), largest_change_id);
884 for (size_t i = 0; i < changes.size(); ++i) {
885 const google_apis::ChangeResource& change = *changes[i];
886 if (HasNewerFileMetadata(change.file_id(), change.change_id()))
887 continue;
889 scoped_ptr<FileMetadata> metadata(
890 CreateFileMetadataFromChangeResource(change));
891 UpdateByFileMetadata(FROM_HERE, metadata.Pass(),
892 UPDATE_TRACKER_FOR_UNSYNCED_FILE);
895 UpdateLargestKnownChangeID(largest_change_id);
896 index_->SetLargestChangeID(largest_change_id);
897 return WriteToDatabase();
900 SyncStatusCode MetadataDatabase::UpdateByFileResource(
901 const google_apis::FileResource& resource) {
902 scoped_ptr<FileMetadata> metadata(
903 CreateFileMetadataFromFileResource(
904 GetLargestKnownChangeID(), resource));
905 UpdateByFileMetadata(FROM_HERE, metadata.Pass(),
906 UPDATE_TRACKER_FOR_UNSYNCED_FILE);
907 return WriteToDatabase();
910 SyncStatusCode MetadataDatabase::UpdateByFileResourceList(
911 ScopedVector<google_apis::FileResource> resources) {
912 for (size_t i = 0; i < resources.size(); ++i) {
913 scoped_ptr<FileMetadata> metadata(
914 CreateFileMetadataFromFileResource(
915 GetLargestKnownChangeID(), *resources[i]));
916 UpdateByFileMetadata(FROM_HERE, metadata.Pass(),
917 UPDATE_TRACKER_FOR_UNSYNCED_FILE);
919 return WriteToDatabase();
922 SyncStatusCode MetadataDatabase::UpdateByDeletedRemoteFile(
923 const std::string& file_id) {
924 scoped_ptr<FileMetadata> metadata(
925 CreateDeletedFileMetadata(GetLargestKnownChangeID(), file_id));
926 UpdateByFileMetadata(FROM_HERE, metadata.Pass(),
927 UPDATE_TRACKER_FOR_UNSYNCED_FILE);
928 return WriteToDatabase();
931 SyncStatusCode MetadataDatabase::UpdateByDeletedRemoteFileList(
932 const FileIDList& file_ids) {
933 for (FileIDList::const_iterator itr = file_ids.begin();
934 itr != file_ids.end(); ++itr) {
935 scoped_ptr<FileMetadata> metadata(
936 CreateDeletedFileMetadata(GetLargestKnownChangeID(), *itr));
937 UpdateByFileMetadata(FROM_HERE, metadata.Pass(),
938 UPDATE_TRACKER_FOR_UNSYNCED_FILE);
940 return WriteToDatabase();
943 SyncStatusCode MetadataDatabase::ReplaceActiveTrackerWithNewResource(
944 int64 parent_tracker_id,
945 const google_apis::FileResource& resource) {
946 DCHECK(!index_->GetFileMetadata(resource.file_id(), nullptr));
947 DCHECK(index_->GetFileTracker(parent_tracker_id, nullptr));
949 UpdateByFileMetadata(
950 FROM_HERE,
951 CreateFileMetadataFromFileResource(GetLargestKnownChangeID(), resource),
952 UPDATE_TRACKER_FOR_SYNCED_FILE);
954 DCHECK(index_->GetFileMetadata(resource.file_id(), nullptr));
955 DCHECK(!index_->GetFileTrackerIDsByFileID(resource.file_id()).has_active());
957 TrackerIDSet same_path_trackers =
958 index_->GetFileTrackerIDsByParentAndTitle(
959 parent_tracker_id, resource.title());
960 FileTracker to_be_activated;
961 if (!FilterFileTrackersByFileID(index_.get(), same_path_trackers,
962 resource.file_id(), &to_be_activated)) {
963 NOTREACHED();
964 return SYNC_STATUS_FAILED;
967 int64 tracker_id = to_be_activated.tracker_id();
968 if (same_path_trackers.has_active()) {
969 DeactivateFileTracker(same_path_trackers.active_tracker(),
970 MARK_ITSELF_DIRTY |
971 MARK_SAME_FILE_ID_TRACKERS_DIRTY,
972 index_.get());
975 ActivateFileTracker(tracker_id, MARK_NOTHING_DIRTY, index_.get());
976 return WriteToDatabase();
979 SyncStatusCode MetadataDatabase::PopulateFolderByChildList(
980 const std::string& folder_id,
981 const FileIDList& child_file_ids) {
982 TrackerIDSet trackers = index_->GetFileTrackerIDsByFileID(folder_id);
983 if (!trackers.has_active()) {
984 // It's OK that there is no folder to populate its children.
985 // Inactive folders should ignore their contents updates.
986 return SYNC_STATUS_OK;
989 scoped_ptr<FileTracker> folder_tracker(new FileTracker);
990 if (!index_->GetFileTracker(trackers.active_tracker(),
991 folder_tracker.get())) {
992 NOTREACHED();
993 return SYNC_STATUS_FAILED;
996 base::hash_set<std::string> children(child_file_ids.begin(),
997 child_file_ids.end());
999 std::vector<int64> known_children =
1000 index_->GetFileTrackerIDsByParent(folder_tracker->tracker_id());
1001 for (size_t i = 0; i < known_children.size(); ++i) {
1002 FileTracker tracker;
1003 if (!index_->GetFileTracker(known_children[i], &tracker)) {
1004 NOTREACHED();
1005 continue;
1007 children.erase(tracker.file_id());
1010 for (base::hash_set<std::string>::const_iterator itr = children.begin();
1011 itr != children.end(); ++itr)
1012 CreateTrackerForParentAndFileID(*folder_tracker, *itr);
1013 folder_tracker->set_needs_folder_listing(false);
1014 if (folder_tracker->dirty() && !ShouldKeepDirty(*folder_tracker))
1015 folder_tracker->set_dirty(false);
1016 index_->StoreFileTracker(folder_tracker.Pass());
1018 return WriteToDatabase();
1021 SyncStatusCode MetadataDatabase::UpdateTracker(
1022 int64 tracker_id,
1023 const FileDetails& updated_details) {
1024 FileTracker tracker;
1025 if (!index_->GetFileTracker(tracker_id, &tracker)) {
1026 return SYNC_DATABASE_ERROR_NOT_FOUND;
1029 // Check if the tracker is to be deleted.
1030 if (updated_details.missing()) {
1031 FileMetadata metadata;
1032 if (!index_->GetFileMetadata(tracker.file_id(), &metadata) ||
1033 metadata.details().missing()) {
1034 // Both the tracker and metadata have the missing flag, now it's safe to
1035 // delete the |tracker|.
1036 RemoveFileTracker(tracker_id,
1037 MARK_SAME_FILE_ID_TRACKERS_DIRTY |
1038 MARK_SAME_PATH_TRACKERS_DIRTY,
1039 index_.get());
1040 return WriteToDatabase();
1044 // Sync-root deletion should be handled separately by SyncEngine.
1045 DCHECK(tracker_id != GetSyncRootTrackerID() ||
1046 (tracker.has_synced_details() &&
1047 tracker.synced_details().title() == updated_details.title() &&
1048 !updated_details.missing()));
1050 if (tracker_id != GetSyncRootTrackerID()) {
1051 // Check if the tracker's parent is still in |parent_tracker_ids|.
1052 // If not, there should exist another tracker for the new parent, so delete
1053 // old tracker.
1054 FileTracker parent_tracker;
1055 index_->GetFileTracker(tracker.parent_tracker_id(), &parent_tracker);
1057 if (!HasFileAsParent(updated_details, parent_tracker.file_id())) {
1058 RemoveFileTracker(tracker.tracker_id(),
1059 MARK_SAME_PATH_TRACKERS_DIRTY,
1060 index_.get());
1061 return WriteToDatabase();
1064 if (tracker.has_synced_details()) {
1065 // Check if the tracker was retitled. If it was, there should exist
1066 // another tracker for the new title, so delete the tracker being updated.
1067 if (tracker.synced_details().title() != updated_details.title()) {
1068 RemoveFileTracker(tracker.tracker_id(),
1069 MARK_SAME_FILE_ID_TRACKERS_DIRTY,
1070 index_.get());
1071 return WriteToDatabase();
1073 } else {
1074 // Check if any other tracker exists has the same parent, title and
1075 // file_id to the updated tracker. If it exists, delete the tracker being
1076 // updated.
1077 if (FilterFileTrackersByFileID(
1078 index_.get(),
1079 index_->GetFileTrackerIDsByParentAndTitle(
1080 parent_tracker.tracker_id(),
1081 updated_details.title()),
1082 tracker.file_id(),
1083 nullptr)) {
1084 RemoveFileTracker(tracker.tracker_id(),
1085 MARK_NOTHING_DIRTY,
1086 index_.get());
1087 return WriteToDatabase();
1092 scoped_ptr<FileTracker> updated_tracker = CloneFileTracker(&tracker);
1093 *updated_tracker->mutable_synced_details() = updated_details;
1095 bool should_promote = false;
1097 // Activate the tracker if:
1098 // - There is no active tracker that tracks |tracker->file_id()|.
1099 // - There is no active tracker that has the same |parent| and |title|.
1100 if (!tracker.active() && CanActivateTracker(tracker)) {
1101 updated_tracker->set_active(true);
1102 updated_tracker->set_dirty(true);
1103 updated_tracker->set_needs_folder_listing(
1104 tracker.synced_details().file_kind() == FILE_KIND_FOLDER);
1105 should_promote = true;
1106 } else if (tracker.dirty() && !ShouldKeepDirty(tracker)) {
1107 updated_tracker->set_dirty(false);
1109 index_->StoreFileTracker(updated_tracker.Pass());
1110 if (should_promote)
1111 index_->PromoteDemotedDirtyTracker(tracker_id);
1113 return WriteToDatabase();
1116 MetadataDatabase::ActivationStatus MetadataDatabase::TryActivateTracker(
1117 int64 parent_tracker_id,
1118 const std::string& file_id,
1119 SyncStatusCode* status_out) {
1120 FileMetadata metadata;
1121 if (!index_->GetFileMetadata(file_id, &metadata)) {
1122 NOTREACHED();
1123 *status_out = SYNC_STATUS_FAILED;
1124 return ACTIVATION_PENDING;
1126 std::string title = metadata.details().title();
1127 DCHECK(!HasInvalidTitle(title));
1129 TrackerIDSet same_file_id_trackers =
1130 index_->GetFileTrackerIDsByFileID(file_id);
1131 scoped_ptr<FileTracker> tracker_to_be_activated(new FileTracker);
1132 FilterFileTrackersByParentAndTitle(
1133 index_.get(), same_file_id_trackers, parent_tracker_id,
1134 title, tracker_to_be_activated.get());
1136 // Check if there is another active tracker that tracks |file_id|.
1137 // This can happen when the tracked file has multiple parents.
1138 // In this case, report the failure to the caller.
1139 if (!tracker_to_be_activated->active() && same_file_id_trackers.has_active())
1140 return ACTIVATION_FAILED_ANOTHER_ACTIVE_TRACKER;
1142 if (!tracker_to_be_activated->active()) {
1143 // Check if there exists another active tracker that has the same path to
1144 // the tracker. If there is, deactivate it, assuming the caller already
1145 // overrides local file with newly added file,
1146 TrackerIDSet same_title_trackers =
1147 index_->GetFileTrackerIDsByParentAndTitle(parent_tracker_id, title);
1148 if (same_title_trackers.has_active()) {
1149 RemoveAllDescendantTrackers(same_title_trackers.active_tracker(),
1150 index_.get());
1152 scoped_ptr<FileTracker> tracker_to_be_deactivated(new FileTracker);
1153 if (index_->GetFileTracker(same_title_trackers.active_tracker(),
1154 tracker_to_be_deactivated.get())) {
1155 const std::string file_id = tracker_to_be_deactivated->file_id();
1156 tracker_to_be_deactivated->set_active(false);
1157 index_->StoreFileTracker(tracker_to_be_deactivated.Pass());
1159 MarkTrackersDirtyByFileID(file_id, index_.get());
1160 } else {
1161 NOTREACHED();
1166 tracker_to_be_activated->set_dirty(false);
1167 tracker_to_be_activated->set_active(true);
1168 *tracker_to_be_activated->mutable_synced_details() = metadata.details();
1169 if (tracker_to_be_activated->synced_details().file_kind() ==
1170 FILE_KIND_FOLDER) {
1171 tracker_to_be_activated->set_needs_folder_listing(true);
1173 tracker_to_be_activated->set_dirty(false);
1175 index_->StoreFileTracker(tracker_to_be_activated.Pass());
1177 *status_out = WriteToDatabase();
1178 return ACTIVATION_PENDING;
1181 void MetadataDatabase::DemoteTracker(int64 tracker_id) {
1182 index_->DemoteDirtyTracker(tracker_id);
1183 WriteToDatabase();
1186 bool MetadataDatabase::PromoteDemotedTrackers() {
1187 bool promoted = index_->PromoteDemotedDirtyTrackers();
1188 WriteToDatabase();
1189 return promoted;
1192 void MetadataDatabase::PromoteDemotedTracker(int64 tracker_id) {
1193 index_->PromoteDemotedDirtyTracker(tracker_id);
1194 WriteToDatabase();
1197 bool MetadataDatabase::GetDirtyTracker(
1198 FileTracker* tracker_out) const {
1199 int64 dirty_tracker_id = index_->PickDirtyTracker();
1200 if (!dirty_tracker_id)
1201 return false;
1203 if (tracker_out) {
1204 if (!index_->GetFileTracker(dirty_tracker_id, tracker_out)) {
1205 NOTREACHED();
1206 return false;
1209 return true;
1212 bool MetadataDatabase::HasDemotedDirtyTracker() const {
1213 return index_->HasDemotedDirtyTracker();
1216 bool MetadataDatabase::HasDirtyTracker() const {
1217 return index_->PickDirtyTracker() != kInvalidTrackerID;
1220 size_t MetadataDatabase::CountDirtyTracker() const {
1221 return index_->CountDirtyTracker();
1224 bool MetadataDatabase::GetMultiParentFileTrackers(std::string* file_id_out,
1225 TrackerIDSet* trackers_out) {
1226 DCHECK(file_id_out);
1227 DCHECK(trackers_out);
1229 std::string file_id = index_->PickMultiTrackerFileID();
1230 if (file_id.empty())
1231 return false;
1233 TrackerIDSet trackers = index_->GetFileTrackerIDsByFileID(file_id);
1234 if (trackers.size() <= 1) {
1235 NOTREACHED();
1236 return false;
1239 *file_id_out = file_id;
1240 std::swap(*trackers_out, trackers);
1241 return true;
1244 size_t MetadataDatabase::CountFileMetadata() const {
1245 return index_->CountFileMetadata();
1248 size_t MetadataDatabase::CountFileTracker() const {
1249 return index_->CountFileTracker();
1252 bool MetadataDatabase::GetConflictingTrackers(TrackerIDSet* trackers_out) {
1253 DCHECK(trackers_out);
1255 ParentIDAndTitle parent_and_title = index_->PickMultiBackingFilePath();
1256 if (parent_and_title.parent_id == kInvalidTrackerID)
1257 return false;
1259 TrackerIDSet trackers = index_->GetFileTrackerIDsByParentAndTitle(
1260 parent_and_title.parent_id, parent_and_title.title);
1261 if (trackers.size() <= 1) {
1262 NOTREACHED();
1263 return false;
1266 std::swap(*trackers_out, trackers);
1267 return true;
1270 void MetadataDatabase::GetRegisteredAppIDs(std::vector<std::string>* app_ids) {
1271 DCHECK(app_ids);
1272 *app_ids = index_->GetRegisteredAppIDs();
1275 SyncStatusCode MetadataDatabase::SweepDirtyTrackers(
1276 const std::vector<std::string>& file_ids) {
1277 std::set<int64> tracker_ids;
1278 for (size_t i = 0; i < file_ids.size(); ++i) {
1279 TrackerIDSet trackers_for_file_id =
1280 index_->GetFileTrackerIDsByFileID(file_ids[i]);
1281 for (TrackerIDSet::iterator itr = trackers_for_file_id.begin();
1282 itr != trackers_for_file_id.end(); ++itr)
1283 tracker_ids.insert(*itr);
1286 for (std::set<int64>::iterator itr = tracker_ids.begin();
1287 itr != tracker_ids.end(); ++itr) {
1288 scoped_ptr<FileTracker> tracker(new FileTracker);
1289 if (!index_->GetFileTracker(*itr, tracker.get()) ||
1290 !CanClearDirty(*tracker))
1291 continue;
1292 tracker->set_dirty(false);
1293 index_->StoreFileTracker(tracker.Pass());
1296 return WriteToDatabase();
1299 MetadataDatabase::MetadataDatabase(
1300 const base::FilePath& database_path,
1301 bool enable_on_disk_index,
1302 leveldb::Env* env_override)
1303 : database_path_(database_path),
1304 env_override_(env_override),
1305 enable_on_disk_index_(enable_on_disk_index),
1306 largest_known_change_id_(0),
1307 weak_ptr_factory_(this) {
1310 SyncStatusCode MetadataDatabase::Initialize() {
1311 SyncStatusCode status = SYNC_STATUS_UNKNOWN;
1312 bool created = false;
1313 // Open database unless |db_| is overridden for testing.
1314 if (!db_) {
1315 status = OpenDatabase(database_path_, env_override_, &db_, &created);
1316 if (status != SYNC_STATUS_OK)
1317 return status;
1320 if (!created) {
1321 status = MigrateDatabaseIfNeeded(db_.get());
1322 if (status != SYNC_STATUS_OK)
1323 return status;
1326 if (enable_on_disk_index_) {
1327 index_ = MetadataDatabaseIndexOnDisk::Create(db_.get());
1328 } else {
1329 index_ = MetadataDatabaseIndex::Create(db_.get());
1331 if (!index_) {
1332 // Delete all entries in |db_| to reset it.
1333 // TODO(peria): Make LevelDBWrapper::DestroyDB() to avoid a full scan.
1334 scoped_ptr<LevelDBWrapper::Iterator> itr = db_->NewIterator();
1335 for (itr->SeekToFirst(); itr->Valid();)
1336 itr->Delete();
1337 db_->Commit();
1339 return SYNC_DATABASE_ERROR_FAILED;
1342 status = LevelDBStatusToSyncStatusCode(db_->Commit());
1343 if (status != SYNC_STATUS_OK)
1344 return status;
1346 UpdateLargestKnownChangeID(index_->GetLargestChangeID());
1348 return status;
1351 void MetadataDatabase::CreateTrackerForParentAndFileID(
1352 const FileTracker& parent_tracker,
1353 const std::string& file_id) {
1354 CreateTrackerInternal(parent_tracker, file_id, nullptr,
1355 UPDATE_TRACKER_FOR_UNSYNCED_FILE);
1358 void MetadataDatabase::CreateTrackerForParentAndFileMetadata(
1359 const FileTracker& parent_tracker,
1360 const FileMetadata& file_metadata,
1361 UpdateOption option) {
1362 DCHECK(file_metadata.has_details());
1363 CreateTrackerInternal(parent_tracker,
1364 file_metadata.file_id(),
1365 &file_metadata.details(),
1366 option);
1369 void MetadataDatabase::CreateTrackerInternal(const FileTracker& parent_tracker,
1370 const std::string& file_id,
1371 const FileDetails* details,
1372 UpdateOption option) {
1373 int64 tracker_id = IncrementTrackerID();
1374 scoped_ptr<FileTracker> tracker(new FileTracker);
1375 tracker->set_tracker_id(tracker_id);
1376 tracker->set_parent_tracker_id(parent_tracker.tracker_id());
1377 tracker->set_file_id(file_id);
1378 tracker->set_app_id(parent_tracker.app_id());
1379 tracker->set_tracker_kind(TRACKER_KIND_REGULAR);
1380 tracker->set_dirty(true);
1381 tracker->set_active(false);
1382 tracker->set_needs_folder_listing(false);
1383 if (details) {
1384 *tracker->mutable_synced_details() = *details;
1385 if (option == UPDATE_TRACKER_FOR_UNSYNCED_FILE) {
1386 tracker->mutable_synced_details()->set_missing(true);
1387 tracker->mutable_synced_details()->clear_md5();
1390 index_->StoreFileTracker(tracker.Pass());
1393 void MetadataDatabase::MaybeAddTrackersForNewFile(
1394 const FileMetadata& metadata,
1395 UpdateOption option) {
1396 std::set<int64> parents_to_exclude;
1397 TrackerIDSet existing_trackers =
1398 index_->GetFileTrackerIDsByFileID(metadata.file_id());
1399 for (TrackerIDSet::const_iterator itr = existing_trackers.begin();
1400 itr != existing_trackers.end(); ++itr) {
1401 FileTracker tracker;
1402 if (!index_->GetFileTracker(*itr, &tracker)) {
1403 NOTREACHED();
1404 continue;
1407 int64 parent_tracker_id = tracker.parent_tracker_id();
1408 if (!parent_tracker_id)
1409 continue;
1411 // Exclude |parent_tracker_id| if it already has a tracker that has
1412 // unknown title or has the same title with |file|.
1413 if (!tracker.has_synced_details() ||
1414 tracker.synced_details().title() == metadata.details().title()) {
1415 parents_to_exclude.insert(parent_tracker_id);
1419 for (int i = 0; i < metadata.details().parent_folder_ids_size(); ++i) {
1420 std::string parent_folder_id = metadata.details().parent_folder_ids(i);
1421 TrackerIDSet parent_trackers =
1422 index_->GetFileTrackerIDsByFileID(parent_folder_id);
1423 for (TrackerIDSet::const_iterator itr = parent_trackers.begin();
1424 itr != parent_trackers.end(); ++itr) {
1425 FileTracker parent_tracker;
1426 index_->GetFileTracker(*itr, &parent_tracker);
1427 if (!parent_tracker.active())
1428 continue;
1430 if (ContainsKey(parents_to_exclude, parent_tracker.tracker_id()))
1431 continue;
1433 CreateTrackerForParentAndFileMetadata(
1434 parent_tracker, metadata, option);
1439 int64 MetadataDatabase::IncrementTrackerID() {
1440 int64 tracker_id = index_->GetNextTrackerID();
1441 index_->SetNextTrackerID(tracker_id + 1);
1442 DCHECK_GT(tracker_id, 0);
1443 return tracker_id;
1446 bool MetadataDatabase::CanActivateTracker(const FileTracker& tracker) {
1447 DCHECK(!tracker.active());
1448 DCHECK_NE(index_->GetSyncRootTrackerID(), tracker.tracker_id());
1450 if (HasActiveTrackerForFileID(tracker.file_id()))
1451 return false;
1453 if (tracker.app_id().empty() &&
1454 tracker.tracker_id() != GetSyncRootTrackerID()) {
1455 return false;
1458 if (!tracker.has_synced_details())
1459 return false;
1460 if (tracker.synced_details().file_kind() == FILE_KIND_UNSUPPORTED)
1461 return false;
1462 if (HasInvalidTitle(tracker.synced_details().title()))
1463 return false;
1464 DCHECK(tracker.parent_tracker_id());
1466 return !HasActiveTrackerForPath(tracker.parent_tracker_id(),
1467 tracker.synced_details().title());
1470 bool MetadataDatabase::ShouldKeepDirty(const FileTracker& tracker) const {
1471 if (HasDisabledAppRoot(tracker))
1472 return false;
1474 DCHECK(tracker.dirty());
1475 if (!tracker.has_synced_details())
1476 return true;
1478 FileMetadata metadata;
1479 if (!index_->GetFileMetadata(tracker.file_id(), &metadata))
1480 return true;
1481 DCHECK(metadata.has_details());
1483 const FileDetails& local_details = tracker.synced_details();
1484 const FileDetails& remote_details = metadata.details();
1486 if (tracker.active()) {
1487 if (tracker.needs_folder_listing())
1488 return true;
1489 if (local_details.md5() != remote_details.md5())
1490 return true;
1491 if (local_details.missing() != remote_details.missing())
1492 return true;
1495 if (local_details.title() != remote_details.title())
1496 return true;
1498 return false;
1501 bool MetadataDatabase::HasDisabledAppRoot(const FileTracker& tracker) const {
1502 int64 app_root_tracker_id = index_->GetAppRootTracker(tracker.app_id());
1503 if (app_root_tracker_id == kInvalidTrackerID)
1504 return false;
1506 FileTracker app_root_tracker;
1507 if (!index_->GetFileTracker(app_root_tracker_id, &app_root_tracker)) {
1508 NOTREACHED();
1509 return false;
1511 return app_root_tracker.tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT;
1514 bool MetadataDatabase::HasActiveTrackerForFileID(
1515 const std::string& file_id) const {
1516 return index_->GetFileTrackerIDsByFileID(file_id).has_active();
1519 bool MetadataDatabase::HasActiveTrackerForPath(int64 parent_tracker_id,
1520 const std::string& title) const {
1521 return index_->GetFileTrackerIDsByParentAndTitle(parent_tracker_id, title)
1522 .has_active();
1525 void MetadataDatabase::RemoveUnneededTrackersForMissingFile(
1526 const std::string& file_id) {
1527 TrackerIDSet trackers = index_->GetFileTrackerIDsByFileID(file_id);
1528 for (TrackerIDSet::const_iterator itr = trackers.begin();
1529 itr != trackers.end(); ++itr) {
1530 FileTracker tracker;
1531 if (!index_->GetFileTracker(*itr, &tracker)) {
1532 NOTREACHED();
1533 continue;
1536 if (!tracker.has_synced_details() || tracker.synced_details().missing()) {
1537 RemoveFileTracker(*itr, MARK_NOTHING_DIRTY, index_.get());
1542 void MetadataDatabase::UpdateByFileMetadata(
1543 const tracked_objects::Location& from_where,
1544 scoped_ptr<FileMetadata> metadata,
1545 UpdateOption option) {
1546 DCHECK(metadata);
1547 DCHECK(metadata->has_details());
1549 DVLOG(1) << from_where.function_name() << ": "
1550 << metadata->file_id() << " ("
1551 << metadata->details().title() << ")"
1552 << (metadata->details().missing() ? " deleted" : "");
1554 std::string file_id = metadata->file_id();
1555 if (metadata->details().missing())
1556 RemoveUnneededTrackersForMissingFile(file_id);
1557 else
1558 MaybeAddTrackersForNewFile(*metadata, option);
1560 TrackerIDSet trackers = index_->GetFileTrackerIDsByFileID(file_id);
1561 if (!trackers.empty()) {
1562 index_->StoreFileMetadata(metadata.Pass());
1564 if (option != UPDATE_TRACKER_FOR_SYNCED_FILE)
1565 MarkTrackerSetDirty(trackers, index_.get());
1570 SyncStatusCode MetadataDatabase::WriteToDatabase() {
1571 return LevelDBStatusToSyncStatusCode(db_->Commit());
1574 scoped_ptr<base::ListValue> MetadataDatabase::DumpFiles(
1575 const std::string& app_id) {
1576 scoped_ptr<base::ListValue> files(new base::ListValue);
1578 FileTracker app_root_tracker;
1579 if (!FindAppRootTracker(app_id, &app_root_tracker))
1580 return files.Pass();
1582 std::vector<int64> stack;
1583 AppendContents(
1584 index_->GetFileTrackerIDsByParent(app_root_tracker.tracker_id()), &stack);
1585 while (!stack.empty()) {
1586 int64 tracker_id = stack.back();
1587 stack.pop_back();
1588 AppendContents(index_->GetFileTrackerIDsByParent(tracker_id), &stack);
1590 FileTracker tracker;
1591 if (!index_->GetFileTracker(tracker_id, &tracker)) {
1592 NOTREACHED();
1593 continue;
1595 base::DictionaryValue* file = new base::DictionaryValue;
1597 base::FilePath path = BuildDisplayPathForTracker(tracker);
1598 file->SetString("path", path.AsUTF8Unsafe());
1599 if (tracker.has_synced_details()) {
1600 file->SetString("title", tracker.synced_details().title());
1601 file->SetString("type",
1602 FileKindToString(tracker.synced_details().file_kind()));
1605 base::DictionaryValue* details = new base::DictionaryValue;
1606 details->SetString("file_id", tracker.file_id());
1607 if (tracker.has_synced_details() &&
1608 tracker.synced_details().file_kind() == FILE_KIND_FILE)
1609 details->SetString("md5", tracker.synced_details().md5());
1610 details->SetString("active", tracker.active() ? "true" : "false");
1611 details->SetString("dirty", tracker.dirty() ? "true" : "false");
1613 file->Set("details", details);
1615 files->Append(file);
1618 return files.Pass();
1621 scoped_ptr<base::ListValue> MetadataDatabase::DumpDatabase() {
1622 scoped_ptr<base::ListValue> list(new base::ListValue);
1623 list->Append(DumpTrackers().release());
1624 list->Append(DumpMetadata().release());
1625 return list.Pass();
1628 bool MetadataDatabase::HasNewerFileMetadata(const std::string& file_id,
1629 int64 change_id) {
1630 FileMetadata metadata;
1631 if (!index_->GetFileMetadata(file_id, &metadata))
1632 return false;
1633 DCHECK(metadata.has_details());
1634 return metadata.details().change_id() >= change_id;
1637 scoped_ptr<base::ListValue> MetadataDatabase::DumpTrackers() {
1638 scoped_ptr<base::ListValue> trackers(new base::ListValue);
1640 // Append the first element for metadata.
1641 base::DictionaryValue* metadata = new base::DictionaryValue;
1642 const char *trackerKeys[] = {
1643 "tracker_id", "path", "file_id", "tracker_kind", "app_id",
1644 "active", "dirty", "folder_listing", "demoted",
1645 "title", "kind", "md5", "etag", "missing", "change_id",
1647 std::vector<std::string> key_strings(
1648 trackerKeys, trackerKeys + arraysize(trackerKeys));
1649 base::ListValue* keys = new base::ListValue;
1650 keys->AppendStrings(key_strings);
1651 metadata->SetString("title", "Trackers");
1652 metadata->Set("keys", keys);
1653 trackers->Append(metadata);
1655 // Append tracker data.
1656 std::vector<int64> tracker_ids(index_->GetAllTrackerIDs());
1657 for (std::vector<int64>::const_iterator itr = tracker_ids.begin();
1658 itr != tracker_ids.end(); ++itr) {
1659 const int64 tracker_id = *itr;
1660 FileTracker tracker;
1661 if (!index_->GetFileTracker(tracker_id, &tracker)) {
1662 NOTREACHED();
1663 continue;
1666 base::DictionaryValue* dict = new base::DictionaryValue;
1667 base::FilePath path = BuildDisplayPathForTracker(tracker);
1668 dict->SetString("tracker_id", base::Int64ToString(tracker_id));
1669 dict->SetString("path", path.AsUTF8Unsafe());
1670 dict->SetString("file_id", tracker.file_id());
1671 TrackerKind tracker_kind = tracker.tracker_kind();
1672 dict->SetString(
1673 "tracker_kind",
1674 tracker_kind == TRACKER_KIND_APP_ROOT ? "AppRoot" :
1675 tracker_kind == TRACKER_KIND_DISABLED_APP_ROOT ? "Disabled App" :
1676 tracker.tracker_id() == GetSyncRootTrackerID() ? "SyncRoot" :
1677 "Regular");
1678 dict->SetString("app_id", tracker.app_id());
1679 dict->SetString("active", tracker.active() ? "true" : "false");
1680 dict->SetString("dirty", tracker.dirty() ? "true" : "false");
1681 dict->SetString("folder_listing",
1682 tracker.needs_folder_listing() ? "needed" : "no");
1684 bool is_demoted = index_->IsDemotedDirtyTracker(tracker.tracker_id());
1685 dict->SetString("demoted", is_demoted ? "true" : "false");
1686 if (tracker.has_synced_details()) {
1687 const FileDetails& details = tracker.synced_details();
1688 dict->SetString("title", details.title());
1689 dict->SetString("kind", FileKindToString(details.file_kind()));
1690 dict->SetString("md5", details.md5());
1691 dict->SetString("etag", details.etag());
1692 dict->SetString("missing", details.missing() ? "true" : "false");
1693 dict->SetString("change_id", base::Int64ToString(details.change_id()));
1695 trackers->Append(dict);
1697 return trackers.Pass();
1700 scoped_ptr<base::ListValue> MetadataDatabase::DumpMetadata() {
1701 scoped_ptr<base::ListValue> files(new base::ListValue);
1703 // Append the first element for metadata.
1704 base::DictionaryValue* metadata = new base::DictionaryValue;
1705 const char *fileKeys[] = {
1706 "file_id", "title", "type", "md5", "etag", "missing",
1707 "change_id", "parents"
1709 std::vector<std::string> key_strings(
1710 fileKeys, fileKeys + arraysize(fileKeys));
1711 base::ListValue* keys = new base::ListValue;
1712 keys->AppendStrings(key_strings);
1713 metadata->SetString("title", "Metadata");
1714 metadata->Set("keys", keys);
1715 files->Append(metadata);
1717 // Append metadata data.
1718 std::vector<std::string> metadata_ids(index_->GetAllMetadataIDs());
1719 for (std::vector<std::string>::const_iterator itr = metadata_ids.begin();
1720 itr != metadata_ids.end(); ++itr) {
1721 const std::string& file_id = *itr;
1722 FileMetadata file;
1723 if (!index_->GetFileMetadata(file_id, &file)) {
1724 NOTREACHED();
1725 continue;
1728 base::DictionaryValue* dict = new base::DictionaryValue;
1729 dict->SetString("file_id", file_id);
1730 if (file.has_details()) {
1731 const FileDetails& details = file.details();
1732 dict->SetString("title", details.title());
1733 dict->SetString("type", FileKindToString(details.file_kind()));
1734 dict->SetString("md5", details.md5());
1735 dict->SetString("etag", details.etag());
1736 dict->SetString("missing", details.missing() ? "true" : "false");
1737 dict->SetString("change_id", base::Int64ToString(details.change_id()));
1739 std::vector<std::string> parents;
1740 for (int i = 0; i < details.parent_folder_ids_size(); ++i)
1741 parents.push_back(details.parent_folder_ids(i));
1742 dict->SetString("parents", JoinString(parents, ","));
1744 files->Append(dict);
1746 return files.Pass();
1749 void MetadataDatabase::AttachSyncRoot(
1750 const google_apis::FileResource& sync_root_folder) {
1751 scoped_ptr<FileMetadata> sync_root_metadata =
1752 CreateFileMetadataFromFileResource(
1753 GetLargestKnownChangeID(), sync_root_folder);
1754 scoped_ptr<FileTracker> sync_root_tracker =
1755 CreateSyncRootTracker(IncrementTrackerID(), *sync_root_metadata);
1757 index_->SetSyncRootTrackerID(sync_root_tracker->tracker_id());
1758 index_->StoreFileMetadata(sync_root_metadata.Pass());
1759 index_->StoreFileTracker(sync_root_tracker.Pass());
1762 void MetadataDatabase::AttachInitialAppRoot(
1763 const google_apis::FileResource& app_root_folder) {
1764 scoped_ptr<FileMetadata> app_root_metadata =
1765 CreateFileMetadataFromFileResource(
1766 GetLargestKnownChangeID(), app_root_folder);
1767 scoped_ptr<FileTracker> app_root_tracker =
1768 CreateInitialAppRootTracker(IncrementTrackerID(),
1769 GetSyncRootTrackerID(),
1770 *app_root_metadata);
1772 index_->StoreFileMetadata(app_root_metadata.Pass());
1773 index_->StoreFileTracker(app_root_tracker.Pass());
1776 bool MetadataDatabase::CanClearDirty(const FileTracker& tracker) {
1777 FileMetadata metadata;
1778 if (!index_->GetFileMetadata(tracker.file_id(), &metadata) ||
1779 !tracker.active() || !tracker.dirty() ||
1780 !tracker.has_synced_details() ||
1781 tracker.needs_folder_listing())
1782 return false;
1784 const FileDetails& remote_details = metadata.details();
1785 const FileDetails& synced_details = tracker.synced_details();
1786 if (remote_details.title() != synced_details.title() ||
1787 remote_details.md5() != synced_details.md5() ||
1788 remote_details.missing() != synced_details.missing())
1789 return false;
1791 std::set<std::string> parents;
1792 for (int i = 0; i < remote_details.parent_folder_ids_size(); ++i)
1793 parents.insert(remote_details.parent_folder_ids(i));
1795 for (int i = 0; i < synced_details.parent_folder_ids_size(); ++i)
1796 if (parents.erase(synced_details.parent_folder_ids(i)) != 1)
1797 return false;
1799 if (!parents.empty())
1800 return false;
1802 return true;
1805 } // namespace drive_backend
1806 } // namespace sync_file_system