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/remote_to_local_syncer.h"
8 #include "base/callback.h"
9 #include "base/files/file_util.h"
10 #include "base/format_macros.h"
11 #include "base/location.h"
12 #include "base/logging.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/task_runner_util.h"
15 #include "chrome/browser/sync_file_system/drive_backend/callback_helper.h"
16 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_util.h"
17 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.h"
18 #include "chrome/browser/sync_file_system/drive_backend/sync_engine_context.h"
19 #include "chrome/browser/sync_file_system/drive_backend/sync_task_manager.h"
20 #include "chrome/browser/sync_file_system/drive_backend/sync_task_token.h"
21 #include "chrome/browser/sync_file_system/drive_backend/task_dependency_manager.h"
22 #include "chrome/browser/sync_file_system/logger.h"
23 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
24 #include "components/drive/drive_api_util.h"
25 #include "components/drive/service/drive_service_interface.h"
26 #include "extensions/common/extension.h"
27 #include "google_apis/drive/drive_api_parser.h"
28 #include "storage/common/fileapi/file_system_util.h"
30 namespace sync_file_system
{
31 namespace drive_backend
{
35 bool BuildFileSystemURL(MetadataDatabase
* metadata_database
,
36 const FileTracker
& tracker
,
37 storage::FileSystemURL
* url
) {
39 if (!metadata_database
->BuildPathForTracker(
40 tracker
.tracker_id(), &path
))
44 extensions::Extension::GetBaseURLFromExtensionId(tracker
.app_id());
45 *url
= sync_file_system::CreateSyncableFileSystemURL(origin
, path
);
50 bool HasFolderAsParent(const FileDetails
& details
,
51 const std::string
& folder_id
) {
52 for (int i
= 0; i
< details
.parent_folder_ids_size(); ++i
) {
53 if (details
.parent_folder_ids(i
) == folder_id
)
59 bool HasDisabledAppRoot(MetadataDatabase
* database
,
60 const FileTracker
& tracker
) {
61 DCHECK(tracker
.active());
62 FileTracker app_root_tracker
;
63 if (database
->FindAppRootTracker(tracker
.app_id(), &app_root_tracker
)) {
64 DCHECK(app_root_tracker
.tracker_kind() == TRACKER_KIND_APP_ROOT
||
65 app_root_tracker
.tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT
);
66 return app_root_tracker
.tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT
;
71 scoped_ptr
<FileMetadata
> GetFileMetadata(MetadataDatabase
* database
,
72 const std::string
& file_id
) {
73 scoped_ptr
<FileMetadata
> metadata(new FileMetadata
);
74 if (!database
->FindFileByFileID(file_id
, metadata
.get()))
76 return metadata
.Pass();
79 // Creates a temporary file in |dir_path|. This must be called on an
80 // IO-allowed task runner, and the runner must be given as |file_task_runner|.
81 storage::ScopedFile
CreateTemporaryFile(
82 const scoped_refptr
<base::TaskRunner
>& file_task_runner
) {
83 base::FilePath temp_file_path
;
84 if (!base::CreateTemporaryFile(&temp_file_path
))
85 return storage::ScopedFile();
87 return storage::ScopedFile(temp_file_path
,
88 storage::ScopedFile::DELETE_ON_SCOPE_OUT
,
94 RemoteToLocalSyncer::RemoteToLocalSyncer(SyncEngineContext
* sync_context
)
95 : sync_context_(sync_context
),
96 file_type_(SYNC_FILE_TYPE_UNKNOWN
),
97 sync_action_(SYNC_ACTION_NONE
),
99 sync_root_deletion_(false),
100 weak_ptr_factory_(this) {
103 RemoteToLocalSyncer::~RemoteToLocalSyncer() {
106 void RemoteToLocalSyncer::RunPreflight(scoped_ptr
<SyncTaskToken
> token
) {
107 token
->InitializeTaskLog("Remote -> Local");
109 if (!drive_service() || !metadata_database() || !remote_change_processor()) {
110 token
->RecordLog("Context not ready.");
111 SyncTaskManager::NotifyTaskDone(token
.Pass(), SYNC_STATUS_FAILED
);
115 dirty_tracker_
= make_scoped_ptr(new FileTracker
);
116 if (metadata_database()->GetDirtyTracker(dirty_tracker_
.get())) {
117 token
->RecordLog(base::StringPrintf(
118 "Start: tracker_id=%" PRId64
, dirty_tracker_
->tracker_id()));
119 metadata_database()->DemoteTracker(dirty_tracker_
->tracker_id());
120 ResolveRemoteChange(token
.Pass());
124 token
->RecordLog("Nothing to do.");
125 SyncTaskManager::NotifyTaskDone(token
.Pass(), SYNC_STATUS_NO_CHANGE_TO_SYNC
);
128 void RemoteToLocalSyncer::ResolveRemoteChange(scoped_ptr
<SyncTaskToken
> token
) {
129 DCHECK(dirty_tracker_
);
130 remote_metadata_
= GetFileMetadata(
131 metadata_database(), dirty_tracker_
->file_id());
133 if (!remote_metadata_
|| !remote_metadata_
->has_details()) {
134 if (remote_metadata_
&& !remote_metadata_
->has_details()) {
136 "Missing details of a remote file: " + remote_metadata_
->file_id());
139 token
->RecordLog("Missing remote metadata case.");
143 base::Bind(&RemoteToLocalSyncer::HandleMissingRemoteMetadata
,
144 weak_ptr_factory_
.GetWeakPtr()));
148 DCHECK(remote_metadata_
);
149 DCHECK(remote_metadata_
->has_details());
150 const FileDetails
& remote_details
= remote_metadata_
->details();
152 if (!dirty_tracker_
->active() ||
153 HasDisabledAppRoot(metadata_database(), *dirty_tracker_
)) {
154 // Handle inactive tracker in SyncCompleted.
155 token
->RecordLog("Inactive tracker case.");
156 SyncCompleted(token
.Pass(), SYNC_STATUS_OK
);
160 DCHECK(dirty_tracker_
->active());
161 DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_
));
163 if (!dirty_tracker_
->has_synced_details()) {
164 token
->RecordLog(base::StringPrintf(
165 "Missing synced_details of an active tracker: %" PRId64
,
166 dirty_tracker_
->tracker_id()));
168 SyncCompleted(token
.Pass(), SYNC_STATUS_FAILED
);
172 DCHECK(dirty_tracker_
->has_synced_details());
173 const FileDetails
& synced_details
= dirty_tracker_
->synced_details();
175 if (dirty_tracker_
->tracker_id() ==
176 metadata_database()->GetSyncRootTrackerID()) {
177 if (remote_details
.missing() ||
178 synced_details
.title() != remote_details
.title() ||
179 remote_details
.parent_folder_ids_size()) {
180 token
->RecordLog("Sync-root deletion.");
181 sync_root_deletion_
= true;
182 SyncCompleted(token
.Pass(), SYNC_STATUS_OK
);
185 token
->RecordLog("Trivial sync-root change.");
186 SyncCompleted(token
.Pass(), SYNC_STATUS_OK
);
190 DCHECK_NE(dirty_tracker_
->tracker_id(),
191 metadata_database()->GetSyncRootTrackerID());
193 if (!BuildFileSystemURL(metadata_database(), *dirty_tracker_
, &url_
)) {
195 SyncCompleted(token
.Pass(), SYNC_STATUS_FAILED
);
199 DCHECK(url_
.is_valid());
201 if (remote_details
.missing()) {
202 if (!synced_details
.missing()) {
203 token
->RecordLog("Remote file deletion.");
204 MoveToBackground(token
.Pass(),
205 base::Bind(&RemoteToLocalSyncer::HandleDeletion
,
206 weak_ptr_factory_
.GetWeakPtr()));
210 DCHECK(synced_details
.missing());
211 token
->RecordLog("Found a stray missing tracker: " +
212 dirty_tracker_
->file_id());
214 SyncCompleted(token
.Pass(), SYNC_STATUS_OK
);
218 // Most of remote_details field is valid from here.
219 DCHECK(!remote_details
.missing());
221 if (synced_details
.file_kind() != remote_details
.file_kind()) {
222 token
->RecordLog(base::StringPrintf(
223 "Found type mismatch between remote and local file: %s"
224 " type: (local) %d vs (remote) %d",
225 dirty_tracker_
->file_id().c_str(),
226 synced_details
.file_kind(),
227 remote_details
.file_kind()));
229 SyncCompleted(token
.Pass(), SYNC_STATUS_FAILED
);
232 DCHECK_EQ(synced_details
.file_kind(), remote_details
.file_kind());
234 if (synced_details
.file_kind() == FILE_KIND_UNSUPPORTED
) {
235 token
->RecordLog("Found an unsupported active file: " +
236 remote_metadata_
->file_id());
238 SyncCompleted(token
.Pass(), SYNC_STATUS_FAILED
);
241 DCHECK(remote_details
.file_kind() == FILE_KIND_FILE
||
242 remote_details
.file_kind() == FILE_KIND_FOLDER
);
244 if (synced_details
.title() != remote_details
.title()) {
245 // Handle rename as deletion + addition.
246 token
->RecordLog("Detected file rename.");
248 MoveToBackground(token
.Pass(),
249 base::Bind(&RemoteToLocalSyncer::HandleFileMove
,
250 weak_ptr_factory_
.GetWeakPtr()));
253 DCHECK_EQ(synced_details
.title(), remote_details
.title());
255 FileTracker parent_tracker
;
256 if (!metadata_database()->FindTrackerByTrackerID(
257 dirty_tracker_
->parent_tracker_id(), &parent_tracker
)) {
258 token
->RecordLog("Missing parent tracker for a non sync-root tracker: "
259 + dirty_tracker_
->file_id());
261 SyncCompleted(token
.Pass(), SYNC_STATUS_FAILED
);
265 if (!HasFolderAsParent(remote_details
, parent_tracker
.file_id())) {
266 // Handle reorganize as deletion + addition.
267 token
->RecordLog("Detected file reorganize.");
269 MoveToBackground(token
.Pass(),
270 base::Bind(&RemoteToLocalSyncer::HandleFileMove
,
271 weak_ptr_factory_
.GetWeakPtr()));
275 if (synced_details
.file_kind() == FILE_KIND_FILE
) {
276 if (synced_details
.md5() != remote_details
.md5()) {
277 token
->RecordLog("Detected file content update.");
278 MoveToBackground(token
.Pass(),
279 base::Bind(&RemoteToLocalSyncer::HandleContentUpdate
,
280 weak_ptr_factory_
.GetWeakPtr()));
284 DCHECK_EQ(FILE_KIND_FOLDER
, synced_details
.file_kind());
285 if (synced_details
.missing()) {
286 token
->RecordLog("Detected folder update.");
287 MoveToBackground(token
.Pass(),
288 base::Bind(&RemoteToLocalSyncer::HandleFolderUpdate
,
289 weak_ptr_factory_
.GetWeakPtr()));
292 if (dirty_tracker_
->needs_folder_listing()) {
293 token
->RecordLog("Needs listing folder.");
294 MoveToBackground(token
.Pass(),
295 base::Bind(&RemoteToLocalSyncer::ListFolderContent
,
296 weak_ptr_factory_
.GetWeakPtr()));
299 SyncCompleted(token
.Pass(), SYNC_STATUS_OK
);
303 token
->RecordLog("Trivial file change.");
304 SyncCompleted(token
.Pass(), SYNC_STATUS_OK
);
307 void RemoteToLocalSyncer::MoveToBackground(scoped_ptr
<SyncTaskToken
> token
,
308 const Continuation
& continuation
) {
309 DCHECK(dirty_tracker_
);
311 scoped_ptr
<TaskBlocker
> blocker(new TaskBlocker
);
312 blocker
->app_id
= dirty_tracker_
->app_id();
314 blocker
->paths
.push_back(url_
.path());
315 blocker
->file_ids
.push_back(dirty_tracker_
->file_id());
316 blocker
->tracker_ids
.push_back(dirty_tracker_
->tracker_id());
318 SyncTaskManager::UpdateTaskBlocker(
319 token
.Pass(), blocker
.Pass(),
320 base::Bind(&RemoteToLocalSyncer::ContinueAsBackgroundTask
,
321 weak_ptr_factory_
.GetWeakPtr(), continuation
));
324 void RemoteToLocalSyncer::ContinueAsBackgroundTask(
325 const Continuation
& continuation
,
326 scoped_ptr
<SyncTaskToken
> token
) {
327 DCHECK(dirty_tracker_
);
329 // The SyncTask runs as a background task beyond this point.
330 // Not that any task can run between MoveToBackground() and
331 // ContinueAsBackgroundTask(), so we need to make sure other tasks didn't
332 // affect to the current RemoteToLocalSyncer task.
334 // - For LocalToRemoteSyncer, it may update or delete any of FileTracker and
335 // FileMetadata. When it updates FileMetadata or FileDetails in FileTracker,
336 // it also updates |change_id|. So, ensure the target FileTracker and
337 // FileMetadata exist and their |change_id|s are not updated.
338 // - For ListChangesTask, it may update FileMetadata together with |change_id|
339 // and may delete FileTracker.
340 // - For UninstallAppTask, it may delete FileMetadata and FileTracker.
341 // Check if FileTracker still exists.
342 // - For other RemoteToLocalSyncer, it may delete the FileTracker of parent.
343 // Note that since RemoteToLocalSyncer demotes the target FileTracker first,
344 // any other RemoteToLocalSyncer does not run for current |dirty_tracker_|.
345 // - Others, SyncEngineInitializer and RegisterAppTask doesn't affect to
347 FileTracker latest_dirty_tracker
;
348 if (!metadata_database()->FindTrackerByTrackerID(
349 dirty_tracker_
->tracker_id(), &latest_dirty_tracker
) ||
350 dirty_tracker_
->active() != latest_dirty_tracker
.active() ||
351 !latest_dirty_tracker
.dirty()) {
352 SyncCompleted(token
.Pass(), SYNC_STATUS_RETRY
);
356 int64 current_change_id
= kint64min
;
357 int64 latest_change_id
= kint64min
;
358 if (dirty_tracker_
->has_synced_details())
359 current_change_id
= dirty_tracker_
->synced_details().change_id();
360 if (latest_dirty_tracker
.has_synced_details())
361 latest_change_id
= latest_dirty_tracker
.synced_details().change_id();
362 if (current_change_id
!= latest_change_id
) {
363 SyncCompleted(token
.Pass(), SYNC_STATUS_RETRY
);
367 FileMetadata latest_file_metadata
;
368 if (metadata_database()->FindFileByFileID(dirty_tracker_
->file_id(),
369 &latest_file_metadata
)) {
370 if (!remote_metadata_
) {
371 SyncCompleted(token
.Pass(), SYNC_STATUS_RETRY
);
375 int64 change_id
= remote_metadata_
->details().change_id();
376 int64 latest_change_id
= latest_file_metadata
.details().change_id();
377 if (change_id
!= latest_change_id
) {
378 SyncCompleted(token
.Pass(), SYNC_STATUS_RETRY
);
382 if (remote_metadata_
) {
383 SyncCompleted(token
.Pass(), SYNC_STATUS_RETRY
);
387 continuation
.Run(token
.Pass());
390 void RemoteToLocalSyncer::HandleMissingRemoteMetadata(
391 scoped_ptr
<SyncTaskToken
> token
) {
392 DCHECK(dirty_tracker_
);
394 drive_service()->GetFileResource(
395 dirty_tracker_
->file_id(),
396 base::Bind(&RemoteToLocalSyncer::DidGetRemoteMetadata
,
397 weak_ptr_factory_
.GetWeakPtr(),
398 base::Passed(&token
)));
401 void RemoteToLocalSyncer::DidGetRemoteMetadata(
402 scoped_ptr
<SyncTaskToken
> token
,
403 google_apis::DriveApiErrorCode error
,
404 scoped_ptr
<google_apis::FileResource
> entry
) {
405 DCHECK(sync_context_
->GetWorkerTaskRunner()->RunsTasksOnCurrentThread());
407 SyncStatusCode status
= DriveApiErrorCodeToSyncStatusCode(error
);
408 if (status
!= SYNC_STATUS_OK
&&
409 error
!= google_apis::HTTP_NOT_FOUND
) {
410 SyncCompleted(token
.Pass(), status
);
414 if (error
== google_apis::HTTP_NOT_FOUND
) {
415 status
= metadata_database()->UpdateByDeletedRemoteFile(
416 dirty_tracker_
->file_id());
417 SyncCompleted(token
.Pass(), status
);
423 SyncCompleted(token
.Pass(), SYNC_STATUS_FAILED
);
427 status
= metadata_database()->UpdateByFileResource(*entry
);
428 if (status
!= SYNC_STATUS_OK
) {
429 SyncCompleted(token
.Pass(), status
);
433 metadata_database()->PromoteDemotedTracker(dirty_tracker_
->tracker_id());
435 // Do not update |dirty_tracker_|.
436 SyncCompleted(token
.Pass(), SYNC_STATUS_RETRY
);
439 void RemoteToLocalSyncer::DidPrepareForAddOrUpdateFile(
440 scoped_ptr
<SyncTaskToken
> token
,
441 SyncStatusCode status
) {
442 if (status
!= SYNC_STATUS_OK
) {
443 SyncCompleted(token
.Pass(), status
);
447 DCHECK(url_
.is_valid());
448 DCHECK(local_metadata_
);
449 DCHECK(local_changes_
);
451 // Check if the local file exists.
452 if (local_metadata_
->file_type
== SYNC_FILE_TYPE_UNKNOWN
||
453 (!local_changes_
->empty() && local_changes_
->back().IsDelete())) {
454 file_type_
= SYNC_FILE_TYPE_FILE
;
455 sync_action_
= SYNC_ACTION_ADDED
;
456 // Missing local file case.
457 // Download the file and add it to local as a new file.
458 DownloadFile(token
.Pass());
462 DCHECK(local_changes_
->empty() || local_changes_
->back().IsAddOrUpdate());
463 if (local_changes_
->empty()) {
464 if (local_metadata_
->file_type
== SYNC_FILE_TYPE_FILE
) {
465 file_type_
= SYNC_FILE_TYPE_FILE
;
466 sync_action_
= SYNC_ACTION_UPDATED
;
467 // Download the file and overwrite the existing local file.
468 DownloadFile(token
.Pass());
472 DCHECK_EQ(SYNC_FILE_TYPE_DIRECTORY
, local_metadata_
->file_type
);
474 // Got a remote regular file modification for existing local folder.
475 // Our policy prioritize folders in this case.
476 // Let local-to-remote sync phase process this change.
477 remote_change_processor()->RecordFakeLocalChange(
479 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE
,
480 local_metadata_
->file_type
),
481 SyncCompletedCallback(token
.Pass()));
485 DCHECK(local_changes_
->back().IsAddOrUpdate());
487 // Do nothing for the change now, and handle this in LocalToRemoteSync phase.
488 SyncCompleted(token
.Pass(), SYNC_STATUS_RETRY
);
491 void RemoteToLocalSyncer::HandleFolderUpdate(
492 scoped_ptr
<SyncTaskToken
> token
) {
493 DCHECK(dirty_tracker_
);
494 DCHECK(dirty_tracker_
->active());
495 DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_
));
497 DCHECK(remote_metadata_
);
498 DCHECK(remote_metadata_
->has_details());
499 DCHECK(!remote_metadata_
->details().missing());
500 DCHECK_EQ(FILE_KIND_FOLDER
, remote_metadata_
->details().file_kind());
502 Prepare(base::Bind(&RemoteToLocalSyncer::DidPrepareForFolderUpdate
,
503 weak_ptr_factory_
.GetWeakPtr(),
504 base::Passed(&token
)));
507 void RemoteToLocalSyncer::DidPrepareForFolderUpdate(
508 scoped_ptr
<SyncTaskToken
> token
,
509 SyncStatusCode status
) {
510 if (status
!= SYNC_STATUS_OK
) {
511 SyncCompleted(token
.Pass(), status
);
515 DCHECK(url_
.is_valid());
516 DCHECK(local_metadata_
);
517 DCHECK(local_changes_
);
519 // Check if the local file exists.
520 if (local_metadata_
->file_type
== SYNC_FILE_TYPE_UNKNOWN
||
521 (!local_changes_
->empty() && local_changes_
->back().IsDelete())) {
522 file_type_
= SYNC_FILE_TYPE_DIRECTORY
;
523 sync_action_
= SYNC_ACTION_ADDED
;
524 // No local file exists at the path.
525 CreateFolder(token
.Pass());
529 if (local_metadata_
->file_type
== SYNC_FILE_TYPE_DIRECTORY
) {
530 // There already exists a folder, nothing left to do.
531 if (dirty_tracker_
->needs_folder_listing() &&
532 !dirty_tracker_
->synced_details().missing()) {
533 ListFolderContent(token
.Pass());
535 SyncCompleted(token
.Pass(), SYNC_STATUS_OK
);
540 DCHECK_EQ(SYNC_FILE_TYPE_FILE
, local_metadata_
->file_type
);
541 file_type_
= SYNC_FILE_TYPE_DIRECTORY
;
542 sync_action_
= SYNC_ACTION_ADDED
;
543 // Got a remote folder for existing local file.
544 // Our policy prioritize folders in this case.
545 CreateFolder(token
.Pass());
548 void RemoteToLocalSyncer::HandleDeletion(
549 scoped_ptr
<SyncTaskToken
> token
) {
550 DCHECK(dirty_tracker_
);
551 DCHECK(dirty_tracker_
->active());
552 DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_
));
553 DCHECK(dirty_tracker_
->has_synced_details());
554 DCHECK(!dirty_tracker_
->synced_details().missing());
556 DCHECK(remote_metadata_
);
557 DCHECK(remote_metadata_
->has_details());
558 DCHECK(remote_metadata_
->details().missing());
560 Prepare(base::Bind(&RemoteToLocalSyncer::DidPrepareForDeletion
,
561 weak_ptr_factory_
.GetWeakPtr(),
562 base::Passed(&token
)));
565 void RemoteToLocalSyncer::HandleFileMove(scoped_ptr
<SyncTaskToken
> token
) {
566 DCHECK(dirty_tracker_
);
567 DCHECK(dirty_tracker_
->active());
568 DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_
));
569 DCHECK(dirty_tracker_
->has_synced_details());
571 DCHECK(remote_metadata_
);
572 DCHECK(remote_metadata_
->has_details());
573 DCHECK(!remote_metadata_
->details().missing());
575 Prepare(base::Bind(&RemoteToLocalSyncer::DidPrepareForDeletion
,
576 weak_ptr_factory_
.GetWeakPtr(),
577 base::Passed(&token
)));
580 void RemoteToLocalSyncer::DidPrepareForDeletion(
581 scoped_ptr
<SyncTaskToken
> token
,
582 SyncStatusCode status
) {
583 if (status
!= SYNC_STATUS_OK
) {
584 SyncCompleted(token
.Pass(), status
);
588 DCHECK(url_
.is_valid());
589 DCHECK(local_metadata_
);
590 DCHECK(local_changes_
);
592 // Check if the local file exists.
593 if (local_metadata_
->file_type
== SYNC_FILE_TYPE_UNKNOWN
||
594 (!local_changes_
->empty() && local_changes_
->back().IsDelete())) {
595 // No local file exists at the path.
596 SyncCompleted(token
.Pass(), SYNC_STATUS_OK
);
600 DCHECK(local_changes_
->empty() || local_changes_
->back().IsAddOrUpdate());
601 if (local_changes_
->empty()) {
602 file_type_
= local_metadata_
->file_type
;
603 sync_action_
= SYNC_ACTION_DELETED
;
604 DeleteLocalFile(token
.Pass());
608 DCHECK(local_changes_
->back().IsAddOrUpdate());
609 // File is remotely deleted and locally updated.
610 // Ignore the remote deletion and handle it as if applied successfully.
611 SyncCompleted(token
.Pass(), SYNC_STATUS_OK
);
614 void RemoteToLocalSyncer::HandleContentUpdate(
615 scoped_ptr
<SyncTaskToken
> token
) {
616 DCHECK(dirty_tracker_
);
617 DCHECK(dirty_tracker_
->active());
618 DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_
));
619 DCHECK(dirty_tracker_
->has_synced_details());
620 DCHECK_EQ(FILE_KIND_FILE
, dirty_tracker_
->synced_details().file_kind());
622 DCHECK(remote_metadata_
);
623 DCHECK(remote_metadata_
->has_details());
624 DCHECK(!remote_metadata_
->details().missing());
626 DCHECK_NE(dirty_tracker_
->synced_details().md5(),
627 remote_metadata_
->details().md5());
629 Prepare(base::Bind(&RemoteToLocalSyncer::DidPrepareForAddOrUpdateFile
,
630 weak_ptr_factory_
.GetWeakPtr(), base::Passed(&token
)));
633 void RemoteToLocalSyncer::ListFolderContent(
634 scoped_ptr
<SyncTaskToken
> token
) {
635 DCHECK(dirty_tracker_
);
636 DCHECK(dirty_tracker_
->active());
637 DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_
));
638 DCHECK(dirty_tracker_
->has_synced_details());
639 DCHECK(!dirty_tracker_
->synced_details().missing());
640 DCHECK_EQ(FILE_KIND_FOLDER
, dirty_tracker_
->synced_details().file_kind());
641 DCHECK(dirty_tracker_
->needs_folder_listing());
643 DCHECK(remote_metadata_
);
644 DCHECK(remote_metadata_
->has_details());
645 DCHECK(!remote_metadata_
->details().missing());
647 // TODO(tzik): Replace this call with ChildList version.
648 drive_service()->GetFileListInDirectory(
649 dirty_tracker_
->file_id(),
650 base::Bind(&RemoteToLocalSyncer::DidListFolderContent
,
651 weak_ptr_factory_
.GetWeakPtr(),
652 base::Passed(&token
),
653 base::Passed(make_scoped_ptr(new FileIDList
))));
656 void RemoteToLocalSyncer::DidListFolderContent(
657 scoped_ptr
<SyncTaskToken
> token
,
658 scoped_ptr
<FileIDList
> children
,
659 google_apis::DriveApiErrorCode error
,
660 scoped_ptr
<google_apis::FileList
> file_list
) {
661 SyncStatusCode status
= DriveApiErrorCodeToSyncStatusCode(error
);
662 if (status
!= SYNC_STATUS_OK
) {
663 SyncCompleted(token
.Pass(), status
);
669 SyncCompleted(token
.Pass(), SYNC_STATUS_FAILED
);
673 children
->reserve(children
->size() + file_list
->items().size());
674 for (ScopedVector
<google_apis::FileResource
>::const_iterator itr
=
675 file_list
->items().begin();
676 itr
!= file_list
->items().end();
678 children
->push_back((*itr
)->file_id());
681 if (!file_list
->next_link().is_empty()) {
682 drive_service()->GetRemainingFileList(
683 file_list
->next_link(),
684 base::Bind(&RemoteToLocalSyncer::DidListFolderContent
,
685 weak_ptr_factory_
.GetWeakPtr(),
686 base::Passed(&token
), base::Passed(&children
)));
690 status
= metadata_database()->PopulateFolderByChildList(
691 dirty_tracker_
->file_id(), *children
);
692 SyncCompleted(token
.Pass(), status
);
695 void RemoteToLocalSyncer::SyncCompleted(scoped_ptr
<SyncTaskToken
> token
,
696 SyncStatusCode status
) {
697 token
->RecordLog(base::StringPrintf(
698 "[Remote -> Local]: Finished: action=%s, tracker=%" PRId64
" status=%s",
699 SyncActionToString(sync_action_
), dirty_tracker_
->tracker_id(),
700 SyncStatusCodeToString(status
)));
702 if (sync_root_deletion_
) {
703 FinalizeSync(token
.Pass(), SYNC_STATUS_OK
);
707 if (status
== SYNC_STATUS_RETRY
) {
708 FinalizeSync(token
.Pass(), SYNC_STATUS_OK
);
712 if (status
!= SYNC_STATUS_OK
) {
713 FinalizeSync(token
.Pass(), status
);
717 DCHECK(dirty_tracker_
);
718 DCHECK(remote_metadata_
);
719 DCHECK(remote_metadata_
->has_details());
721 FileDetails updated_details
= remote_metadata_
->details();
722 if (!dirty_tracker_
->active() ||
723 HasDisabledAppRoot(metadata_database(), *dirty_tracker_
)) {
724 // Operations for an inactive tracker don't update file content.
725 if (dirty_tracker_
->has_synced_details())
726 updated_details
.set_md5(dirty_tracker_
->synced_details().md5());
727 if (!dirty_tracker_
->active()) {
728 // Keep missing true, as the change hasn't been synced to local.
729 updated_details
.clear_md5();
730 updated_details
.set_missing(true);
734 status
= metadata_database()->UpdateTracker(
735 dirty_tracker_
->tracker_id(), updated_details
);
736 FinalizeSync(token
.Pass(), status
);
739 void RemoteToLocalSyncer::FinalizeSync(scoped_ptr
<SyncTaskToken
> token
,
740 SyncStatusCode status
) {
742 remote_change_processor()->FinalizeRemoteSync(
743 url_
, false /* clear_local_change */,
744 base::Bind(SyncTaskManager::NotifyTaskDone
,
745 base::Passed(&token
), status
));
749 SyncTaskManager::NotifyTaskDone(token
.Pass(), status
);
752 void RemoteToLocalSyncer::Prepare(const SyncStatusCallback
& callback
) {
753 DCHECK(url_
.is_valid());
754 remote_change_processor()->PrepareForProcessRemoteChange(
756 base::Bind(&RemoteToLocalSyncer::DidPrepare
,
757 weak_ptr_factory_
.GetWeakPtr(),
761 void RemoteToLocalSyncer::DidPrepare(const SyncStatusCallback
& callback
,
762 SyncStatusCode status
,
763 const SyncFileMetadata
& local_metadata
,
764 const FileChangeList
& local_changes
) {
765 if (status
!= SYNC_STATUS_OK
) {
766 callback
.Run(status
);
771 local_metadata_
.reset(new SyncFileMetadata(local_metadata
));
772 local_changes_
.reset(new FileChangeList(local_changes
));
774 callback
.Run(status
);
777 void RemoteToLocalSyncer::DeleteLocalFile(scoped_ptr
<SyncTaskToken
> token
) {
778 remote_change_processor()->ApplyRemoteChange(
779 FileChange(FileChange::FILE_CHANGE_DELETE
, SYNC_FILE_TYPE_UNKNOWN
),
782 SyncCompletedCallback(token
.Pass()));
785 void RemoteToLocalSyncer::DownloadFile(scoped_ptr
<SyncTaskToken
> token
) {
786 DCHECK(sync_context_
->GetWorkerTaskRunner()->RunsTasksOnCurrentThread());
788 storage::ScopedFile file
= CreateTemporaryFile(
789 make_scoped_refptr(sync_context_
->GetWorkerTaskRunner()));
791 base::FilePath path
= file
.path();
792 drive_service()->DownloadFile(
793 path
, remote_metadata_
->file_id(),
794 base::Bind(&RemoteToLocalSyncer::DidDownloadFile
,
795 weak_ptr_factory_
.GetWeakPtr(),
796 base::Passed(&token
), base::Passed(&file
)),
797 google_apis::GetContentCallback(),
798 google_apis::ProgressCallback());
801 void RemoteToLocalSyncer::DidDownloadFile(scoped_ptr
<SyncTaskToken
> token
,
802 storage::ScopedFile file
,
803 google_apis::DriveApiErrorCode error
,
804 const base::FilePath
&) {
805 DCHECK(sync_context_
->GetWorkerTaskRunner()->RunsTasksOnCurrentThread());
807 SyncStatusCode status
= DriveApiErrorCodeToSyncStatusCode(error
);
808 if (status
!= SYNC_STATUS_OK
) {
809 SyncCompleted(token
.Pass(), status
);
813 base::FilePath path
= file
.path();
814 const std::string md5
= drive::util::GetMd5Digest(path
, nullptr);
816 SyncCompleted(token
.Pass(), SYNC_FILE_ERROR_NOT_FOUND
);
820 if (md5
!= remote_metadata_
->details().md5()) {
821 // File has been modified since last metadata retrieval.
822 SyncCompleted(token
.Pass(), SYNC_STATUS_RETRY
);
826 remote_change_processor()->ApplyRemoteChange(
827 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE
, SYNC_FILE_TYPE_FILE
),
829 base::Bind(&RemoteToLocalSyncer::DidApplyDownload
,
830 weak_ptr_factory_
.GetWeakPtr(),
831 base::Passed(&token
), base::Passed(&file
)));
834 void RemoteToLocalSyncer::DidApplyDownload(scoped_ptr
<SyncTaskToken
> token
,
836 SyncStatusCode status
) {
837 SyncCompleted(token
.Pass(), status
);
840 void RemoteToLocalSyncer::CreateFolder(scoped_ptr
<SyncTaskToken
> token
) {
841 remote_change_processor()->ApplyRemoteChange(
842 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE
,
843 SYNC_FILE_TYPE_DIRECTORY
),
844 base::FilePath(), url_
,
845 SyncCompletedCallback(token
.Pass()));
848 drive::DriveServiceInterface
* RemoteToLocalSyncer::drive_service() {
849 return sync_context_
->GetDriveService();
852 MetadataDatabase
* RemoteToLocalSyncer::metadata_database() {
853 return sync_context_
->GetMetadataDatabase();
856 RemoteChangeProcessor
* RemoteToLocalSyncer::remote_change_processor() {
857 DCHECK(sync_context_
->GetRemoteChangeProcessor());
858 return sync_context_
->GetRemoteChangeProcessor();
861 SyncStatusCallback
RemoteToLocalSyncer::SyncCompletedCallback(
862 scoped_ptr
<SyncTaskToken
> token
) {
863 return base::Bind(&RemoteToLocalSyncer::SyncCompleted
,
864 weak_ptr_factory_
.GetWeakPtr(),
865 base::Passed(&token
));
868 } // namespace drive_backend
869 } // namespace sync_file_system