1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.h"
10 #include "base/bind.h"
11 #include "base/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
{
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
) {
53 case FILE_KIND_UNSUPPORTED
:
57 case FILE_KIND_FOLDER
:
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();
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
);
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(
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);
128 PopulateFileDetailsByFileResource(resource
, details
);
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);
145 PopulateFileDetailsByFileResource(*change
.file(), details
);
149 scoped_ptr
<FileMetadata
> CreateDeletedFileMetadata(
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);
161 scoped_ptr
<FileTracker
> CreateSyncRootTracker(
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(
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
) {
195 return scoped_ptr
<FileTracker
>(new FileTracker(*obj
));
198 // Returns true if |db| has no content.
199 bool IsDatabaseEmpty(LevelDBWrapper
* db
) {
201 scoped_ptr
<LevelDBWrapper::Iterator
> itr(db
->NewIterator());
203 return !itr
->Valid();
206 SyncStatusCode
OpenDatabase(const base::FilePath
& path
,
207 leveldb::Env
* env_override
,
208 scoped_ptr
<LevelDBWrapper
>* db_out
,
210 base::ThreadRestrictions::AssertIOAllowed();
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
;
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
) {
234 db_out
->reset(new LevelDBWrapper(make_scoped_ptr(db
)));
235 *created
= IsDatabaseEmpty(db_out
->get());
239 SyncStatusCode
MigrateDatabaseIfNeeded(LevelDBWrapper
* db
) {
240 // See metadata_database_index.cc for the database schema.
241 base::ThreadRestrictions::AssertIOAllowed();
244 leveldb::Status status
= db
->Get(kDatabaseVersionKey
, &value
);
247 if (!base::StringToInt64(value
, &version
))
248 return SYNC_DATABASE_ERROR_FAILED
;
250 if (!status
.IsNotFound())
251 return SYNC_DATABASE_ERROR_FAILED
;
258 // Drop all data in old database and refetch them from the remote service.
260 return SYNC_DATABASE_ERROR_FAILED
;
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
;
268 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
269 kDisableMetadataDatabaseOnDisk
)) {
270 MigrateDatabaseFromV4ToV3(db
->GetLevelDB());
272 return SYNC_STATUS_OK
;
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())
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())
303 index
->GetFileTrackerIDsByParentAndTitle(parent_tracker_id
, title
),
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();
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
),
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
),
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
) {
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
);
363 MarkTrackerSetDirty(trackers
, index
);
368 bool FilterFileTrackersByParent(
369 const MetadataDatabaseIndexInterface
* index
,
370 const TrackerIDSet
& trackers
,
371 int64 parent_tracker_id
,
372 FileTracker
* tracker_out
) {
374 for (TrackerIDSet::const_iterator itr
= trackers
.begin();
375 itr
!= trackers
.end(); ++itr
) {
376 if (!index
->GetFileTracker(*itr
, &tracker
)) {
381 if (tracker
.parent_tracker_id() == parent_tracker_id
) {
383 tracker_out
->CopyFrom(tracker
);
390 bool FilterFileTrackersByParentAndTitle(
391 const MetadataDatabaseIndexInterface
* index
,
392 const TrackerIDSet
& trackers
,
393 int64 parent_tracker_id
,
394 const std::string
& title
,
395 FileTracker
* result
) {
397 for (TrackerIDSet::const_iterator itr
= trackers
.begin();
398 itr
!= trackers
.end(); ++itr
) {
400 if (!index
->GetFileTracker(*itr
, &tracker
)) {
405 if (tracker
.parent_tracker_id() != parent_tracker_id
)
408 if (tracker
.has_synced_details() &&
409 tracker
.synced_details().title() != title
)
412 // Prioritize trackers that has |synced_details| when |trackers| has
413 // multiple candidates.
414 if (!found
|| tracker
.has_synced_details()) {
417 result
->CopyFrom(tracker
);
418 if (!result
|| result
->has_synced_details())
426 bool FilterFileTrackersByFileID(
427 const MetadataDatabaseIndexInterface
* index
,
428 const TrackerIDSet
& trackers
,
429 const std::string
& file_id
,
430 FileTracker
* tracker_out
) {
432 for (TrackerIDSet::const_iterator itr
= trackers
.begin();
433 itr
!= trackers
.end(); ++itr
) {
434 if (!index
->GetFileTracker(*itr
, &tracker
)) {
439 if (tracker
.file_id() == file_id
) {
441 tracker_out
->CopyFrom(tracker
);
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
);
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
));
503 if (!index
->GetFileTracker(tracker_id
, &tracker
))
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
);
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
,
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
,
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
552 metadata_database
.reset();
554 metadata_database
.reset(
555 new MetadataDatabase(database_path
,
556 enable_on_disk_index
,
558 status
= metadata_database
->Initialize();
561 if (status
!= SYNC_STATUS_OK
)
562 metadata_database
.reset();
564 *status_out
= status
;
565 return metadata_database
.Pass();
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
,
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();
584 MetadataDatabase::~MetadataDatabase() {
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
)
640 if (!index_
->GetFileTracker(tracker_id
, &tracker
))
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
)
756 !index_
->GetFileTracker(app_root_tracker_id
, tracker_out
)) {
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())
776 std::swap(trackers
, *trackers_out
);
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())
790 std::swap(trackers
, *trackers_out
);
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 {
802 if (!FindTrackerByTrackerID(tracker_id
, ¤t
) || !current
.active())
805 std::vector
<base::FilePath
> components
;
806 while (!IsAppRoot(current
)) {
807 std::string title
= GetTrackerTitle(current
);
810 components
.push_back(base::FilePath::FromUTF8Unsafe(title
));
811 if (!FindTrackerByTrackerID(current
.parent_tracker_id(), ¤t
) ||
817 *path
= ReverseConcatPathComponents(components
);
822 base::FilePath
MetadataDatabase::BuildDisplayPathForTracker(
823 const FileTracker
& tracker
) const {
825 if (tracker
.active()) {
826 BuildPathForTracker(tracker
.tracker_id(), &path
);
829 BuildPathForTracker(tracker
.parent_tracker_id(), &path
);
830 if (tracker
.has_synced_details()) {
832 base::FilePath::FromUTF8Unsafe(tracker
.synced_details().title()));
834 path
= path
.Append(FILE_PATH_LITERAL("<unknown>"));
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 {
847 if (full_path
.IsAbsolute() ||
848 !FindAppRootTracker(app_id
, tracker_out
) ||
849 tracker_out
->tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT
) {
853 std::vector
<base::FilePath::StringType
> components
;
854 full_path
.GetComponents(&components
);
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()) {
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.
876 tracker_out
->CopyFrom(tracker
);
877 *path_out
= path_out
->Append(components
[i
]);
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()))
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(
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
)) {
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(),
975 MARK_SAME_FILE_ID_TRACKERS_DIRTY
,
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())) {
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
)) {
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(
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
,
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
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
,
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
,
1075 return WriteToDatabase();
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
1081 if (FilterFileTrackersByFileID(
1083 index_
->GetFileTrackerIDsByParentAndTitle(
1084 parent_tracker
.tracker_id(),
1085 updated_details
.title()),
1088 RemoveFileTracker(tracker
.tracker_id(),
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());
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
)) {
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(),
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());
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() ==
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
);
1190 bool MetadataDatabase::PromoteDemotedTrackers() {
1191 bool promoted
= index_
->PromoteDemotedDirtyTrackers();
1196 void MetadataDatabase::PromoteDemotedTracker(int64 tracker_id
) {
1197 index_
->PromoteDemotedDirtyTracker(tracker_id
);
1201 bool MetadataDatabase::GetDirtyTracker(
1202 FileTracker
* tracker_out
) const {
1203 int64 dirty_tracker_id
= index_
->PickDirtyTracker();
1204 if (!dirty_tracker_id
)
1208 if (!index_
->GetFileTracker(dirty_tracker_id
, tracker_out
)) {
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())
1237 TrackerIDSet trackers
= index_
->GetFileTrackerIDsByFileID(file_id
);
1238 if (trackers
.size() <= 1) {
1243 *file_id_out
= file_id
;
1244 std::swap(*trackers_out
, trackers
);
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
)
1263 TrackerIDSet trackers
= index_
->GetFileTrackerIDsByParentAndTitle(
1264 parent_and_title
.parent_id
, parent_and_title
.title
);
1265 if (trackers
.size() <= 1) {
1270 std::swap(*trackers_out
, trackers
);
1274 void MetadataDatabase::GetRegisteredAppIDs(std::vector
<std::string
>* 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
))
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.
1319 status
= OpenDatabase(database_path_
, env_override_
, &db_
, &created
);
1320 if (status
!= SYNC_STATUS_OK
)
1325 status
= MigrateDatabaseIfNeeded(db_
.get());
1326 if (status
!= SYNC_STATUS_OK
)
1330 if (enable_on_disk_index_
) {
1331 index_
= MetadataDatabaseIndexOnDisk::Create(db_
.get());
1333 index_
= MetadataDatabaseIndex::Create(db_
.get());
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();)
1343 return SYNC_DATABASE_ERROR_FAILED
;
1346 status
= LevelDBStatusToSyncStatusCode(db_
->Commit());
1347 if (status
!= SYNC_STATUS_OK
)
1350 UpdateLargestKnownChangeID(index_
->GetLargestChangeID());
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(),
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);
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
)) {
1411 int64 parent_tracker_id
= tracker
.parent_tracker_id();
1412 if (!parent_tracker_id
)
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())
1434 if (ContainsKey(parents_to_exclude
, parent_tracker
.tracker_id()))
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);
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()))
1457 if (tracker
.app_id().empty() &&
1458 tracker
.tracker_id() != GetSyncRootTrackerID()) {
1462 if (!tracker
.has_synced_details())
1464 if (tracker
.synced_details().file_kind() == FILE_KIND_UNSUPPORTED
)
1466 if (HasInvalidTitle(tracker
.synced_details().title()))
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
))
1478 DCHECK(tracker
.dirty());
1479 if (!tracker
.has_synced_details())
1482 FileMetadata metadata
;
1483 if (!index_
->GetFileMetadata(tracker
.file_id(), &metadata
))
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())
1493 if (local_details
.md5() != remote_details
.md5())
1495 if (local_details
.missing() != remote_details
.missing())
1499 if (local_details
.title() != remote_details
.title())
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
)
1510 FileTracker app_root_tracker
;
1511 if (!index_
->GetFileTracker(app_root_tracker_id
, &app_root_tracker
)) {
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
)
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
)) {
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
) {
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
);
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
;
1588 index_
->GetFileTrackerIDsByParent(app_root_tracker
.tracker_id()), &stack
);
1589 while (!stack
.empty()) {
1590 int64 tracker_id
= stack
.back();
1592 AppendContents(index_
->GetFileTrackerIDsByParent(tracker_id
), &stack
);
1594 FileTracker tracker
;
1595 if (!index_
->GetFileTracker(tracker_id
, &tracker
)) {
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());
1632 bool MetadataDatabase::HasNewerFileMetadata(const std::string
& file_id
,
1634 FileMetadata metadata
;
1635 if (!index_
->GetFileMetadata(file_id
, &metadata
))
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
)) {
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();
1678 tracker_kind
== TRACKER_KIND_APP_ROOT
? "AppRoot" :
1679 tracker_kind
== TRACKER_KIND_DISABLED_APP_ROOT
? "Disabled App" :
1680 tracker
.tracker_id() == GetSyncRootTrackerID() ? "SyncRoot" :
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
;
1727 if (!index_
->GetFileMetadata(file_id
, &file
)) {
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())
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())
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)
1803 if (!parents
.empty())
1809 } // namespace drive_backend
1810 } // namespace sync_file_system