1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.h"
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/file_util.h"
13 #include "base/files/file_path.h"
14 #include "base/location.h"
15 #include "base/memory/scoped_vector.h"
16 #include "base/message_loop/message_loop_proxy.h"
17 #include "base/sequenced_task_runner.h"
18 #include "base/stl_util.h"
19 #include "base/strings/string_number_conversions.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/stringprintf.h"
22 #include "base/task_runner_util.h"
23 #include "base/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/metadata_database.pb.h"
28 #include "chrome/browser/sync_file_system/drive_backend/metadata_db_migration_util.h"
29 #include "chrome/browser/sync_file_system/logger.h"
30 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
31 #include "google_apis/drive/drive_api_parser.h"
32 #include "google_apis/drive/drive_entry_kinds.h"
33 #include "third_party/leveldatabase/src/include/leveldb/db.h"
34 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
35 #include "webkit/common/fileapi/file_system_util.h"
37 namespace sync_file_system
{
38 namespace drive_backend
{
40 struct DatabaseContents
{
41 scoped_ptr
<ServiceMetadata
> service_metadata
;
42 ScopedVector
<FileMetadata
> file_metadata
;
43 ScopedVector
<FileTracker
> file_trackers
;
48 typedef MetadataDatabase::FileByID FileByID
;
49 typedef MetadataDatabase::TrackerByID TrackerByID
;
50 typedef MetadataDatabase::TrackersByParentAndTitle TrackersByParentAndTitle
;
51 typedef MetadataDatabase::TrackersByTitle TrackersByTitle
;
53 bool IsAppRoot(const FileTracker
& tracker
) {
54 return tracker
.tracker_kind() == TRACKER_KIND_APP_ROOT
||
55 tracker
.tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT
;
58 std::string
RemovePrefix(const std::string
& str
, const std::string
& prefix
) {
59 if (StartsWithASCII(str
, prefix
, true))
60 return str
.substr(prefix
.size());
64 base::FilePath
ReverseConcatPathComponents(
65 const std::vector
<base::FilePath
>& components
) {
66 if (components
.empty())
67 return base::FilePath(FILE_PATH_LITERAL("/")).NormalizePathSeparators();
69 size_t total_size
= 0;
70 typedef std::vector
<base::FilePath
> PathComponents
;
71 for (PathComponents::const_iterator itr
= components
.begin();
72 itr
!= components
.end(); ++itr
)
73 total_size
+= itr
->value().size() + 1;
75 base::FilePath::StringType result
;
76 result
.reserve(total_size
);
77 for (PathComponents::const_reverse_iterator itr
= components
.rbegin();
78 itr
!= components
.rend(); ++itr
) {
79 result
.append(1, base::FilePath::kSeparators
[0]);
80 result
.append(itr
->value());
83 return base::FilePath(result
).NormalizePathSeparators();
86 void CreateInitialSyncRootTracker(
88 const google_apis::FileResource
& file_resource
,
89 scoped_ptr
<FileMetadata
>* file_out
,
90 scoped_ptr
<FileTracker
>* tracker_out
) {
92 PopulateFileDetailsByFileResource(file_resource
, &details
);
94 scoped_ptr
<FileMetadata
> file(new FileMetadata
);
95 file
->set_file_id(file_resource
.file_id());
96 *file
->mutable_details() = details
;
98 scoped_ptr
<FileTracker
> tracker(new FileTracker
);
99 tracker
->set_tracker_id(tracker_id
);
100 tracker
->set_file_id(file_resource
.file_id());
101 tracker
->set_parent_tracker_id(0);
102 tracker
->set_tracker_kind(TRACKER_KIND_REGULAR
);
103 tracker
->set_dirty(false);
104 tracker
->set_active(true);
105 tracker
->set_needs_folder_listing(false);
106 *tracker
->mutable_synced_details() = details
;
108 *file_out
= file
.Pass();
109 *tracker_out
= tracker
.Pass();
112 void CreateInitialAppRootTracker(
114 const FileTracker
& parent_tracker
,
115 const google_apis::FileResource
& file_resource
,
116 scoped_ptr
<FileMetadata
>* file_out
,
117 scoped_ptr
<FileTracker
>* tracker_out
) {
119 PopulateFileDetailsByFileResource(file_resource
, &details
);
121 scoped_ptr
<FileMetadata
> file(new FileMetadata
);
122 file
->set_file_id(file_resource
.file_id());
123 *file
->mutable_details() = details
;
125 scoped_ptr
<FileTracker
> tracker(new FileTracker
);
126 tracker
->set_tracker_id(tracker_id
);
127 tracker
->set_parent_tracker_id(parent_tracker
.tracker_id());
128 tracker
->set_file_id(file_resource
.file_id());
129 tracker
->set_tracker_kind(TRACKER_KIND_REGULAR
);
130 tracker
->set_dirty(false);
131 tracker
->set_active(false);
132 tracker
->set_needs_folder_listing(false);
133 *tracker
->mutable_synced_details() = details
;
135 *file_out
= file
.Pass();
136 *tracker_out
= tracker
.Pass();
139 void AdaptLevelDBStatusToSyncStatusCode(const SyncStatusCallback
& callback
,
140 const leveldb::Status
& status
) {
141 callback
.Run(LevelDBStatusToSyncStatusCode(status
));
144 void PutFileDeletionToBatch(const std::string
& file_id
,
145 leveldb::WriteBatch
* batch
) {
146 batch
->Delete(kFileMetadataKeyPrefix
+ file_id
);
149 void PutTrackerDeletionToBatch(int64 tracker_id
, leveldb::WriteBatch
* batch
) {
150 batch
->Delete(kFileTrackerKeyPrefix
+ base::Int64ToString(tracker_id
));
153 template <typename OutputIterator
>
154 OutputIterator
PushChildTrackersToContainer(
155 const TrackersByParentAndTitle
& trackers_by_parent
,
156 int64 parent_tracker_id
,
157 OutputIterator target_itr
) {
158 TrackersByParentAndTitle::const_iterator found
=
159 trackers_by_parent
.find(parent_tracker_id
);
160 if (found
== trackers_by_parent
.end())
163 for (TrackersByTitle::const_iterator title_itr
= found
->second
.begin();
164 title_itr
!= found
->second
.end(); ++title_itr
) {
165 const TrackerSet
& trackers
= title_itr
->second
;
166 for (TrackerSet::const_iterator tracker_itr
= trackers
.begin();
167 tracker_itr
!= trackers
.end(); ++tracker_itr
) {
168 *target_itr
= (*tracker_itr
)->tracker_id();
175 std::string
GetTrackerTitle(const FileTracker
& tracker
) {
176 if (tracker
.has_synced_details())
177 return tracker
.synced_details().title();
178 return std::string();
181 // Returns true if |db| has no content.
182 bool IsDatabaseEmpty(leveldb::DB
* db
) {
184 scoped_ptr
<leveldb::Iterator
> itr(db
->NewIterator(leveldb::ReadOptions()));
186 return !itr
->Valid();
189 SyncStatusCode
OpenDatabase(const base::FilePath
& path
,
190 scoped_ptr
<leveldb::DB
>* db_out
,
192 base::ThreadRestrictions::AssertIOAllowed();
196 leveldb::Options options
;
197 options
.max_open_files
= 0; // Use minimum.
198 options
.create_if_missing
= true;
199 leveldb::DB
* db
= NULL
;
200 leveldb::Status db_status
=
201 leveldb::DB::Open(options
, path
.AsUTF8Unsafe(), &db
);
202 SyncStatusCode status
= LevelDBStatusToSyncStatusCode(db_status
);
203 if (status
!= SYNC_STATUS_OK
) {
208 *created
= IsDatabaseEmpty(db
);
213 SyncStatusCode
MigrateDatabaseIfNeeded(leveldb::DB
* db
) {
214 base::ThreadRestrictions::AssertIOAllowed();
217 leveldb::Status status
=
218 db
->Get(leveldb::ReadOptions(), kDatabaseVersionKey
, &value
);
221 if (!base::StringToInt64(value
, &version
))
222 return SYNC_DATABASE_ERROR_FAILED
;
224 if (!status
.IsNotFound())
225 return SYNC_DATABASE_ERROR_FAILED
;
230 drive_backend::MigrateDatabaseFromV0ToV1(db
);
233 drive_backend::MigrateDatabaseFromV1ToV2(db
);
236 // TODO(tzik): Migrate database from version 2 to 3.
237 // * Add sync-root folder as active, dirty and needs_folder_listing
239 // * Add app-root folders for each origins. Each app-root folder for
240 // an enabled origin should be a active, dirty and
241 // needs_folder_listing folder. And Each app-root folder for a
242 // disabled origin should be an inactive, dirty and
243 // non-needs_folder_listing folder.
244 // * Add a file metadata for each file in previous version.
246 return SYNC_DATABASE_ERROR_FAILED
;
249 DCHECK_EQ(3, kCurrentDatabaseVersion
);
250 return SYNC_STATUS_OK
;
252 return SYNC_DATABASE_ERROR_FAILED
;
256 SyncStatusCode
WriteVersionInfo(leveldb::DB
* db
) {
257 base::ThreadRestrictions::AssertIOAllowed();
259 return LevelDBStatusToSyncStatusCode(
260 db
->Put(leveldb::WriteOptions(),
262 base::Int64ToString(kCurrentDatabaseVersion
)));
265 SyncStatusCode
ReadDatabaseContents(leveldb::DB
* db
,
266 DatabaseContents
* contents
) {
267 base::ThreadRestrictions::AssertIOAllowed();
271 scoped_ptr
<leveldb::Iterator
> itr(db
->NewIterator(leveldb::ReadOptions()));
272 for (itr
->SeekToFirst(); itr
->Valid(); itr
->Next()) {
273 std::string key
= itr
->key().ToString();
274 std::string value
= itr
->value().ToString();
275 if (key
== kServiceMetadataKey
) {
276 scoped_ptr
<ServiceMetadata
> service_metadata(new ServiceMetadata
);
277 if (!service_metadata
->ParseFromString(value
)) {
278 util::Log(logging::LOG_WARNING
, FROM_HERE
,
279 "Failed to parse SyncServiceMetadata");
283 contents
->service_metadata
= service_metadata
.Pass();
287 if (StartsWithASCII(key
, kFileMetadataKeyPrefix
, true)) {
288 std::string file_id
= RemovePrefix(key
, kFileMetadataKeyPrefix
);
290 scoped_ptr
<FileMetadata
> file(new FileMetadata
);
291 if (!file
->ParseFromString(itr
->value().ToString())) {
292 util::Log(logging::LOG_WARNING
, FROM_HERE
,
293 "Failed to parse a FileMetadata");
297 contents
->file_metadata
.push_back(file
.release());
301 if (StartsWithASCII(key
, kFileTrackerKeyPrefix
, true)) {
302 int64 tracker_id
= 0;
303 if (!base::StringToInt64(RemovePrefix(key
, kFileTrackerKeyPrefix
),
305 util::Log(logging::LOG_WARNING
, FROM_HERE
,
306 "Failed to parse TrackerID");
310 scoped_ptr
<FileTracker
> tracker(new FileTracker
);
311 if (!tracker
->ParseFromString(itr
->value().ToString())) {
312 util::Log(logging::LOG_WARNING
, FROM_HERE
,
313 "Failed to parse a Tracker");
316 contents
->file_trackers
.push_back(tracker
.release());
321 return SYNC_STATUS_OK
;
324 SyncStatusCode
InitializeServiceMetadata(DatabaseContents
* contents
,
325 leveldb::WriteBatch
* batch
) {
326 if (!contents
->service_metadata
) {
327 contents
->service_metadata
.reset(new ServiceMetadata
);
328 contents
->service_metadata
->set_next_tracker_id(1);
331 contents
->service_metadata
->SerializeToString(&value
);
332 batch
->Put(kServiceMetadataKey
, value
);
334 return SYNC_STATUS_OK
;
337 SyncStatusCode
RemoveUnreachableItems(DatabaseContents
* contents
,
338 leveldb::WriteBatch
* batch
) {
339 TrackerByID unvisited_trackers
;
340 typedef std::map
<int64
, std::set
<FileTracker
*> > TrackersByParent
;
341 TrackersByParent trackers_by_parent
;
343 for (ScopedVector
<FileTracker
>::iterator itr
=
344 contents
->file_trackers
.begin();
345 itr
!= contents
->file_trackers
.end();
347 FileTracker
* tracker
= *itr
;
348 DCHECK(!ContainsKey(unvisited_trackers
, tracker
->tracker_id()));
349 unvisited_trackers
[tracker
->tracker_id()] = tracker
;
350 if (tracker
->parent_tracker_id())
351 trackers_by_parent
[tracker
->parent_tracker_id()].insert(tracker
);
354 // Traverse synced tracker tree. Take only active items, app-root and their
355 // children. Drop unreachable items.
356 ScopedVector
<FileTracker
> reachable_trackers
;
357 std::stack
<int64
> pending
;
358 if (contents
->service_metadata
->sync_root_tracker_id())
359 pending
.push(contents
->service_metadata
->sync_root_tracker_id());
361 while (!pending
.empty()) {
362 int64 tracker_id
= pending
.top();
366 TrackerByID::iterator found
= unvisited_trackers
.find(tracker_id
);
367 if (found
== unvisited_trackers
.end())
370 FileTracker
* tracker
= found
->second
;
371 unvisited_trackers
.erase(found
);
372 reachable_trackers
.push_back(tracker
);
374 if (!tracker
->active())
378 TrackersByParent::iterator found
= trackers_by_parent
.find(tracker_id
);
379 if (found
== trackers_by_parent
.end())
382 for (std::set
<FileTracker
*>::const_iterator itr
=
383 found
->second
.begin();
384 itr
!= found
->second
.end();
386 pending
.push((*itr
)->tracker_id());
389 // Delete all unreachable trackers.
390 for (TrackerByID::iterator itr
= unvisited_trackers
.begin();
391 itr
!= unvisited_trackers
.end(); ++itr
) {
392 FileTracker
* tracker
= itr
->second
;
393 PutTrackerDeletionToBatch(tracker
->tracker_id(), batch
);
396 unvisited_trackers
.clear();
398 // |reachable_trackers| contains all files/folders reachable from sync-root
399 // folder via active folders and app-root folders.
400 // Update the tracker set in database contents with the reachable tracker set.
401 contents
->file_trackers
.weak_clear();
402 contents
->file_trackers
.swap(reachable_trackers
);
404 // Do the similar traverse for FileMetadata and remove FileMetadata that don't
405 // have reachable trackers.
406 FileByID unreferred_files
;
407 for (ScopedVector
<FileMetadata
>::const_iterator itr
=
408 contents
->file_metadata
.begin();
409 itr
!= contents
->file_metadata
.end();
411 unreferred_files
.insert(std::make_pair((*itr
)->file_id(), *itr
));
414 ScopedVector
<FileMetadata
> referred_files
;
415 for (ScopedVector
<FileTracker
>::const_iterator itr
=
416 contents
->file_trackers
.begin();
417 itr
!= contents
->file_trackers
.end();
419 FileByID::iterator found
= unreferred_files
.find((*itr
)->file_id());
420 if (found
!= unreferred_files
.end()) {
421 referred_files
.push_back(found
->second
);
422 unreferred_files
.erase(found
);
426 for (FileByID::iterator itr
= unreferred_files
.begin();
427 itr
!= unreferred_files
.end(); ++itr
) {
428 FileMetadata
* file
= itr
->second
;
429 PutFileDeletionToBatch(file
->file_id(), batch
);
432 unreferred_files
.clear();
434 contents
->file_metadata
.weak_clear();
435 contents
->file_metadata
.swap(referred_files
);
437 return SYNC_STATUS_OK
;
440 template <typename Container
, typename Key
, typename Value
>
441 bool FindItem(const Container
& container
, const Key
& key
, Value
* value
) {
442 typename
Container::const_iterator found
= container
.find(key
);
443 if (found
== container
.end())
446 *value
= *found
->second
;
450 template <typename Container
, typename Key
>
451 typename
Container::mapped_type
FindAndEraseItem(Container
* container
,
453 typedef typename
Container::mapped_type Value
;
454 typename
Container::iterator found
= container
->find(key
);
455 if (found
== container
->end())
458 Value result
= found
->second
;
459 container
->erase(found
);
463 void RunSoon(const tracked_objects::Location
& from_here
,
464 const base::Closure
& closure
) {
465 base::MessageLoopProxy::current()->PostTask(from_here
, closure
);
468 bool HasInvalidTitle(const std::string
& title
) {
469 return title
.find('/') != std::string::npos
||
470 title
.find('\\') != std::string::npos
;
475 bool MetadataDatabase::DirtyTrackerComparator::operator()(
476 const FileTracker
* left
,
477 const FileTracker
* right
) const {
478 return left
->tracker_id() < right
->tracker_id();
482 void MetadataDatabase::Create(base::SequencedTaskRunner
* task_runner
,
483 const base::FilePath
& database_path
,
484 const CreateCallback
& callback
) {
485 task_runner
->PostTask(FROM_HERE
, base::Bind(
487 base::MessageLoopProxy::current(),
488 make_scoped_refptr(task_runner
),
489 database_path
, callback
));
492 MetadataDatabase::~MetadataDatabase() {
493 task_runner_
->DeleteSoon(FROM_HERE
, db_
.release());
494 STLDeleteContainerPairSecondPointers(
495 file_by_id_
.begin(), file_by_id_
.end());
496 STLDeleteContainerPairSecondPointers(
497 tracker_by_id_
.begin(), tracker_by_id_
.end());
501 void MetadataDatabase::ClearDatabase(
502 scoped_ptr
<MetadataDatabase
> metadata_database
) {
503 DCHECK(metadata_database
);
504 scoped_refptr
<base::SequencedTaskRunner
> task_runner
=
505 metadata_database
->task_runner_
;
506 base::FilePath database_path
= metadata_database
->database_path_
;
507 DCHECK(!database_path
.empty());
508 metadata_database
.reset();
510 task_runner
->PostTask(
512 base::Bind(base::IgnoreResult(base::DeleteFile
),
513 database_path
, true /* recursive */));
516 int64
MetadataDatabase::GetLargestFetchedChangeID() const {
517 return service_metadata_
->largest_change_id();
520 int64
MetadataDatabase::GetSyncRootTrackerID() const {
521 return service_metadata_
->sync_root_tracker_id();
524 int64
MetadataDatabase::GetLargestKnownChangeID() const {
525 DCHECK_LE(GetLargestFetchedChangeID(), largest_known_change_id_
);
526 return largest_known_change_id_
;
529 void MetadataDatabase::UpdateLargestKnownChangeID(int64 change_id
) {
530 if (largest_known_change_id_
< change_id
)
531 largest_known_change_id_
= change_id
;
534 bool MetadataDatabase::HasSyncRoot() const {
535 return service_metadata_
->has_sync_root_tracker_id() &&
536 !!service_metadata_
->sync_root_tracker_id();
539 void MetadataDatabase::PopulateInitialData(
540 int64 largest_change_id
,
541 const google_apis::FileResource
& sync_root_folder
,
542 const ScopedVector
<google_apis::FileResource
>& app_root_folders
,
543 const SyncStatusCallback
& callback
) {
544 DCHECK(tracker_by_id_
.empty());
545 DCHECK(file_by_id_
.empty());
547 scoped_ptr
<leveldb::WriteBatch
> batch(new leveldb::WriteBatch
);
548 service_metadata_
->set_largest_change_id(largest_change_id
);
549 UpdateLargestKnownChangeID(largest_change_id
);
551 FileTracker
* sync_root_tracker
= NULL
;
552 int64 sync_root_tracker_id
= 0;
554 scoped_ptr
<FileMetadata
> folder
;
555 scoped_ptr
<FileTracker
> tracker
;
556 CreateInitialSyncRootTracker(GetNextTrackerID(batch
.get()),
560 std::string sync_root_folder_id
= folder
->file_id();
561 sync_root_tracker
= tracker
.get();
562 sync_root_tracker_id
= tracker
->tracker_id();
564 PutFileToBatch(*folder
, batch
.get());
565 PutTrackerToBatch(*tracker
, batch
.get());
567 service_metadata_
->set_sync_root_tracker_id(tracker
->tracker_id());
568 PutServiceMetadataToBatch(*service_metadata_
, batch
.get());
570 trackers_by_file_id_
[folder
->file_id()].Insert(tracker
.get());
572 file_by_id_
[sync_root_folder_id
] = folder
.release();
573 tracker_by_id_
[sync_root_tracker_id
] = tracker
.release();
576 for (ScopedVector
<google_apis::FileResource
>::const_iterator itr
=
577 app_root_folders
.begin();
578 itr
!= app_root_folders
.end();
580 const google_apis::FileResource
& folder_resource
= **itr
;
581 scoped_ptr
<FileMetadata
> folder
;
582 scoped_ptr
<FileTracker
> tracker
;
583 CreateInitialAppRootTracker(GetNextTrackerID(batch
.get()),
588 std::string title
= folder
->details().title();
589 std::string folder_id
= folder
->file_id();
590 int64 tracker_id
= tracker
->tracker_id();
592 PutFileToBatch(*folder
, batch
.get());
593 PutTrackerToBatch(*tracker
, batch
.get());
595 trackers_by_file_id_
[folder_id
].Insert(tracker
.get());
596 trackers_by_parent_and_title_
[sync_root_tracker_id
][title
]
597 .Insert(tracker
.get());
599 file_by_id_
[folder_id
] = folder
.release();
600 tracker_by_id_
[tracker_id
] = tracker
.release();
603 WriteToDatabase(batch
.Pass(), callback
);
606 bool MetadataDatabase::IsAppEnabled(const std::string
& app_id
) const {
608 if (!FindAppRootTracker(app_id
, &tracker
))
610 return tracker
.tracker_kind() == TRACKER_KIND_APP_ROOT
;
613 void MetadataDatabase::RegisterApp(const std::string
& app_id
,
614 const std::string
& folder_id
,
615 const SyncStatusCallback
& callback
) {
616 if (FindAppRootTracker(app_id
, NULL
)) {
617 // The app-root is already registered.
618 RunSoon(FROM_HERE
, base::Bind(callback
, SYNC_STATUS_OK
));
623 if (!FindTrackersByFileID(folder_id
, &trackers
) || trackers
.has_active()) {
624 // The folder is tracked by another tracker.
625 util::Log(logging::LOG_WARNING
, FROM_HERE
,
626 "Failed to register App for %s", app_id
.c_str());
627 RunSoon(FROM_HERE
, base::Bind(callback
, SYNC_STATUS_HAS_CONFLICT
));
631 int64 sync_root_tracker_id
= service_metadata_
->sync_root_tracker_id();
632 if (!sync_root_tracker_id
) {
633 util::Log(logging::LOG_WARNING
, FROM_HERE
,
634 "Sync-root needs to be set up before registering app-root");
635 RunSoon(FROM_HERE
, base::Bind(callback
, SYNC_DATABASE_ERROR_NOT_FOUND
));
639 // Make this tracker an app-root tracker.
640 FileTracker
* app_root_tracker
= NULL
;
641 for (TrackerSet::iterator itr
= trackers
.begin();
642 itr
!= trackers
.end(); ++itr
) {
643 FileTracker
* tracker
= *itr
;
644 if (tracker
->parent_tracker_id() == sync_root_tracker_id
)
645 app_root_tracker
= tracker
;
648 if (!app_root_tracker
) {
649 RunSoon(FROM_HERE
, base::Bind(callback
, SYNC_DATABASE_ERROR_NOT_FOUND
));
653 scoped_ptr
<leveldb::WriteBatch
> batch(new leveldb::WriteBatch
);
654 RegisterTrackerAsAppRoot(app_id
, app_root_tracker
->tracker_id(), batch
.get());
655 WriteToDatabase(batch
.Pass(), callback
);
658 void MetadataDatabase::DisableApp(const std::string
& app_id
,
659 const SyncStatusCallback
& callback
) {
661 if (!FindAppRootTracker(app_id
, &tracker
)) {
662 RunSoon(FROM_HERE
, base::Bind(callback
, SYNC_DATABASE_ERROR_NOT_FOUND
));
666 if (tracker
.tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT
) {
667 RunSoon(FROM_HERE
, base::Bind(callback
, SYNC_STATUS_OK
));
671 scoped_ptr
<leveldb::WriteBatch
> batch(new leveldb::WriteBatch
);
672 MakeAppRootDisabled(tracker
.tracker_id(), batch
.get());
673 WriteToDatabase(batch
.Pass(), callback
);
676 void MetadataDatabase::EnableApp(const std::string
& app_id
,
677 const SyncStatusCallback
& callback
) {
679 if (!FindAppRootTracker(app_id
, &tracker
) ||
680 tracker
.tracker_kind() == TRACKER_KIND_REGULAR
) {
681 RunSoon(FROM_HERE
, base::Bind(callback
, SYNC_DATABASE_ERROR_NOT_FOUND
));
685 if (tracker
.tracker_kind() == TRACKER_KIND_APP_ROOT
) {
686 RunSoon(FROM_HERE
, base::Bind(callback
, SYNC_STATUS_OK
));
690 scoped_ptr
<leveldb::WriteBatch
> batch(new leveldb::WriteBatch
);
691 MakeAppRootEnabled(tracker
.tracker_id(), batch
.get());
692 WriteToDatabase(batch
.Pass(), callback
);
695 void MetadataDatabase::UnregisterApp(const std::string
& app_id
,
696 const SyncStatusCallback
& callback
) {
698 if (!FindAppRootTracker(app_id
, &tracker
) ||
699 tracker
.tracker_kind() == TRACKER_KIND_REGULAR
) {
700 RunSoon(FROM_HERE
, base::Bind(callback
, SYNC_STATUS_OK
));
704 scoped_ptr
<leveldb::WriteBatch
> batch(new leveldb::WriteBatch
);
705 UnregisterTrackerAsAppRoot(app_id
, batch
.get());
706 WriteToDatabase(batch
.Pass(), callback
);
709 bool MetadataDatabase::FindAppRootTracker(const std::string
& app_id
,
710 FileTracker
* tracker
) const {
711 return FindItem(app_root_by_app_id_
, app_id
, tracker
);
714 bool MetadataDatabase::FindFileByFileID(const std::string
& file_id
,
715 FileMetadata
* file
) const {
716 return FindItem(file_by_id_
, file_id
, file
);
719 bool MetadataDatabase::FindTrackersByFileID(const std::string
& file_id
,
720 TrackerSet
* trackers
) const {
721 TrackersByFileID::const_iterator found
= trackers_by_file_id_
.find(file_id
);
722 if (found
== trackers_by_file_id_
.end())
725 *trackers
= found
->second
;
729 bool MetadataDatabase::FindTrackersByParentAndTitle(
730 int64 parent_tracker_id
,
731 const std::string
& title
,
732 TrackerSet
* trackers
) const {
733 TrackersByParentAndTitle::const_iterator found_by_parent
=
734 trackers_by_parent_and_title_
.find(parent_tracker_id
);
735 if (found_by_parent
== trackers_by_parent_and_title_
.end())
738 TrackersByTitle::const_iterator found_by_title
=
739 found_by_parent
->second
.find(title
);
740 if (found_by_title
== found_by_parent
->second
.end())
744 *trackers
= found_by_title
->second
;
748 bool MetadataDatabase::FindTrackerByTrackerID(int64 tracker_id
,
749 FileTracker
* tracker
) const {
750 return FindItem(tracker_by_id_
, tracker_id
, tracker
);
753 bool MetadataDatabase::BuildPathForTracker(int64 tracker_id
,
754 base::FilePath
* path
) const {
756 if (!FindTrackerByTrackerID(tracker_id
, ¤t
) || !current
.active())
759 std::vector
<base::FilePath
> components
;
760 while (!IsAppRoot(current
)) {
761 std::string title
= GetTrackerTitle(current
);
764 components
.push_back(base::FilePath::FromUTF8Unsafe(title
));
765 if (!FindTrackerByTrackerID(current
.parent_tracker_id(), ¤t
) ||
771 *path
= ReverseConcatPathComponents(components
);
776 base::FilePath
MetadataDatabase::BuildDisplayPathForTracker(
777 const FileTracker
& tracker
) const {
779 if (tracker
.active()) {
780 BuildPathForTracker(tracker
.tracker_id(), &path
);
783 BuildPathForTracker(tracker
.parent_tracker_id(), &path
);
784 if (tracker
.has_synced_details()) {
786 base::FilePath::FromUTF8Unsafe(tracker
.synced_details().title()));
788 path
= path
.Append(FILE_PATH_LITERAL("<unknown>"));
793 bool MetadataDatabase::FindNearestActiveAncestor(
794 const std::string
& app_id
,
795 const base::FilePath
& full_path
,
796 FileTracker
* tracker
,
797 base::FilePath
* path
) const {
801 if (full_path
.IsAbsolute() ||
802 !FindAppRootTracker(app_id
, tracker
) ||
803 tracker
->tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT
) {
807 std::vector
<base::FilePath::StringType
> components
;
808 full_path
.GetComponents(&components
);
811 for (size_t i
= 0; i
< components
.size(); ++i
) {
812 const std::string title
= base::FilePath(components
[i
]).AsUTF8Unsafe();
814 if (!FindTrackersByParentAndTitle(
815 tracker
->tracker_id(), title
, &trackers
) ||
816 !trackers
.has_active()) {
820 DCHECK(trackers
.active_tracker()->has_synced_details());
821 const FileDetails
& details
= trackers
.active_tracker()->synced_details();
822 if (details
.file_kind() != FILE_KIND_FOLDER
&& i
!= components
.size() - 1) {
823 // This non-last component indicates file. Give up search.
827 *tracker
= *trackers
.active_tracker();
828 *path
= path
->Append(components
[i
]);
834 void MetadataDatabase::UpdateByChangeList(
835 int64 largest_change_id
,
836 ScopedVector
<google_apis::ChangeResource
> changes
,
837 const SyncStatusCallback
& callback
) {
838 DCHECK_LE(service_metadata_
->largest_change_id(), largest_change_id
);
840 scoped_ptr
<leveldb::WriteBatch
> batch(new leveldb::WriteBatch
);
842 for (ScopedVector
<google_apis::ChangeResource
>::const_iterator itr
=
844 itr
!= changes
.end();
846 const google_apis::ChangeResource
& change
= **itr
;
847 if (HasNewerFileMetadata(change
.file_id(), change
.change_id()))
850 scoped_ptr
<FileMetadata
> file(
851 CreateFileMetadataFromChangeResource(change
));
852 UpdateByFileMetadata(FROM_HERE
, file
.Pass(), batch
.get());
855 UpdateLargestKnownChangeID(largest_change_id
);
856 service_metadata_
->set_largest_change_id(largest_change_id
);
857 PutServiceMetadataToBatch(*service_metadata_
, batch
.get());
858 WriteToDatabase(batch
.Pass(), callback
);
861 void MetadataDatabase::UpdateByFileResource(
862 const google_apis::FileResource
& resource
,
863 const SyncStatusCallback
& callback
) {
864 scoped_ptr
<leveldb::WriteBatch
> batch(new leveldb::WriteBatch
);
866 scoped_ptr
<FileMetadata
> file(
867 CreateFileMetadataFromFileResource(
868 GetLargestKnownChangeID(), resource
));
869 UpdateByFileMetadata(FROM_HERE
, file
.Pass(), batch
.get());
870 WriteToDatabase(batch
.Pass(), callback
);
873 void MetadataDatabase::UpdateByFileResourceList(
874 ScopedVector
<google_apis::FileResource
> resources
,
875 const SyncStatusCallback
& callback
) {
876 scoped_ptr
<leveldb::WriteBatch
> batch(new leveldb::WriteBatch
);
878 for (size_t i
= 0; i
< resources
.size(); ++i
) {
879 scoped_ptr
<FileMetadata
> file(
880 CreateFileMetadataFromFileResource(
881 GetLargestKnownChangeID(), *resources
[i
]));
882 UpdateByFileMetadata(FROM_HERE
, file
.Pass(), batch
.get());
884 WriteToDatabase(batch
.Pass(), callback
);
887 void MetadataDatabase::UpdateByDeletedRemoteFile(
888 const std::string
& file_id
,
889 const SyncStatusCallback
& callback
) {
890 scoped_ptr
<leveldb::WriteBatch
> batch(new leveldb::WriteBatch
);
891 scoped_ptr
<FileMetadata
> file(
892 CreateDeletedFileMetadata(GetLargestKnownChangeID(), file_id
));
893 UpdateByFileMetadata(FROM_HERE
, file
.Pass(), batch
.get());
894 WriteToDatabase(batch
.Pass(), callback
);
897 void MetadataDatabase::UpdateByDeletedRemoteFileList(
898 const FileIDList
& file_ids
,
899 const SyncStatusCallback
& callback
) {
900 scoped_ptr
<leveldb::WriteBatch
> batch(new leveldb::WriteBatch
);
901 for (FileIDList::const_iterator itr
= file_ids
.begin();
902 itr
!= file_ids
.end(); ++itr
) {
903 scoped_ptr
<FileMetadata
> file(
904 CreateDeletedFileMetadata(GetLargestKnownChangeID(), *itr
));
905 UpdateByFileMetadata(FROM_HERE
, file
.Pass(), batch
.get());
907 WriteToDatabase(batch
.Pass(), callback
);
910 void MetadataDatabase::ReplaceActiveTrackerWithNewResource(
911 int64 parent_tracker_id
,
912 const google_apis::FileResource
& resource
,
913 const SyncStatusCallback
& callback
) {
914 scoped_ptr
<leveldb::WriteBatch
> batch(new leveldb::WriteBatch
);
916 scoped_ptr
<FileMetadata
> file(
917 CreateFileMetadataFromFileResource(
918 GetLargestKnownChangeID(), resource
));
919 std::string file_id
= file
->file_id();
920 DCHECK(!ContainsKey(file_by_id_
, file_id
));
921 DCHECK(!file
->details().missing());
923 // TODO(tzik): Consolidate with UpdateByChangeList.
924 MaybeAddTrackersForNewFile(*file
, batch
.get());
926 const FileDetails
& new_file_details
= file
->details();
927 PutFileToBatch(*file
, batch
.get());
928 file_by_id_
[file_id
] = file
.release();
930 TrackerSet new_trackers
;
931 if (!FindTrackersByFileID(file_id
, &new_trackers
)) {
933 WriteToDatabase(batch
.Pass(), callback
);
936 DCHECK_EQ(1u, new_trackers
.size());
938 FileTracker
* new_tracker
= *new_trackers
.begin();
939 DCHECK(!new_tracker
->active());
940 DCHECK(new_tracker
->has_synced_details());
941 DCHECK_EQ(parent_tracker_id
, new_tracker
->parent_tracker_id());
943 std::string title
= new_file_details
.title();
945 if (FindTrackersByParentAndTitle(parent_tracker_id
, title
, &trackers
) &&
946 trackers
.has_active())
947 MakeTrackerInactive(trackers
.active_tracker()->tracker_id(), batch
.get());
949 MakeTrackerActive(new_tracker
->tracker_id(), batch
.get());
951 if (new_tracker
->synced_details().title() != title
) {
952 trackers_by_parent_and_title_
[parent_tracker_id
]
953 [GetTrackerTitle(*new_tracker
)].Erase(new_tracker
);
954 trackers_by_parent_and_title_
[parent_tracker_id
][title
].Insert(
957 *new_tracker
->mutable_synced_details() = new_file_details
;
959 new_tracker
->set_dirty(false);
960 dirty_trackers_
.erase(new_tracker
);
961 low_priority_dirty_trackers_
.erase(new_tracker
);
962 PutTrackerToBatch(*new_tracker
, batch
.get());
964 WriteToDatabase(batch
.Pass(), callback
);
967 void MetadataDatabase::PopulateFolderByChildList(
968 const std::string
& folder_id
,
969 const FileIDList
& child_file_ids
,
970 const SyncStatusCallback
& callback
) {
972 if (!FindTrackersByFileID(folder_id
, &trackers
) ||
973 !trackers
.has_active()) {
974 // It's OK that there is no folder to populate its children.
975 // Inactive folders should ignore their contents updates.
976 RunSoon(FROM_HERE
, base::Bind(callback
, SYNC_STATUS_OK
));
980 FileTracker
* folder_tracker
=
981 tracker_by_id_
[trackers
.active_tracker()->tracker_id()];
982 DCHECK(folder_tracker
);
983 std::set
<std::string
> children(child_file_ids
.begin(), child_file_ids
.end());
985 std::vector
<int64
> known_children
;
986 PushChildTrackersToContainer(trackers_by_parent_and_title_
,
987 folder_tracker
->tracker_id(),
988 std::back_inserter(known_children
));
989 for (std::vector
<int64
>::iterator itr
= known_children
.begin();
990 itr
!= known_children
.end(); ++itr
)
991 children
.erase(tracker_by_id_
[*itr
]->file_id());
993 scoped_ptr
<leveldb::WriteBatch
> batch(new leveldb::WriteBatch
);
994 for (FileIDList::const_iterator itr
= child_file_ids
.begin();
995 itr
!= child_file_ids
.end(); ++itr
)
996 CreateTrackerForParentAndFileID(*folder_tracker
, *itr
, batch
.get());
997 folder_tracker
->set_needs_folder_listing(false);
998 if (folder_tracker
->dirty() && !ShouldKeepDirty(*folder_tracker
)) {
999 folder_tracker
->set_dirty(false);
1000 dirty_trackers_
.erase(folder_tracker
);
1001 low_priority_dirty_trackers_
.erase(folder_tracker
);
1003 PutTrackerToBatch(*folder_tracker
, batch
.get());
1005 WriteToDatabase(batch
.Pass(), callback
);
1008 void MetadataDatabase::UpdateTracker(int64 tracker_id
,
1009 const FileDetails
& updated_details
,
1010 const SyncStatusCallback
& callback
) {
1011 TrackerByID::iterator found
= tracker_by_id_
.find(tracker_id
);
1012 if (found
== tracker_by_id_
.end()) {
1013 RunSoon(FROM_HERE
, base::Bind(callback
, SYNC_DATABASE_ERROR_NOT_FOUND
));
1017 FileTracker
* tracker
= found
->second
;
1020 scoped_ptr
<leveldb::WriteBatch
> batch(new leveldb::WriteBatch
);
1022 if (updated_details
.missing()) {
1023 FileByID::iterator found
= file_by_id_
.find(tracker
->file_id());
1024 if (found
== file_by_id_
.end() || found
->second
->details().missing()) {
1025 // Both the tracker and metadata have the missing flag, now it's safe to
1026 // delete the |tracker|.
1027 RemoveTracker(tracker
->tracker_id(), batch
.get());
1028 WriteToDatabase(batch
.Pass(), callback
);
1033 // Sync-root deletion should be handled separately by SyncEngine.
1034 DCHECK(tracker_id
!= GetSyncRootTrackerID() ||
1035 (tracker
->has_synced_details() &&
1036 tracker
->synced_details().title() == updated_details
.title() &&
1037 !updated_details
.missing()));
1039 if (tracker_id
!= GetSyncRootTrackerID()) {
1040 // Check if the tracker's parent is still in |parent_tracker_ids|.
1041 // If not, there should exist another tracker for the new parent, so delete
1043 DCHECK(ContainsKey(tracker_by_id_
, tracker
->parent_tracker_id()));
1044 FileTracker
* parent_tracker
= tracker_by_id_
[tracker
->parent_tracker_id()];
1045 if (!HasFileAsParent(updated_details
, parent_tracker
->file_id())) {
1046 RemoveTracker(tracker
->tracker_id(), batch
.get());
1047 WriteToDatabase(batch
.Pass(), callback
);
1051 if (tracker
->has_synced_details()) {
1052 // Check if the tracker was retitled. If it was, there should exist
1053 // another tracker for the new title, so delete old tracker.
1054 if (tracker
->synced_details().title() != updated_details
.title()) {
1055 RemoveTracker(tracker
->tracker_id(), batch
.get());
1056 WriteToDatabase(batch
.Pass(), callback
);
1060 int64 parent_tracker_id
= parent_tracker
->tracker_id();
1061 const std::string
& title
= updated_details
.title();
1062 TrackerSet
* trackers
=
1063 &trackers_by_parent_and_title_
[parent_tracker_id
][title
];
1065 for (TrackerSet::iterator itr
= trackers
->begin();
1066 itr
!= trackers
->end(); ++itr
) {
1067 if ((*itr
)->file_id() == tracker
->file_id()) {
1068 RemoveTracker(tracker
->tracker_id(), batch
.get());
1069 WriteToDatabase(batch
.Pass(), callback
);
1074 trackers_by_parent_and_title_
[parent_tracker_id
][std::string()].Erase(
1076 trackers
->Insert(tracker
);
1080 *tracker
->mutable_synced_details() = updated_details
;
1082 // Activate the tracker if:
1083 // - There is no active tracker that tracks |tracker->file_id()|.
1084 // - There is no active tracker that has the same |parent| and |title|.
1085 if (!tracker
->active() && CanActivateTracker(*tracker
))
1086 MakeTrackerActive(tracker
->tracker_id(), batch
.get());
1087 if (tracker
->dirty() && !ShouldKeepDirty(*tracker
)) {
1088 tracker
->set_dirty(false);
1089 dirty_trackers_
.erase(tracker
);
1090 low_priority_dirty_trackers_
.erase(tracker
);
1092 PutTrackerToBatch(*tracker
, batch
.get());
1094 WriteToDatabase(batch
.Pass(), callback
);
1097 bool MetadataDatabase::TryNoSideEffectActivation(
1098 int64 parent_tracker_id
,
1099 const std::string
& file_id
,
1100 const SyncStatusCallback
& callback
) {
1101 DCHECK(ContainsKey(tracker_by_id_
, parent_tracker_id
));
1102 DCHECK(ContainsKey(file_by_id_
, file_id
));
1105 if (!FindFileByFileID(file_id
, &file
)) {
1107 RunSoon(FROM_HERE
, base::Bind(callback
, SYNC_STATUS_FAILED
));
1110 std::string title
= file
.details().title();
1111 DCHECK(!HasInvalidTitle(title
));
1113 TrackerSet same_file_id
;
1114 FindTrackersByFileID(file_id
, &same_file_id
);
1116 FileTracker
* tracker
= NULL
;
1117 for (TrackerSet::iterator itr
= same_file_id
.begin();
1118 itr
!= same_file_id
.end(); ++itr
) {
1119 FileTracker
* candidate
= *itr
;
1120 if (candidate
->parent_tracker_id() != parent_tracker_id
)
1123 if (candidate
->has_synced_details() &&
1124 candidate
->synced_details().title() != title
)
1126 tracker
= candidate
;
1131 if (!tracker
->active()) {
1132 if (same_file_id
.has_active())
1135 TrackerSet same_title
;
1136 FindTrackersByParentAndTitle(parent_tracker_id
, title
, &same_title
);
1137 if (same_title
.has_active())
1141 scoped_ptr
<leveldb::WriteBatch
> batch(new leveldb::WriteBatch
);
1142 if (!tracker
->has_synced_details() ||
1143 tracker
->synced_details().title() != title
) {
1144 trackers_by_parent_and_title_
[parent_tracker_id
]
1145 [GetTrackerTitle(*tracker
)].Erase(tracker
);
1146 trackers_by_parent_and_title_
[parent_tracker_id
][title
].Insert(
1149 *tracker
->mutable_synced_details() = file
.details();
1151 MakeTrackerActive(tracker
->tracker_id(), batch
.get());
1152 tracker
->set_dirty(false);
1153 dirty_trackers_
.erase(tracker
);
1154 low_priority_dirty_trackers_
.erase(tracker
);
1155 PutTrackerToBatch(*tracker
, batch
.get());
1157 WriteToDatabase(batch
.Pass(), callback
);
1161 void MetadataDatabase::LowerTrackerPriority(int64 tracker_id
) {
1162 TrackerByID::const_iterator found
= tracker_by_id_
.find(tracker_id
);
1163 if (found
== tracker_by_id_
.end())
1166 FileTracker
* tracker
= found
->second
;
1167 if (dirty_trackers_
.erase(tracker
))
1168 low_priority_dirty_trackers_
.insert(tracker
);
1171 void MetadataDatabase::PromoteLowerPriorityTrackersToNormal() {
1172 if (dirty_trackers_
.empty()) {
1173 dirty_trackers_
.swap(low_priority_dirty_trackers_
);
1176 dirty_trackers_
.insert(low_priority_dirty_trackers_
.begin(),
1177 low_priority_dirty_trackers_
.end());
1178 low_priority_dirty_trackers_
.clear();
1181 bool MetadataDatabase::GetNormalPriorityDirtyTracker(
1182 FileTracker
* tracker
) const {
1183 DirtyTrackers::const_iterator itr
= dirty_trackers_
.begin();
1184 if (itr
== dirty_trackers_
.end())
1191 bool MetadataDatabase::GetLowPriorityDirtyTracker(
1192 FileTracker
* tracker
) const {
1193 DirtyTrackers::const_iterator itr
= low_priority_dirty_trackers_
.begin();
1194 if (itr
== low_priority_dirty_trackers_
.end())
1201 bool MetadataDatabase::GetMultiParentFileTrackers(std::string
* file_id
,
1202 TrackerSet
* trackers
) {
1205 // TODO(tzik): Make this function more efficient.
1206 for (TrackersByFileID::const_iterator itr
= trackers_by_file_id_
.begin();
1207 itr
!= trackers_by_file_id_
.end(); ++itr
) {
1208 if (itr
->second
.size() > 1 && itr
->second
.has_active()) {
1209 *file_id
= itr
->first
;
1210 *trackers
= itr
->second
;
1217 bool MetadataDatabase::GetConflictingTrackers(TrackerSet
* trackers
) {
1219 // TODO(tzik): Make this function more efficient.
1220 for (TrackersByParentAndTitle::const_iterator parent_itr
=
1221 trackers_by_parent_and_title_
.begin();
1222 parent_itr
!= trackers_by_parent_and_title_
.end();
1224 const TrackersByTitle
& trackers_by_title
= parent_itr
->second
;
1225 for (TrackersByTitle::const_iterator itr
= trackers_by_title
.begin();
1226 itr
!= trackers_by_title
.end();
1228 if (itr
->second
.size() > 1 && itr
->second
.has_active()) {
1229 *trackers
= itr
->second
;
1237 void MetadataDatabase::GetRegisteredAppIDs(std::vector
<std::string
>* app_ids
) {
1240 app_ids
->reserve(app_root_by_app_id_
.size());
1241 for (TrackerByAppID::iterator itr
= app_root_by_app_id_
.begin();
1242 itr
!= app_root_by_app_id_
.end(); ++itr
) {
1243 app_ids
->push_back(itr
->first
);
1247 MetadataDatabase::MetadataDatabase(base::SequencedTaskRunner
* task_runner
,
1248 const base::FilePath
& database_path
)
1249 : task_runner_(task_runner
),
1250 database_path_(database_path
),
1251 largest_known_change_id_(0),
1252 weak_ptr_factory_(this) {
1253 DCHECK(task_runner
);
1257 void MetadataDatabase::CreateOnTaskRunner(
1258 base::SingleThreadTaskRunner
* callback_runner
,
1259 base::SequencedTaskRunner
* task_runner
,
1260 const base::FilePath
& database_path
,
1261 const CreateCallback
& callback
) {
1262 scoped_ptr
<MetadataDatabase
> metadata_database(
1263 new MetadataDatabase(task_runner
, database_path
));
1264 SyncStatusCode status
=
1265 metadata_database
->InitializeOnTaskRunner();
1266 if (status
!= SYNC_STATUS_OK
)
1267 metadata_database
.reset();
1269 callback_runner
->PostTask(FROM_HERE
, base::Bind(
1270 callback
, status
, base::Passed(&metadata_database
)));
1274 SyncStatusCode
MetadataDatabase::CreateForTesting(
1275 scoped_ptr
<leveldb::DB
> db
,
1276 scoped_ptr
<MetadataDatabase
>* metadata_database_out
) {
1277 scoped_ptr
<MetadataDatabase
> metadata_database(
1278 new MetadataDatabase(base::MessageLoopProxy::current(),
1280 metadata_database
->db_
= db
.Pass();
1281 SyncStatusCode status
=
1282 metadata_database
->InitializeOnTaskRunner();
1283 if (status
== SYNC_STATUS_OK
)
1284 *metadata_database_out
= metadata_database
.Pass();
1288 SyncStatusCode
MetadataDatabase::InitializeOnTaskRunner() {
1289 base::ThreadRestrictions::AssertIOAllowed();
1290 DCHECK(task_runner_
->RunsTasksOnCurrentThread());
1292 SyncStatusCode status
= SYNC_STATUS_UNKNOWN
;
1293 bool created
= false;
1294 // Open database unless |db_| is overridden for testing.
1296 status
= OpenDatabase(database_path_
, &db_
, &created
);
1297 if (status
!= SYNC_STATUS_OK
)
1302 status
= WriteVersionInfo(db_
.get());
1303 if (status
!= SYNC_STATUS_OK
)
1306 status
= MigrateDatabaseIfNeeded(db_
.get());
1307 if (status
!= SYNC_STATUS_OK
)
1311 DatabaseContents contents
;
1312 status
= ReadDatabaseContents(db_
.get(), &contents
);
1313 if (status
!= SYNC_STATUS_OK
)
1316 leveldb::WriteBatch batch
;
1317 status
= InitializeServiceMetadata(&contents
, &batch
);
1318 if (status
!= SYNC_STATUS_OK
)
1321 status
= RemoveUnreachableItems(&contents
, &batch
);
1322 if (status
!= SYNC_STATUS_OK
)
1325 status
= LevelDBStatusToSyncStatusCode(
1326 db_
->Write(leveldb::WriteOptions(), &batch
));
1327 if (status
!= SYNC_STATUS_OK
)
1330 BuildIndexes(&contents
);
1334 void MetadataDatabase::BuildIndexes(DatabaseContents
* contents
) {
1335 service_metadata_
= contents
->service_metadata
.Pass();
1336 UpdateLargestKnownChangeID(service_metadata_
->largest_change_id());
1338 for (ScopedVector
<FileMetadata
>::const_iterator itr
=
1339 contents
->file_metadata
.begin();
1340 itr
!= contents
->file_metadata
.end();
1342 file_by_id_
[(*itr
)->file_id()] = *itr
;
1344 contents
->file_metadata
.weak_clear();
1346 for (ScopedVector
<FileTracker
>::const_iterator itr
=
1347 contents
->file_trackers
.begin();
1348 itr
!= contents
->file_trackers
.end();
1350 FileTracker
* tracker
= *itr
;
1351 tracker_by_id_
[tracker
->tracker_id()] = tracker
;
1352 trackers_by_file_id_
[tracker
->file_id()].Insert(tracker
);
1354 if (IsAppRoot(*tracker
))
1355 app_root_by_app_id_
[tracker
->app_id()] = tracker
;
1357 if (tracker
->parent_tracker_id()) {
1358 std::string title
= GetTrackerTitle(*tracker
);
1359 TrackerSet
* trackers
=
1360 &trackers_by_parent_and_title_
[tracker
->parent_tracker_id()][title
];
1361 trackers
->Insert(tracker
);
1364 if (tracker
->dirty())
1365 dirty_trackers_
.insert(tracker
);
1367 contents
->file_trackers
.weak_clear();
1370 void MetadataDatabase::RegisterTrackerAsAppRoot(
1371 const std::string
& app_id
,
1373 leveldb::WriteBatch
* batch
) {
1374 FileTracker
* tracker
= tracker_by_id_
[tracker_id
];
1376 tracker
->set_app_id(app_id
);
1377 tracker
->set_tracker_kind(TRACKER_KIND_APP_ROOT
);
1378 app_root_by_app_id_
[app_id
] = tracker
;
1380 MakeTrackerActive(tracker
->tracker_id(), batch
);
1383 void MetadataDatabase::UnregisterTrackerAsAppRoot(
1384 const std::string
& app_id
,
1385 leveldb::WriteBatch
* batch
) {
1386 FileTracker
* tracker
= FindAndEraseItem(&app_root_by_app_id_
, app_id
);
1387 tracker
->set_app_id(std::string());
1388 tracker
->set_tracker_kind(TRACKER_KIND_REGULAR
);
1390 // Inactivate the tracker to drop all descendant.
1391 // (Note that we set tracker_kind to TRACKER_KIND_REGULAR before calling
1393 MakeTrackerInactive(tracker
->tracker_id(), batch
);
1396 void MetadataDatabase::MakeTrackerActive(int64 tracker_id
,
1397 leveldb::WriteBatch
* batch
) {
1398 FileTracker
* tracker
= tracker_by_id_
[tracker_id
];
1400 DCHECK(!tracker
->active());
1402 int64 parent_tracker_id
= tracker
->parent_tracker_id();
1403 DCHECK(tracker
->has_synced_details());
1404 trackers_by_file_id_
[tracker
->file_id()].Activate(tracker
);
1405 if (parent_tracker_id
) {
1406 trackers_by_parent_and_title_
[parent_tracker_id
][
1407 tracker
->synced_details().title()].Activate(tracker
);
1409 tracker
->set_active(true);
1410 tracker
->set_needs_folder_listing(
1411 tracker
->synced_details().file_kind() == FILE_KIND_FOLDER
);
1413 // Make |tracker| a normal priority dirty tracker.
1414 if (tracker
->dirty())
1415 low_priority_dirty_trackers_
.erase(tracker
);
1416 tracker
->set_dirty(true);
1417 dirty_trackers_
.insert(tracker
);
1419 PutTrackerToBatch(*tracker
, batch
);
1422 void MetadataDatabase::MakeTrackerInactive(int64 tracker_id
,
1423 leveldb::WriteBatch
* batch
) {
1424 FileTracker
* tracker
= tracker_by_id_
[tracker_id
];
1426 DCHECK(tracker
->active());
1427 DCHECK_EQ(TRACKER_KIND_REGULAR
, tracker
->tracker_kind());
1428 trackers_by_file_id_
[tracker
->file_id()].Inactivate(tracker
);
1430 std::string title
= GetTrackerTitle(*tracker
);
1431 int64 parent_tracker_id
= tracker
->parent_tracker_id();
1432 if (parent_tracker_id
)
1433 trackers_by_parent_and_title_
[parent_tracker_id
][title
].Inactivate(tracker
);
1434 tracker
->set_active(false);
1436 RemoveAllDescendantTrackers(tracker_id
, batch
);
1437 MarkTrackersDirtyByFileID(tracker
->file_id(), batch
);
1438 if (parent_tracker_id
)
1439 MarkTrackersDirtyByPath(parent_tracker_id
, title
, batch
);
1440 PutTrackerToBatch(*tracker
, batch
);
1443 void MetadataDatabase::MakeAppRootDisabled(int64 tracker_id
,
1444 leveldb::WriteBatch
* batch
) {
1445 FileTracker
* tracker
= tracker_by_id_
[tracker_id
];
1447 DCHECK_EQ(TRACKER_KIND_APP_ROOT
, tracker
->tracker_kind());
1448 DCHECK(tracker
->active());
1450 // Keep the app-root tracker active (but change the tracker_kind) so that
1451 // other conflicting trackers won't become active.
1452 tracker
->set_tracker_kind(TRACKER_KIND_DISABLED_APP_ROOT
);
1453 PutTrackerToBatch(*tracker
, batch
);
1456 void MetadataDatabase::MakeAppRootEnabled(int64 tracker_id
,
1457 leveldb::WriteBatch
* batch
) {
1458 FileTracker
* tracker
= tracker_by_id_
[tracker_id
];
1460 DCHECK_EQ(TRACKER_KIND_DISABLED_APP_ROOT
, tracker
->tracker_kind());
1461 DCHECK(tracker
->active());
1463 tracker
->set_tracker_kind(TRACKER_KIND_APP_ROOT
);
1464 // Mark descendant trackers as dirty to handle changes in disable period.
1465 RecursiveMarkTrackerAsDirty(tracker_id
, batch
);
1466 PutTrackerToBatch(*tracker
, batch
);
1469 void MetadataDatabase::CreateTrackerForParentAndFileID(
1470 const FileTracker
& parent_tracker
,
1471 const std::string
& file_id
,
1472 leveldb::WriteBatch
* batch
) {
1473 CreateTrackerInternal(parent_tracker
, file_id
, NULL
, batch
);
1476 void MetadataDatabase::CreateTrackerForParentAndFileMetadata(
1477 const FileTracker
& parent_tracker
,
1478 const FileMetadata
& file_metadata
,
1479 leveldb::WriteBatch
* batch
) {
1480 DCHECK(file_metadata
.has_details());
1481 CreateTrackerInternal(parent_tracker
,
1482 file_metadata
.file_id(),
1483 &file_metadata
.details(),
1487 void MetadataDatabase::CreateTrackerInternal(const FileTracker
& parent_tracker
,
1488 const std::string
& file_id
,
1489 const FileDetails
* details
,
1490 leveldb::WriteBatch
* batch
) {
1491 int64 tracker_id
= GetNextTrackerID(batch
);
1492 scoped_ptr
<FileTracker
> tracker(new FileTracker
);
1493 tracker
->set_tracker_id(tracker_id
);
1494 tracker
->set_parent_tracker_id(parent_tracker
.tracker_id());
1495 tracker
->set_file_id(file_id
);
1496 tracker
->set_app_id(parent_tracker
.app_id());
1497 tracker
->set_tracker_kind(TRACKER_KIND_REGULAR
);
1498 tracker
->set_dirty(true);
1499 tracker
->set_active(false);
1500 tracker
->set_needs_folder_listing(false);
1502 *tracker
->mutable_synced_details() = *details
;
1503 tracker
->mutable_synced_details()->set_missing(true);
1504 tracker
->mutable_synced_details()->clear_md5();
1506 PutTrackerToBatch(*tracker
, batch
);
1508 trackers_by_file_id_
[file_id
].Insert(tracker
.get());
1509 // Note: |trackers_by_parent_and_title_| does not map from
1510 // FileMetadata::details but from FileTracker::synced_details, which is filled
1511 // on tracker updated phase. Use empty string as the title since
1512 // FileTracker::synced_details is empty here.
1515 title
= details
->title();
1516 trackers_by_parent_and_title_
[parent_tracker
.tracker_id()][title
]
1517 .Insert(tracker
.get());
1518 dirty_trackers_
.insert(tracker
.get());
1519 DCHECK(!ContainsKey(tracker_by_id_
, tracker_id
));
1520 tracker_by_id_
[tracker_id
] = tracker
.release();
1523 void MetadataDatabase::RemoveTracker(int64 tracker_id
,
1524 leveldb::WriteBatch
* batch
) {
1525 RemoveTrackerInternal(tracker_id
, batch
, false);
1526 RemoveAllDescendantTrackers(tracker_id
, batch
);
1529 void MetadataDatabase::RemoveTrackerIgnoringSameTitle(
1531 leveldb::WriteBatch
* batch
) {
1532 RemoveTrackerInternal(tracker_id
, batch
, true);
1535 void MetadataDatabase::RemoveTrackerInternal(
1537 leveldb::WriteBatch
* batch
,
1538 bool ignoring_same_title
) {
1539 scoped_ptr
<FileTracker
> tracker(
1540 FindAndEraseItem(&tracker_by_id_
, tracker_id
));
1544 EraseTrackerFromFileIDIndex(tracker
.get(), batch
);
1545 if (IsAppRoot(*tracker
))
1546 app_root_by_app_id_
.erase(tracker
->app_id());
1547 EraseTrackerFromPathIndex(tracker
.get());
1548 dirty_trackers_
.erase(tracker
.get());
1549 low_priority_dirty_trackers_
.erase(tracker
.get());
1551 MarkTrackersDirtyByFileID(tracker
->file_id(), batch
);
1552 if (!ignoring_same_title
) {
1553 // Mark trackers having the same title with the given tracker as dirty.
1554 MarkTrackersDirtyByPath(tracker
->parent_tracker_id(),
1555 GetTrackerTitle(*tracker
),
1558 PutTrackerDeletionToBatch(tracker_id
, batch
);
1561 void MetadataDatabase::MaybeAddTrackersForNewFile(
1562 const FileMetadata
& file
,
1563 leveldb::WriteBatch
* batch
) {
1564 std::set
<int64
> parents_to_exclude
;
1565 TrackersByFileID::iterator found
= trackers_by_file_id_
.find(file
.file_id());
1566 if (found
!= trackers_by_file_id_
.end()) {
1567 for (TrackerSet::const_iterator itr
= found
->second
.begin();
1568 itr
!= found
->second
.end(); ++itr
) {
1569 const FileTracker
& tracker
= **itr
;
1570 int64 parent_tracker_id
= tracker
.parent_tracker_id();
1571 if (!parent_tracker_id
)
1574 // Exclude |parent_tracker_id| if it already has a tracker that has
1575 // unknown title or has the same title with |file|.
1576 if (!tracker
.has_synced_details() ||
1577 tracker
.synced_details().title() == file
.details().title()) {
1578 parents_to_exclude
.insert(parent_tracker_id
);
1583 for (int i
= 0; i
< file
.details().parent_folder_ids_size(); ++i
) {
1584 std::string parent_folder_id
= file
.details().parent_folder_ids(i
);
1585 TrackersByFileID::iterator found
=
1586 trackers_by_file_id_
.find(parent_folder_id
);
1587 if (found
== trackers_by_file_id_
.end())
1590 for (TrackerSet::const_iterator itr
= found
->second
.begin();
1591 itr
!= found
->second
.end(); ++itr
) {
1592 FileTracker
* parent_tracker
= *itr
;
1593 int64 parent_tracker_id
= parent_tracker
->tracker_id();
1594 if (!parent_tracker
->active())
1597 if (ContainsKey(parents_to_exclude
, parent_tracker_id
))
1600 CreateTrackerForParentAndFileMetadata(*parent_tracker
, file
, batch
);
1605 void MetadataDatabase::RemoveAllDescendantTrackers(int64 root_tracker_id
,
1606 leveldb::WriteBatch
* batch
) {
1607 std::vector
<int64
> pending_trackers
;
1608 PushChildTrackersToContainer(trackers_by_parent_and_title_
,
1610 std::back_inserter(pending_trackers
));
1612 while (!pending_trackers
.empty()) {
1613 int64 tracker_id
= pending_trackers
.back();
1614 pending_trackers
.pop_back();
1615 PushChildTrackersToContainer(trackers_by_parent_and_title_
,
1617 std::back_inserter(pending_trackers
));
1618 RemoveTrackerIgnoringSameTitle(tracker_id
, batch
);
1622 void MetadataDatabase::EraseTrackerFromFileIDIndex(FileTracker
* tracker
,
1623 leveldb::WriteBatch
* batch
) {
1624 TrackersByFileID::iterator found
=
1625 trackers_by_file_id_
.find(tracker
->file_id());
1626 if (found
== trackers_by_file_id_
.end())
1629 TrackerSet
* trackers
= &found
->second
;
1630 trackers
->Erase(tracker
);
1631 if (!trackers
->tracker_set().empty())
1633 trackers_by_file_id_
.erase(found
);
1634 EraseFileFromDatabase(tracker
->file_id(), batch
);
1637 void MetadataDatabase::EraseFileFromDatabase(const std::string
& file_id
,
1638 leveldb::WriteBatch
* batch
) {
1639 scoped_ptr
<FileMetadata
> file(FindAndEraseItem(&file_by_id_
, file_id
));
1641 PutFileDeletionToBatch(file_id
, batch
);
1644 void MetadataDatabase::EraseTrackerFromPathIndex(FileTracker
* tracker
) {
1645 TrackersByParentAndTitle::iterator found
=
1646 trackers_by_parent_and_title_
.find(tracker
->parent_tracker_id());
1647 if (found
== trackers_by_parent_and_title_
.end())
1650 std::string title
= GetTrackerTitle(*tracker
);
1651 TrackersByTitle
* trackers_by_title
= &found
->second
;
1652 TrackersByTitle::iterator found_by_title
= trackers_by_title
->find(title
);
1653 TrackerSet
* conflicting_trackers
= &found_by_title
->second
;
1654 conflicting_trackers
->Erase(tracker
);
1656 if (conflicting_trackers
->tracker_set().empty()) {
1657 trackers_by_title
->erase(found_by_title
);
1658 if (trackers_by_title
->empty())
1659 trackers_by_parent_and_title_
.erase(found
);
1663 void MetadataDatabase::MarkSingleTrackerDirty(FileTracker
* tracker
,
1664 leveldb::WriteBatch
* batch
) {
1665 if (!tracker
->dirty()) {
1666 tracker
->set_dirty(true);
1667 PutTrackerToBatch(*tracker
, batch
);
1669 dirty_trackers_
.insert(tracker
);
1670 low_priority_dirty_trackers_
.erase(tracker
);
1673 void MetadataDatabase::MarkTrackerSetDirty(
1674 TrackerSet
* trackers
,
1675 leveldb::WriteBatch
* batch
) {
1676 for (TrackerSet::iterator itr
= trackers
->begin();
1677 itr
!= trackers
->end(); ++itr
) {
1678 MarkSingleTrackerDirty(*itr
, batch
);
1682 void MetadataDatabase::MarkTrackersDirtyByFileID(
1683 const std::string
& file_id
,
1684 leveldb::WriteBatch
* batch
) {
1685 TrackersByFileID::iterator found
= trackers_by_file_id_
.find(file_id
);
1686 if (found
!= trackers_by_file_id_
.end())
1687 MarkTrackerSetDirty(&found
->second
, batch
);
1690 void MetadataDatabase::MarkTrackersDirtyByPath(int64 parent_tracker_id
,
1691 const std::string
& title
,
1692 leveldb::WriteBatch
* batch
) {
1693 TrackersByParentAndTitle::iterator found
=
1694 trackers_by_parent_and_title_
.find(parent_tracker_id
);
1695 if (found
== trackers_by_parent_and_title_
.end())
1698 TrackersByTitle::iterator itr
= found
->second
.find(title
);
1699 if (itr
!= found
->second
.end())
1700 MarkTrackerSetDirty(&itr
->second
, batch
);
1703 int64
MetadataDatabase::GetNextTrackerID(leveldb::WriteBatch
* batch
) {
1704 int64 tracker_id
= service_metadata_
->next_tracker_id();
1705 service_metadata_
->set_next_tracker_id(tracker_id
+ 1);
1706 PutServiceMetadataToBatch(*service_metadata_
, batch
);
1707 DCHECK_GT(tracker_id
, 0);
1711 void MetadataDatabase::RecursiveMarkTrackerAsDirty(int64 root_tracker_id
,
1712 leveldb::WriteBatch
* batch
) {
1713 std::vector
<int64
> stack
;
1714 stack
.push_back(root_tracker_id
);
1715 while (!stack
.empty()) {
1716 int64 tracker_id
= stack
.back();
1718 PushChildTrackersToContainer(
1719 trackers_by_parent_and_title_
, tracker_id
, std::back_inserter(stack
));
1721 FileTracker
* tracker
= tracker_by_id_
[tracker_id
];
1722 if (!tracker
->dirty()) {
1723 tracker
->set_dirty(true);
1724 PutTrackerToBatch(*tracker
, batch
);
1725 dirty_trackers_
.insert(tracker
);
1726 low_priority_dirty_trackers_
.erase(tracker
);
1731 bool MetadataDatabase::CanActivateTracker(const FileTracker
& tracker
) {
1732 DCHECK(!tracker
.active());
1733 DCHECK_NE(service_metadata_
->sync_root_tracker_id(), tracker
.tracker_id());
1735 if (HasActiveTrackerForFileID(tracker
.file_id()))
1738 if (tracker
.app_id().empty() &&
1739 tracker
.tracker_id() != GetSyncRootTrackerID()) {
1743 if (!tracker
.has_synced_details())
1745 if (tracker
.synced_details().file_kind() == FILE_KIND_UNSUPPORTED
)
1747 if (HasInvalidTitle(tracker
.synced_details().title()))
1749 DCHECK(tracker
.parent_tracker_id());
1751 return !HasActiveTrackerForPath(tracker
.parent_tracker_id(),
1752 tracker
.synced_details().title());
1755 bool MetadataDatabase::ShouldKeepDirty(const FileTracker
& tracker
) const {
1756 if (HasDisabledAppRoot(tracker
))
1759 DCHECK(tracker
.dirty());
1760 if (!tracker
.has_synced_details())
1763 FileByID::const_iterator found
= file_by_id_
.find(tracker
.file_id());
1764 if (found
== file_by_id_
.end())
1766 const FileMetadata
* file
= found
->second
;
1768 DCHECK(file
->has_details());
1770 const FileDetails
& local_details
= tracker
.synced_details();
1771 const FileDetails
& remote_details
= file
->details();
1773 if (tracker
.active()) {
1774 if (tracker
.needs_folder_listing())
1776 if (tracker
.synced_details().md5() != file
->details().md5())
1778 if (local_details
.missing() != remote_details
.missing())
1782 if (local_details
.title() != remote_details
.title())
1788 bool MetadataDatabase::HasDisabledAppRoot(const FileTracker
& tracker
) const {
1789 TrackerByAppID::const_iterator found
=
1790 app_root_by_app_id_
.find(tracker
.app_id());
1791 if (found
== app_root_by_app_id_
.end())
1794 const FileTracker
* app_root_tracker
= found
->second
;
1795 DCHECK(app_root_tracker
);
1796 return app_root_tracker
->tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT
;
1799 bool MetadataDatabase::HasActiveTrackerForFileID(
1800 const std::string
& file_id
) const {
1801 TrackersByFileID::const_iterator found
= trackers_by_file_id_
.find(file_id
);
1802 return found
!= trackers_by_file_id_
.end() && found
->second
.has_active();
1805 bool MetadataDatabase::HasActiveTrackerForPath(int64 parent_tracker_id
,
1806 const std::string
& title
) const {
1807 TrackersByParentAndTitle::const_iterator found_by_parent
=
1808 trackers_by_parent_and_title_
.find(parent_tracker_id
);
1809 if (found_by_parent
== trackers_by_parent_and_title_
.end())
1812 const TrackersByTitle
& trackers_by_title
= found_by_parent
->second
;
1813 TrackersByTitle::const_iterator found
= trackers_by_title
.find(title
);
1814 return found
!= trackers_by_title
.end() && found
->second
.has_active();
1817 void MetadataDatabase::UpdateByFileMetadata(
1818 const tracked_objects::Location
& from_where
,
1819 scoped_ptr
<FileMetadata
> file
,
1820 leveldb::WriteBatch
* batch
) {
1822 DCHECK(file
->has_details());
1824 DVLOG(1) << from_where
.function_name() << ": "
1825 << file
->file_id() << " ("
1826 << file
->details().title() << ")"
1827 << (file
->details().missing() ? " deleted" : "");
1829 std::string file_id
= file
->file_id();
1830 if (file
->details().missing()) {
1831 TrackerSet trackers
;
1832 FindTrackersByFileID(file_id
, &trackers
);
1833 for (TrackerSet::const_iterator itr
= trackers
.begin();
1834 itr
!= trackers
.end(); ++itr
) {
1835 const FileTracker
& tracker
= **itr
;
1836 if (!tracker
.has_synced_details() ||
1837 tracker
.synced_details().missing()) {
1838 RemoveTracker(tracker
.tracker_id(), batch
);
1842 MaybeAddTrackersForNewFile(*file
, batch
);
1845 if (FindTrackersByFileID(file_id
, NULL
)) {
1846 MarkTrackersDirtyByFileID(file_id
, batch
);
1847 PutFileToBatch(*file
, batch
);
1848 FileMetadata
* file_ptr
= file
.release();
1849 std::swap(file_ptr
, file_by_id_
[file_id
]);
1854 void MetadataDatabase::WriteToDatabase(scoped_ptr
<leveldb::WriteBatch
> batch
,
1855 const SyncStatusCallback
& callback
) {
1856 base::PostTaskAndReplyWithResult(
1859 base::Bind(&leveldb::DB::Write
,
1860 base::Unretained(db_
.get()),
1861 leveldb::WriteOptions(),
1862 base::Owned(batch
.release())),
1863 base::Bind(&AdaptLevelDBStatusToSyncStatusCode
, callback
));
1866 scoped_ptr
<base::ListValue
> MetadataDatabase::DumpFiles(
1867 const std::string
& app_id
) {
1868 scoped_ptr
<base::ListValue
> files(new base::ListValue
);
1870 FileTracker app_root_tracker
;
1871 if (!FindAppRootTracker(app_id
, &app_root_tracker
))
1872 return files
.Pass();
1874 std::vector
<int64
> stack
;
1875 PushChildTrackersToContainer(
1876 trackers_by_parent_and_title_
,
1877 app_root_tracker
.tracker_id(),
1878 std::back_inserter(stack
));
1879 while (!stack
.empty()) {
1880 int64 tracker_id
= stack
.back();
1882 PushChildTrackersToContainer(
1883 trackers_by_parent_and_title_
, tracker_id
, std::back_inserter(stack
));
1885 FileTracker
* tracker
= tracker_by_id_
[tracker_id
];
1886 base::DictionaryValue
* file
= new base::DictionaryValue
;
1888 base::FilePath path
= BuildDisplayPathForTracker(*tracker
);
1889 file
->SetString("path", path
.AsUTF8Unsafe());
1890 if (tracker
->has_synced_details()) {
1891 file
->SetString("title", tracker
->synced_details().title());
1892 file
->SetString("type",
1893 FileKindToString(tracker
->synced_details().file_kind()));
1896 base::DictionaryValue
* details
= new base::DictionaryValue
;
1897 details
->SetString("file_id", tracker
->file_id());
1898 if (tracker
->has_synced_details() &&
1899 tracker
->synced_details().file_kind() == FILE_KIND_FILE
)
1900 details
->SetString("md5", tracker
->synced_details().md5());
1901 details
->SetString("active", tracker
->active() ? "true" : "false");
1902 details
->SetString("dirty", tracker
->dirty() ? "true" : "false");
1904 file
->Set("details", details
);
1906 files
->Append(file
);
1909 return files
.Pass();
1912 scoped_ptr
<base::ListValue
> MetadataDatabase::DumpDatabase() {
1913 scoped_ptr
<base::ListValue
> list(new base::ListValue
);
1914 list
->Append(DumpTrackers().release());
1915 list
->Append(DumpMetadata().release());
1919 bool MetadataDatabase::HasNewerFileMetadata(const std::string
& file_id
,
1921 FileByID::const_iterator found
= file_by_id_
.find(file_id
);
1922 if (found
== file_by_id_
.end())
1924 DCHECK(found
->second
->has_details());
1925 return found
->second
->details().change_id() >= change_id
;
1928 scoped_ptr
<base::ListValue
> MetadataDatabase::DumpTrackers() {
1929 scoped_ptr
<base::ListValue
> trackers(new base::ListValue
);
1931 // Append the first element for metadata.
1932 base::DictionaryValue
* metadata
= new base::DictionaryValue
;
1933 const char *trackerKeys
[] = {
1934 "tracker_id", "path", "file_id", "tracker_kind", "app_id",
1935 "active", "dirty", "folder_listing",
1936 "title", "kind", "md5", "etag", "missing", "change_id",
1938 std::vector
<std::string
> key_strings(
1939 trackerKeys
, trackerKeys
+ ARRAYSIZE_UNSAFE(trackerKeys
));
1940 base::ListValue
* keys
= new base::ListValue
;
1941 keys
->AppendStrings(key_strings
);
1942 metadata
->SetString("title", "Trackers");
1943 metadata
->Set("keys", keys
);
1944 trackers
->Append(metadata
);
1946 // Append tracker data.
1947 for (TrackerByID::const_iterator itr
= tracker_by_id_
.begin();
1948 itr
!= tracker_by_id_
.end(); ++itr
) {
1949 const FileTracker
& tracker
= *itr
->second
;
1950 base::DictionaryValue
* dict
= new base::DictionaryValue
;
1951 base::FilePath path
= BuildDisplayPathForTracker(tracker
);
1952 dict
->SetString("tracker_id", base::Int64ToString(tracker
.tracker_id()));
1953 dict
->SetString("path", path
.AsUTF8Unsafe());
1954 dict
->SetString("file_id", tracker
.file_id());
1955 TrackerKind tracker_kind
= tracker
.tracker_kind();
1958 tracker_kind
== TRACKER_KIND_APP_ROOT
? "AppRoot" :
1959 tracker_kind
== TRACKER_KIND_DISABLED_APP_ROOT
? "Disabled App" :
1960 tracker
.tracker_id() == GetSyncRootTrackerID() ? "SyncRoot" :
1962 dict
->SetString("app_id", tracker
.app_id());
1963 dict
->SetString("active", tracker
.active() ? "true" : "false");
1964 dict
->SetString("dirty", tracker
.dirty() ? "true" : "false");
1965 dict
->SetString("folder_listing",
1966 tracker
.needs_folder_listing() ? "needed" : "no");
1967 if (tracker
.has_synced_details()) {
1968 const FileDetails
& details
= tracker
.synced_details();
1969 dict
->SetString("title", details
.title());
1970 dict
->SetString("kind", FileKindToString(details
.file_kind()));
1971 dict
->SetString("md5", details
.md5());
1972 dict
->SetString("etag", details
.etag());
1973 dict
->SetString("missing", details
.missing() ? "true" : "false");
1974 dict
->SetString("change_id", base::Int64ToString(details
.change_id()));
1976 trackers
->Append(dict
);
1978 return trackers
.Pass();
1981 scoped_ptr
<base::ListValue
> MetadataDatabase::DumpMetadata() {
1982 scoped_ptr
<base::ListValue
> files(new base::ListValue
);
1984 // Append the first element for metadata.
1985 base::DictionaryValue
* metadata
= new base::DictionaryValue
;
1986 const char *fileKeys
[] = {
1987 "file_id", "title", "type", "md5", "etag", "missing",
1988 "change_id", "parents"
1990 std::vector
<std::string
> key_strings(
1991 fileKeys
, fileKeys
+ ARRAYSIZE_UNSAFE(fileKeys
));
1992 base::ListValue
* keys
= new base::ListValue
;
1993 keys
->AppendStrings(key_strings
);
1994 metadata
->SetString("title", "Metadata");
1995 metadata
->Set("keys", keys
);
1996 files
->Append(metadata
);
1998 // Append metadata data.
1999 for (FileByID::const_iterator itr
= file_by_id_
.begin();
2000 itr
!= file_by_id_
.end(); ++itr
) {
2001 const FileMetadata
& file
= *itr
->second
;
2003 base::DictionaryValue
* dict
= new base::DictionaryValue
;
2004 dict
->SetString("file_id", file
.file_id());
2005 if (file
.has_details()) {
2006 const FileDetails
& details
= file
.details();
2007 dict
->SetString("title", details
.title());
2008 dict
->SetString("type", FileKindToString(details
.file_kind()));
2009 dict
->SetString("md5", details
.md5());
2010 dict
->SetString("etag", details
.etag());
2011 dict
->SetString("missing", details
.missing() ? "true" : "false");
2012 dict
->SetString("change_id", base::Int64ToString(details
.change_id()));
2014 std::vector
<std::string
> parents
;
2015 for (int i
= 0; i
< details
.parent_folder_ids_size(); ++i
)
2016 parents
.push_back(details
.parent_folder_ids(i
));
2017 dict
->SetString("parents", JoinString(parents
, ","));
2019 files
->Append(dict
);
2021 return files
.Pass();
2024 } // namespace drive_backend
2025 } // namespace sync_file_system