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/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
{
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
.reuse_logs
= leveldb_env::kDefaultLogReuseOptionValue
;
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
) {
230 db_out
->reset(new LevelDBWrapper(make_scoped_ptr(db
)));
231 *created
= IsDatabaseEmpty(db_out
->get());
235 SyncStatusCode
MigrateDatabaseIfNeeded(LevelDBWrapper
* db
) {
236 // See metadata_database_index.cc for the database schema.
237 base::ThreadRestrictions::AssertIOAllowed();
240 leveldb::Status status
= db
->Get(kDatabaseVersionKey
, &value
);
243 if (!base::StringToInt64(value
, &version
))
244 return SYNC_DATABASE_ERROR_FAILED
;
246 if (!status
.IsNotFound())
247 return SYNC_DATABASE_ERROR_FAILED
;
254 // Drop all data in old database and refetch them from the remote service.
256 return SYNC_DATABASE_ERROR_FAILED
;
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
;
264 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
265 kDisableMetadataDatabaseOnDisk
)) {
266 MigrateDatabaseFromV4ToV3(db
->GetLevelDB());
268 return SYNC_STATUS_OK
;
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())
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())
299 index
->GetFileTrackerIDsByParentAndTitle(parent_tracker_id
, title
),
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();
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
),
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
),
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
) {
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
);
359 MarkTrackerSetDirty(trackers
, index
);
364 bool FilterFileTrackersByParent(
365 const MetadataDatabaseIndexInterface
* index
,
366 const TrackerIDSet
& trackers
,
367 int64 parent_tracker_id
,
368 FileTracker
* tracker_out
) {
370 for (TrackerIDSet::const_iterator itr
= trackers
.begin();
371 itr
!= trackers
.end(); ++itr
) {
372 if (!index
->GetFileTracker(*itr
, &tracker
)) {
377 if (tracker
.parent_tracker_id() == parent_tracker_id
) {
379 tracker_out
->CopyFrom(tracker
);
386 bool FilterFileTrackersByParentAndTitle(
387 const MetadataDatabaseIndexInterface
* index
,
388 const TrackerIDSet
& trackers
,
389 int64 parent_tracker_id
,
390 const std::string
& title
,
391 FileTracker
* result
) {
393 for (TrackerIDSet::const_iterator itr
= trackers
.begin();
394 itr
!= trackers
.end(); ++itr
) {
396 if (!index
->GetFileTracker(*itr
, &tracker
)) {
401 if (tracker
.parent_tracker_id() != parent_tracker_id
)
404 if (tracker
.has_synced_details() &&
405 tracker
.synced_details().title() != title
)
408 // Prioritize trackers that has |synced_details| when |trackers| has
409 // multiple candidates.
410 if (!found
|| tracker
.has_synced_details()) {
413 result
->CopyFrom(tracker
);
414 if (!result
|| result
->has_synced_details())
422 bool FilterFileTrackersByFileID(
423 const MetadataDatabaseIndexInterface
* index
,
424 const TrackerIDSet
& trackers
,
425 const std::string
& file_id
,
426 FileTracker
* tracker_out
) {
428 for (TrackerIDSet::const_iterator itr
= trackers
.begin();
429 itr
!= trackers
.end(); ++itr
) {
430 if (!index
->GetFileTracker(*itr
, &tracker
)) {
435 if (tracker
.file_id() == file_id
) {
437 tracker_out
->CopyFrom(tracker
);
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
);
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
));
499 if (!index
->GetFileTracker(tracker_id
, &tracker
))
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
);
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
,
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
,
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
548 metadata_database
.reset();
550 metadata_database
.reset(
551 new MetadataDatabase(database_path
,
552 enable_on_disk_index
,
554 status
= metadata_database
->Initialize();
557 if (status
!= SYNC_STATUS_OK
)
558 metadata_database
.reset();
560 *status_out
= status
;
561 return metadata_database
.Pass();
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
,
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();
580 MetadataDatabase::~MetadataDatabase() {
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
)
636 if (!index_
->GetFileTracker(tracker_id
, &tracker
))
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
)
752 !index_
->GetFileTracker(app_root_tracker_id
, tracker_out
)) {
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())
772 std::swap(trackers
, *trackers_out
);
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())
786 std::swap(trackers
, *trackers_out
);
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 {
798 if (!FindTrackerByTrackerID(tracker_id
, ¤t
) || !current
.active())
801 std::vector
<base::FilePath
> components
;
802 while (!IsAppRoot(current
)) {
803 std::string title
= GetTrackerTitle(current
);
806 components
.push_back(base::FilePath::FromUTF8Unsafe(title
));
807 if (!FindTrackerByTrackerID(current
.parent_tracker_id(), ¤t
) ||
813 *path
= ReverseConcatPathComponents(components
);
818 base::FilePath
MetadataDatabase::BuildDisplayPathForTracker(
819 const FileTracker
& tracker
) const {
821 if (tracker
.active()) {
822 BuildPathForTracker(tracker
.tracker_id(), &path
);
825 BuildPathForTracker(tracker
.parent_tracker_id(), &path
);
826 if (tracker
.has_synced_details()) {
828 base::FilePath::FromUTF8Unsafe(tracker
.synced_details().title()));
830 path
= path
.Append(FILE_PATH_LITERAL("<unknown>"));
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 {
843 if (full_path
.IsAbsolute() ||
844 !FindAppRootTracker(app_id
, tracker_out
) ||
845 tracker_out
->tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT
) {
849 std::vector
<base::FilePath::StringType
> components
;
850 full_path
.GetComponents(&components
);
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()) {
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.
872 tracker_out
->CopyFrom(tracker
);
873 *path_out
= path_out
->Append(components
[i
]);
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()))
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(
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
)) {
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(),
971 MARK_SAME_FILE_ID_TRACKERS_DIRTY
,
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())) {
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
)) {
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(
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
,
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
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
,
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
,
1071 return WriteToDatabase();
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
1077 if (FilterFileTrackersByFileID(
1079 index_
->GetFileTrackerIDsByParentAndTitle(
1080 parent_tracker
.tracker_id(),
1081 updated_details
.title()),
1084 RemoveFileTracker(tracker
.tracker_id(),
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());
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
)) {
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(),
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());
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() ==
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
);
1186 bool MetadataDatabase::PromoteDemotedTrackers() {
1187 bool promoted
= index_
->PromoteDemotedDirtyTrackers();
1192 void MetadataDatabase::PromoteDemotedTracker(int64 tracker_id
) {
1193 index_
->PromoteDemotedDirtyTracker(tracker_id
);
1197 bool MetadataDatabase::GetDirtyTracker(
1198 FileTracker
* tracker_out
) const {
1199 int64 dirty_tracker_id
= index_
->PickDirtyTracker();
1200 if (!dirty_tracker_id
)
1204 if (!index_
->GetFileTracker(dirty_tracker_id
, tracker_out
)) {
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())
1233 TrackerIDSet trackers
= index_
->GetFileTrackerIDsByFileID(file_id
);
1234 if (trackers
.size() <= 1) {
1239 *file_id_out
= file_id
;
1240 std::swap(*trackers_out
, trackers
);
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
)
1259 TrackerIDSet trackers
= index_
->GetFileTrackerIDsByParentAndTitle(
1260 parent_and_title
.parent_id
, parent_and_title
.title
);
1261 if (trackers
.size() <= 1) {
1266 std::swap(*trackers_out
, trackers
);
1270 void MetadataDatabase::GetRegisteredAppIDs(std::vector
<std::string
>* 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
))
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.
1315 status
= OpenDatabase(database_path_
, env_override_
, &db_
, &created
);
1316 if (status
!= SYNC_STATUS_OK
)
1321 status
= MigrateDatabaseIfNeeded(db_
.get());
1322 if (status
!= SYNC_STATUS_OK
)
1326 if (enable_on_disk_index_
) {
1327 index_
= MetadataDatabaseIndexOnDisk::Create(db_
.get());
1329 index_
= MetadataDatabaseIndex::Create(db_
.get());
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();)
1339 return SYNC_DATABASE_ERROR_FAILED
;
1342 status
= LevelDBStatusToSyncStatusCode(db_
->Commit());
1343 if (status
!= SYNC_STATUS_OK
)
1346 UpdateLargestKnownChangeID(index_
->GetLargestChangeID());
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(),
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);
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
)) {
1407 int64 parent_tracker_id
= tracker
.parent_tracker_id();
1408 if (!parent_tracker_id
)
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())
1430 if (ContainsKey(parents_to_exclude
, parent_tracker
.tracker_id()))
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);
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()))
1453 if (tracker
.app_id().empty() &&
1454 tracker
.tracker_id() != GetSyncRootTrackerID()) {
1458 if (!tracker
.has_synced_details())
1460 if (tracker
.synced_details().file_kind() == FILE_KIND_UNSUPPORTED
)
1462 if (HasInvalidTitle(tracker
.synced_details().title()))
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
))
1474 DCHECK(tracker
.dirty());
1475 if (!tracker
.has_synced_details())
1478 FileMetadata metadata
;
1479 if (!index_
->GetFileMetadata(tracker
.file_id(), &metadata
))
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())
1489 if (local_details
.md5() != remote_details
.md5())
1491 if (local_details
.missing() != remote_details
.missing())
1495 if (local_details
.title() != remote_details
.title())
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
)
1506 FileTracker app_root_tracker
;
1507 if (!index_
->GetFileTracker(app_root_tracker_id
, &app_root_tracker
)) {
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
)
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
)) {
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
) {
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
);
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
;
1584 index_
->GetFileTrackerIDsByParent(app_root_tracker
.tracker_id()), &stack
);
1585 while (!stack
.empty()) {
1586 int64 tracker_id
= stack
.back();
1588 AppendContents(index_
->GetFileTrackerIDsByParent(tracker_id
), &stack
);
1590 FileTracker tracker
;
1591 if (!index_
->GetFileTracker(tracker_id
, &tracker
)) {
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());
1628 bool MetadataDatabase::HasNewerFileMetadata(const std::string
& file_id
,
1630 FileMetadata metadata
;
1631 if (!index_
->GetFileMetadata(file_id
, &metadata
))
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
)) {
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();
1674 tracker_kind
== TRACKER_KIND_APP_ROOT
? "AppRoot" :
1675 tracker_kind
== TRACKER_KIND_DISABLED_APP_ROOT
? "Disabled App" :
1676 tracker
.tracker_id() == GetSyncRootTrackerID() ? "SyncRoot" :
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
;
1723 if (!index_
->GetFileMetadata(file_id
, &file
)) {
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())
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())
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)
1799 if (!parents
.empty())
1805 } // namespace drive_backend
1806 } // namespace sync_file_system