remote_to_local_syncer.cc revision 03b57e008b61dfcb1fbad3aea950ae0e001748b0
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.
4
5#include "chrome/browser/sync_file_system/drive_backend/remote_to_local_syncer.h"
6
7#include "base/bind.h"
8#include "base/callback.h"
9#include "base/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/drive/drive_api_util.h"
16#include "chrome/browser/drive/drive_service_interface.h"
17#include "chrome/browser/sync_file_system/drive_backend/callback_helper.h"
18#include "chrome/browser/sync_file_system/drive_backend/drive_backend_util.h"
19#include "chrome/browser/sync_file_system/drive_backend/metadata_database.h"
20#include "chrome/browser/sync_file_system/drive_backend/sync_engine_context.h"
21#include "chrome/browser/sync_file_system/drive_backend/sync_task_manager.h"
22#include "chrome/browser/sync_file_system/drive_backend/sync_task_token.h"
23#include "chrome/browser/sync_file_system/drive_backend/task_dependency_manager.h"
24#include "chrome/browser/sync_file_system/logger.h"
25#include "chrome/browser/sync_file_system/syncable_file_system_util.h"
26#include "extensions/common/extension.h"
27#include "google_apis/drive/drive_api_parser.h"
28#include "google_apis/drive/gdata_wapi_parser.h"
29#include "webkit/common/fileapi/file_system_util.h"
30
31namespace sync_file_system {
32namespace drive_backend {
33
34namespace {
35
36bool BuildFileSystemURL(MetadataDatabase* metadata_database,
37                        const FileTracker& tracker,
38                        storage::FileSystemURL* url) {
39  base::FilePath path;
40  if (!metadata_database->BuildPathForTracker(
41          tracker.tracker_id(), &path))
42    return false;
43
44  GURL origin =
45      extensions::Extension::GetBaseURLFromExtensionId(tracker.app_id());
46  *url = sync_file_system::CreateSyncableFileSystemURL(origin, path);
47
48  return true;
49}
50
51bool HasFolderAsParent(const FileDetails& details,
52                       const std::string& folder_id) {
53  for (int i = 0; i < details.parent_folder_ids_size(); ++i) {
54    if (details.parent_folder_ids(i) == folder_id)
55      return true;
56  }
57  return false;
58}
59
60bool HasDisabledAppRoot(MetadataDatabase* database,
61                        const FileTracker& tracker) {
62  DCHECK(tracker.active());
63  FileTracker app_root_tracker;
64  if (database->FindAppRootTracker(tracker.app_id(), &app_root_tracker)) {
65    DCHECK(app_root_tracker.tracker_kind() == TRACKER_KIND_APP_ROOT ||
66           app_root_tracker.tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT);
67    return app_root_tracker.tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT;
68  }
69  return false;
70}
71
72scoped_ptr<FileMetadata> GetFileMetadata(MetadataDatabase* database,
73                                         const std::string& file_id) {
74  scoped_ptr<FileMetadata> metadata(new FileMetadata);
75  if (!database->FindFileByFileID(file_id, metadata.get()))
76    metadata.reset();
77  return metadata.Pass();
78}
79
80// Creates a temporary file in |dir_path|.  This must be called on an
81// IO-allowed task runner, and the runner must be given as |file_task_runner|.
82storage::ScopedFile CreateTemporaryFile(base::TaskRunner* file_task_runner) {
83  base::FilePath temp_file_path;
84  if (!base::CreateTemporaryFile(&temp_file_path))
85    return storage::ScopedFile();
86
87  return storage::ScopedFile(temp_file_path,
88                             storage::ScopedFile::DELETE_ON_SCOPE_OUT,
89                             file_task_runner);
90}
91
92}  // namespace
93
94RemoteToLocalSyncer::RemoteToLocalSyncer(SyncEngineContext* sync_context)
95    : sync_context_(sync_context),
96      sync_action_(SYNC_ACTION_NONE),
97      prepared_(false),
98      sync_root_deletion_(false),
99      weak_ptr_factory_(this) {
100}
101
102RemoteToLocalSyncer::~RemoteToLocalSyncer() {
103}
104
105void RemoteToLocalSyncer::RunPreflight(scoped_ptr<SyncTaskToken> token) {
106  token->InitializeTaskLog("Remote -> Local");
107
108  scoped_ptr<BlockingFactor> blocking_factor(new BlockingFactor);
109  blocking_factor->exclusive = true;
110  SyncTaskManager::UpdateBlockingFactor(
111      token.Pass(), blocking_factor.Pass(),
112      base::Bind(&RemoteToLocalSyncer::RunExclusive,
113                 weak_ptr_factory_.GetWeakPtr()));
114}
115
116void RemoteToLocalSyncer::RunExclusive(scoped_ptr<SyncTaskToken> token) {
117  if (!drive_service() || !metadata_database() || !remote_change_processor()) {
118    token->RecordLog("Context not ready.");
119    SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED);
120    return;
121  }
122
123  dirty_tracker_ = make_scoped_ptr(new FileTracker);
124  if (metadata_database()->GetNormalPriorityDirtyTracker(
125          dirty_tracker_.get())) {
126    token->RecordLog(base::StringPrintf(
127        "Start: tracker_id=%" PRId64, dirty_tracker_->tracker_id()));
128    metadata_database()->LowerTrackerPriority(dirty_tracker_->tracker_id());
129    ResolveRemoteChange(token.Pass());
130    return;
131  }
132
133  token->RecordLog("Nothing to do.");
134  SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_NO_CHANGE_TO_SYNC);
135}
136
137void RemoteToLocalSyncer::ResolveRemoteChange(scoped_ptr<SyncTaskToken> token) {
138  DCHECK(dirty_tracker_);
139  remote_metadata_ = GetFileMetadata(
140      metadata_database(), dirty_tracker_->file_id());
141
142  if (!remote_metadata_ || !remote_metadata_->has_details()) {
143    if (remote_metadata_ && !remote_metadata_->has_details()) {
144      token->RecordLog(
145          "Missing details of a remote file: " + remote_metadata_->file_id());
146      NOTREACHED();
147    }
148    token->RecordLog("Missing remote metadata case.");
149    HandleMissingRemoteMetadata(token.Pass());
150    return;
151  }
152
153  DCHECK(remote_metadata_);
154  DCHECK(remote_metadata_->has_details());
155  const FileDetails& remote_details = remote_metadata_->details();
156
157  if (!dirty_tracker_->active() ||
158      HasDisabledAppRoot(metadata_database(), *dirty_tracker_)) {
159    // Handle inactive tracker in SyncCompleted.
160    token->RecordLog("Inactive tracker case.");
161    SyncCompleted(token.Pass(), SYNC_STATUS_OK);
162    return;
163  }
164
165  DCHECK(dirty_tracker_->active());
166  DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_));
167
168  if (!dirty_tracker_->has_synced_details()) {
169    token->RecordLog(base::StringPrintf(
170        "Missing synced_details of an active tracker: %" PRId64,
171        dirty_tracker_->tracker_id()));
172    NOTREACHED();
173    SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
174    return;
175  }
176
177  DCHECK(dirty_tracker_->has_synced_details());
178  const FileDetails& synced_details = dirty_tracker_->synced_details();
179
180  if (dirty_tracker_->tracker_id() ==
181      metadata_database()->GetSyncRootTrackerID()) {
182    if (remote_details.missing() ||
183        synced_details.title() != remote_details.title() ||
184        remote_details.parent_folder_ids_size()) {
185      token->RecordLog("Sync-root deletion.");
186      HandleSyncRootDeletion(token.Pass());
187      return;
188    }
189    token->RecordLog("Trivial sync-root change.");
190    SyncCompleted(token.Pass(), SYNC_STATUS_OK);
191    return;
192  }
193
194  DCHECK_NE(dirty_tracker_->tracker_id(),
195            metadata_database()->GetSyncRootTrackerID());
196
197  if (remote_details.missing()) {
198    if (!synced_details.missing()) {
199      token->RecordLog("Remote file deletion.");
200      HandleDeletion(token.Pass());
201      return;
202    }
203
204    DCHECK(synced_details.missing());
205    token->RecordLog("Found a stray missing tracker: " +
206                     dirty_tracker_->file_id());
207    NOTREACHED();
208    SyncCompleted(token.Pass(), SYNC_STATUS_OK);
209    return;
210  }
211
212  // Most of remote_details field is valid from here.
213  DCHECK(!remote_details.missing());
214
215  if (synced_details.file_kind() != remote_details.file_kind()) {
216    token->RecordLog(base::StringPrintf(
217        "Found type mismatch between remote and local file: %s"
218        " type: (local) %d vs (remote) %d",
219        dirty_tracker_->file_id().c_str(),
220        synced_details.file_kind(),
221        remote_details.file_kind()));
222    NOTREACHED();
223    SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
224    return;
225  }
226  DCHECK_EQ(synced_details.file_kind(), remote_details.file_kind());
227
228  if (synced_details.file_kind() == FILE_KIND_UNSUPPORTED) {
229    token->RecordLog("Found an unsupported active file: " +
230                     remote_metadata_->file_id());
231    NOTREACHED();
232    SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
233    return;
234  }
235  DCHECK(remote_details.file_kind() == FILE_KIND_FILE ||
236         remote_details.file_kind() == FILE_KIND_FOLDER);
237
238  if (synced_details.title() != remote_details.title()) {
239    // Handle rename as deletion + addition.
240    token->RecordLog("Detected file rename.");
241    Prepare(base::Bind(&RemoteToLocalSyncer::DidPrepareForDeletion,
242                       weak_ptr_factory_.GetWeakPtr(),
243                       base::Passed(&token)));
244    return;
245  }
246  DCHECK_EQ(synced_details.title(), remote_details.title());
247
248  FileTracker parent_tracker;
249  if (!metadata_database()->FindTrackerByTrackerID(
250          dirty_tracker_->parent_tracker_id(), &parent_tracker)) {
251    token->RecordLog("Missing parent tracker for a non sync-root tracker: "
252                     + dirty_tracker_->file_id());
253    NOTREACHED();
254    SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
255    return;
256  }
257
258  if (!HasFolderAsParent(remote_details, parent_tracker.file_id())) {
259    // Handle reorganize as deletion + addition.
260    token->RecordLog("Detected file reorganize.");
261    Prepare(base::Bind(&RemoteToLocalSyncer::DidPrepareForDeletion,
262                       weak_ptr_factory_.GetWeakPtr(),
263                       base::Passed(&token)));
264    return;
265  }
266
267  if (synced_details.file_kind() == FILE_KIND_FILE) {
268    if (synced_details.md5() != remote_details.md5()) {
269      token->RecordLog("Detected file content update.");
270      HandleContentUpdate(token.Pass());
271      return;
272    }
273  } else {
274    DCHECK_EQ(FILE_KIND_FOLDER, synced_details.file_kind());
275    if (synced_details.missing()) {
276      token->RecordLog("Detected folder update.");
277      HandleFolderUpdate(token.Pass());
278      return;
279    }
280    if (dirty_tracker_->needs_folder_listing()) {
281      token->RecordLog("Needs listing folder.");
282      ListFolderContent(token.Pass());
283      return;
284    }
285    SyncCompleted(token.Pass(), SYNC_STATUS_OK);
286    return;
287  }
288
289  token->RecordLog("Trivial file change.");
290  SyncCompleted(token.Pass(), SYNC_STATUS_OK);
291}
292
293void RemoteToLocalSyncer::HandleMissingRemoteMetadata(
294    scoped_ptr<SyncTaskToken> token) {
295  DCHECK(dirty_tracker_);
296
297  drive_service()->GetFileResource(
298      dirty_tracker_->file_id(),
299      base::Bind(&RemoteToLocalSyncer::DidGetRemoteMetadata,
300                 weak_ptr_factory_.GetWeakPtr(),
301                 base::Passed(&token)));
302}
303
304void RemoteToLocalSyncer::DidGetRemoteMetadata(
305    scoped_ptr<SyncTaskToken> token,
306    google_apis::GDataErrorCode error,
307    scoped_ptr<google_apis::FileResource> entry) {
308  DCHECK(sync_context_->GetWorkerTaskRunner()->RunsTasksOnCurrentThread());
309
310  SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
311  if (status != SYNC_STATUS_OK &&
312      error != google_apis::HTTP_NOT_FOUND) {
313    SyncCompleted(token.Pass(), status);
314    return;
315  }
316
317  if (error == google_apis::HTTP_NOT_FOUND) {
318    metadata_database()->UpdateByDeletedRemoteFile(
319        dirty_tracker_->file_id(), SyncCompletedCallback(token.Pass()));
320    return;
321  }
322
323  if (!entry) {
324    NOTREACHED();
325    SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
326    return;
327  }
328
329  metadata_database()->UpdateByFileResource(
330      *entry,
331      base::Bind(&RemoteToLocalSyncer::DidUpdateDatabaseForRemoteMetadata,
332                 weak_ptr_factory_.GetWeakPtr(),
333                 base::Passed(&token)));
334}
335
336void RemoteToLocalSyncer::DidUpdateDatabaseForRemoteMetadata(
337    scoped_ptr<SyncTaskToken> token,
338    SyncStatusCode status) {
339  if (status != SYNC_STATUS_OK) {
340    SyncCompleted(token.Pass(), status);
341    return;
342  }
343
344  metadata_database()->PromoteDemotedTracker(dirty_tracker_->tracker_id());
345
346  // Do not update |dirty_tracker_|.
347  SyncCompleted(token.Pass(), SYNC_STATUS_RETRY);
348}
349
350void RemoteToLocalSyncer::DidPrepareForAddOrUpdateFile(
351    scoped_ptr<SyncTaskToken> token,
352    SyncStatusCode status) {
353  if (status != SYNC_STATUS_OK) {
354    SyncCompleted(token.Pass(), status);
355    return;
356  }
357
358  DCHECK(url_.is_valid());
359  DCHECK(local_metadata_);
360  DCHECK(local_changes_);
361
362  // Check if the local file exists.
363  if (local_metadata_->file_type == SYNC_FILE_TYPE_UNKNOWN ||
364      (!local_changes_->empty() && local_changes_->back().IsDelete())) {
365    sync_action_ = SYNC_ACTION_ADDED;
366    // Missing local file case.
367    // Download the file and add it to local as a new file.
368    DownloadFile(token.Pass());
369    return;
370  }
371
372  DCHECK(local_changes_->empty() || local_changes_->back().IsAddOrUpdate());
373  if (local_changes_->empty()) {
374    if (local_metadata_->file_type == SYNC_FILE_TYPE_FILE) {
375      sync_action_ = SYNC_ACTION_UPDATED;
376      // Download the file and overwrite the existing local file.
377      DownloadFile(token.Pass());
378      return;
379    }
380
381    DCHECK_EQ(SYNC_FILE_TYPE_DIRECTORY, local_metadata_->file_type);
382
383    // Got a remote regular file modification for existing local folder.
384    // Our policy prioritize folders in this case.
385    // Let local-to-remote sync phase process this change.
386    remote_change_processor()->RecordFakeLocalChange(
387        url_,
388        FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
389                   local_metadata_->file_type),
390        SyncCompletedCallback(token.Pass()));
391    return;
392  }
393
394  DCHECK(local_changes_->back().IsAddOrUpdate());
395  // Conflict case.
396  // Do nothing for the change now, and handle this in LocalToRemoteSync phase.
397  SyncCompleted(token.Pass(), SYNC_STATUS_RETRY);
398}
399
400void RemoteToLocalSyncer::HandleFolderUpdate(
401    scoped_ptr<SyncTaskToken> token) {
402  DCHECK(dirty_tracker_);
403  DCHECK(dirty_tracker_->active());
404  DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_));
405
406  DCHECK(remote_metadata_);
407  DCHECK(remote_metadata_->has_details());
408  DCHECK(!remote_metadata_->details().missing());
409  DCHECK_EQ(FILE_KIND_FOLDER, remote_metadata_->details().file_kind());
410
411  Prepare(base::Bind(&RemoteToLocalSyncer::DidPrepareForFolderUpdate,
412                     weak_ptr_factory_.GetWeakPtr(),
413                     base::Passed(&token)));
414}
415
416void RemoteToLocalSyncer::DidPrepareForFolderUpdate(
417    scoped_ptr<SyncTaskToken> token,
418    SyncStatusCode status) {
419  if (status != SYNC_STATUS_OK) {
420    SyncCompleted(token.Pass(), status);
421    return;
422  }
423
424  DCHECK(url_.is_valid());
425  DCHECK(local_metadata_);
426  DCHECK(local_changes_);
427
428  // Check if the local file exists.
429  if (local_metadata_->file_type == SYNC_FILE_TYPE_UNKNOWN ||
430      (!local_changes_->empty() && local_changes_->back().IsDelete())) {
431    sync_action_ = SYNC_ACTION_ADDED;
432    // No local file exists at the path.
433    CreateFolder(token.Pass());
434    return;
435  }
436
437  if (local_metadata_->file_type == SYNC_FILE_TYPE_DIRECTORY) {
438    // There already exists a folder, nothing left to do.
439    if (dirty_tracker_->needs_folder_listing() &&
440        !dirty_tracker_->synced_details().missing()) {
441      ListFolderContent(token.Pass());
442    } else {
443      SyncCompleted(token.Pass(), SYNC_STATUS_OK);
444    }
445    return;
446  }
447
448  DCHECK_EQ(SYNC_FILE_TYPE_FILE, local_metadata_->file_type);
449  sync_action_ = SYNC_ACTION_ADDED;
450  // Got a remote folder for existing local file.
451  // Our policy prioritize folders in this case.
452  CreateFolder(token.Pass());
453}
454
455void RemoteToLocalSyncer::HandleSyncRootDeletion(
456    scoped_ptr<SyncTaskToken> token) {
457  sync_root_deletion_ = true;
458  SyncCompleted(token.Pass(), SYNC_STATUS_OK);
459}
460
461void RemoteToLocalSyncer::HandleDeletion(
462    scoped_ptr<SyncTaskToken> token) {
463  DCHECK(dirty_tracker_);
464  DCHECK(dirty_tracker_->active());
465  DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_));
466  DCHECK(dirty_tracker_->has_synced_details());
467  DCHECK(!dirty_tracker_->synced_details().missing());
468
469  DCHECK(remote_metadata_);
470  DCHECK(remote_metadata_->has_details());
471  DCHECK(remote_metadata_->details().missing());
472
473  Prepare(base::Bind(&RemoteToLocalSyncer::DidPrepareForDeletion,
474                     weak_ptr_factory_.GetWeakPtr(),
475                     base::Passed(&token)));
476}
477
478void RemoteToLocalSyncer::DidPrepareForDeletion(
479    scoped_ptr<SyncTaskToken> token,
480    SyncStatusCode status) {
481  if (status != SYNC_STATUS_OK) {
482    SyncCompleted(token.Pass(), status);
483    return;
484  }
485
486  DCHECK(url_.is_valid());
487  DCHECK(local_metadata_);
488  DCHECK(local_changes_);
489
490  // Check if the local file exists.
491  if (local_metadata_->file_type == SYNC_FILE_TYPE_UNKNOWN ||
492      (!local_changes_->empty() && local_changes_->back().IsDelete())) {
493    // No local file exists at the path.
494    SyncCompleted(token.Pass(), SYNC_STATUS_OK);
495    return;
496  }
497
498  DCHECK(local_changes_->empty() || local_changes_->back().IsAddOrUpdate());
499  if (local_changes_->empty()) {
500    sync_action_ = SYNC_ACTION_DELETED;
501    DeleteLocalFile(token.Pass());
502    return;
503  }
504
505  DCHECK(local_changes_->back().IsAddOrUpdate());
506  // File is remotely deleted and locally updated.
507  // Ignore the remote deletion and handle it as if applied successfully.
508  SyncCompleted(token.Pass(), SYNC_STATUS_OK);
509}
510
511void RemoteToLocalSyncer::HandleContentUpdate(
512    scoped_ptr<SyncTaskToken> token) {
513  DCHECK(dirty_tracker_);
514  DCHECK(dirty_tracker_->active());
515  DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_));
516  DCHECK(dirty_tracker_->has_synced_details());
517  DCHECK_EQ(FILE_KIND_FILE, dirty_tracker_->synced_details().file_kind());
518
519  DCHECK(remote_metadata_);
520  DCHECK(remote_metadata_->has_details());
521  DCHECK(!remote_metadata_->details().missing());
522
523  DCHECK_NE(dirty_tracker_->synced_details().md5(),
524            remote_metadata_->details().md5());
525
526  Prepare(base::Bind(&RemoteToLocalSyncer::DidPrepareForAddOrUpdateFile,
527                     weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)));
528}
529
530void RemoteToLocalSyncer::ListFolderContent(
531    scoped_ptr<SyncTaskToken> token) {
532  DCHECK(dirty_tracker_);
533  DCHECK(dirty_tracker_->active());
534  DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_));
535  DCHECK(dirty_tracker_->has_synced_details());
536  DCHECK(!dirty_tracker_->synced_details().missing());
537  DCHECK_EQ(FILE_KIND_FOLDER, dirty_tracker_->synced_details().file_kind());
538  DCHECK(dirty_tracker_->needs_folder_listing());
539
540  DCHECK(remote_metadata_);
541  DCHECK(remote_metadata_->has_details());
542  DCHECK(!remote_metadata_->details().missing());
543
544  // TODO(tzik): Replace this call with ChildList version.
545  drive_service()->GetFileListInDirectory(
546      dirty_tracker_->file_id(),
547      base::Bind(&RemoteToLocalSyncer::DidListFolderContent,
548                 weak_ptr_factory_.GetWeakPtr(),
549                 base::Passed(&token),
550                 base::Passed(make_scoped_ptr(new FileIDList))));
551}
552
553void RemoteToLocalSyncer::DidListFolderContent(
554    scoped_ptr<SyncTaskToken> token,
555    scoped_ptr<FileIDList> children,
556    google_apis::GDataErrorCode error,
557    scoped_ptr<google_apis::FileList> file_list) {
558  SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
559  if (status != SYNC_STATUS_OK) {
560    SyncCompleted(token.Pass(), status);
561    return;
562  }
563
564  if (!file_list) {
565    NOTREACHED();
566    SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
567    return;
568  }
569
570  children->reserve(children->size() + file_list->items().size());
571  for (ScopedVector<google_apis::FileResource>::const_iterator itr =
572           file_list->items().begin();
573       itr != file_list->items().end();
574       ++itr) {
575    children->push_back((*itr)->file_id());
576  }
577
578  if (!file_list->next_link().is_empty()) {
579    drive_service()->GetRemainingFileList(
580        file_list->next_link(),
581        base::Bind(&RemoteToLocalSyncer::DidListFolderContent,
582                   weak_ptr_factory_.GetWeakPtr(),
583                   base::Passed(&token), base::Passed(&children)));
584    return;
585  }
586
587  metadata_database()->PopulateFolderByChildList(
588      dirty_tracker_->file_id(), *children,
589      SyncCompletedCallback(token.Pass()));
590}
591
592void RemoteToLocalSyncer::SyncCompleted(scoped_ptr<SyncTaskToken> token,
593                                        SyncStatusCode status) {
594  token->RecordLog(base::StringPrintf(
595      "[Remote -> Local]: Finished: action=%s, tracker=%" PRId64 " status=%s",
596      SyncActionToString(sync_action_), dirty_tracker_->tracker_id(),
597      SyncStatusCodeToString(status)));
598
599  if (sync_root_deletion_) {
600    FinalizeSync(token.Pass(), SYNC_STATUS_OK);
601    return;
602  }
603
604  if (status == SYNC_STATUS_RETRY) {
605    FinalizeSync(token.Pass(), SYNC_STATUS_OK);
606    return;
607  }
608
609  if (status != SYNC_STATUS_OK) {
610    FinalizeSync(token.Pass(), status);
611    return;
612  }
613
614  DCHECK(dirty_tracker_);
615  DCHECK(remote_metadata_);
616  DCHECK(remote_metadata_->has_details());
617
618  FileDetails updated_details = remote_metadata_->details();
619  if (!dirty_tracker_->active() ||
620      HasDisabledAppRoot(metadata_database(), *dirty_tracker_)) {
621    // Operations for an inactive tracker don't update file content.
622    if (dirty_tracker_->has_synced_details())
623      updated_details.set_md5(dirty_tracker_->synced_details().md5());
624    if (!dirty_tracker_->active()) {
625      // Keep missing true, as the change hasn't been synced to local.
626      updated_details.clear_md5();
627      updated_details.set_missing(true);
628    }
629  }
630  metadata_database()->UpdateTracker(
631      dirty_tracker_->tracker_id(),
632      updated_details,
633      base::Bind(&RemoteToLocalSyncer::FinalizeSync,
634                 weak_ptr_factory_.GetWeakPtr(),
635                 base::Passed(&token)));
636}
637
638void RemoteToLocalSyncer::FinalizeSync(scoped_ptr<SyncTaskToken> token,
639                                       SyncStatusCode status) {
640  if (prepared_) {
641    remote_change_processor()->FinalizeRemoteSync(
642        url_, false /* clear_local_change */,
643        base::Bind(SyncTaskManager::NotifyTaskDone,
644                   base::Passed(&token), status));
645    return;
646  }
647
648  SyncTaskManager::NotifyTaskDone(token.Pass(), status);
649}
650
651void RemoteToLocalSyncer::Prepare(const SyncStatusCallback& callback) {
652  bool should_success = BuildFileSystemURL(
653      metadata_database(), *dirty_tracker_, &url_);
654  DCHECK(should_success);
655  DCHECK(url_.is_valid());
656  remote_change_processor()->PrepareForProcessRemoteChange(
657      url_,
658      base::Bind(&RemoteToLocalSyncer::DidPrepare,
659                 weak_ptr_factory_.GetWeakPtr(),
660                 callback));
661}
662
663void RemoteToLocalSyncer::DidPrepare(const SyncStatusCallback& callback,
664                                     SyncStatusCode status,
665                                     const SyncFileMetadata& local_metadata,
666                                     const FileChangeList& local_changes) {
667  if (status != SYNC_STATUS_OK) {
668    callback.Run(status);
669    return;
670  }
671  prepared_ = true;
672
673  local_metadata_.reset(new SyncFileMetadata(local_metadata));
674  local_changes_.reset(new FileChangeList(local_changes));
675
676  callback.Run(status);
677}
678
679void RemoteToLocalSyncer::DeleteLocalFile(scoped_ptr<SyncTaskToken> token) {
680  remote_change_processor()->ApplyRemoteChange(
681      FileChange(FileChange::FILE_CHANGE_DELETE, SYNC_FILE_TYPE_UNKNOWN),
682      base::FilePath(),
683      url_,
684      SyncCompletedCallback(token.Pass()));
685}
686
687void RemoteToLocalSyncer::DownloadFile(scoped_ptr<SyncTaskToken> token) {
688  DCHECK(sync_context_->GetWorkerTaskRunner()->RunsTasksOnCurrentThread());
689
690  storage::ScopedFile file = CreateTemporaryFile(
691      make_scoped_refptr(sync_context_->GetWorkerTaskRunner()));
692
693  base::FilePath path = file.path();
694  drive_service()->DownloadFile(
695      path, remote_metadata_->file_id(),
696      base::Bind(&RemoteToLocalSyncer::DidDownloadFile,
697                 weak_ptr_factory_.GetWeakPtr(),
698                 base::Passed(&token), base::Passed(&file)),
699      google_apis::GetContentCallback(),
700      google_apis::ProgressCallback());
701}
702
703void RemoteToLocalSyncer::DidDownloadFile(scoped_ptr<SyncTaskToken> token,
704                                          storage::ScopedFile file,
705                                          google_apis::GDataErrorCode error,
706                                          const base::FilePath&) {
707  DCHECK(sync_context_->GetWorkerTaskRunner()->RunsTasksOnCurrentThread());
708
709  SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
710  if (status != SYNC_STATUS_OK) {
711    SyncCompleted(token.Pass(), status);
712    return;
713  }
714
715  base::FilePath path = file.path();
716  const std::string md5 = drive::util::GetMd5Digest(path);
717  if (md5.empty()) {
718    SyncCompleted(token.Pass(), SYNC_FILE_ERROR_NOT_FOUND);
719    return;
720  }
721
722  if (md5 != remote_metadata_->details().md5()) {
723    // File has been modified since last metadata retrieval.
724    SyncCompleted(token.Pass(), SYNC_STATUS_RETRY);
725    return;
726  }
727
728  remote_change_processor()->ApplyRemoteChange(
729      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, SYNC_FILE_TYPE_FILE),
730      path, url_,
731      base::Bind(&RemoteToLocalSyncer::DidApplyDownload,
732                 weak_ptr_factory_.GetWeakPtr(),
733                 base::Passed(&token), base::Passed(&file)));
734}
735
736void RemoteToLocalSyncer::DidApplyDownload(scoped_ptr<SyncTaskToken> token,
737                                           storage::ScopedFile,
738                                           SyncStatusCode status) {
739  SyncCompleted(token.Pass(), status);
740}
741
742void RemoteToLocalSyncer::CreateFolder(scoped_ptr<SyncTaskToken> token) {
743  remote_change_processor()->ApplyRemoteChange(
744      FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
745                 SYNC_FILE_TYPE_DIRECTORY),
746      base::FilePath(), url_,
747      SyncCompletedCallback(token.Pass()));
748}
749
750drive::DriveServiceInterface* RemoteToLocalSyncer::drive_service() {
751  return sync_context_->GetDriveService();
752}
753
754MetadataDatabase* RemoteToLocalSyncer::metadata_database() {
755  return sync_context_->GetMetadataDatabase();
756}
757
758RemoteChangeProcessor* RemoteToLocalSyncer::remote_change_processor() {
759  DCHECK(sync_context_->GetRemoteChangeProcessor());
760  return sync_context_->GetRemoteChangeProcessor();
761}
762
763SyncStatusCallback RemoteToLocalSyncer::SyncCompletedCallback(
764    scoped_ptr<SyncTaskToken> token) {
765  return base::Bind(&RemoteToLocalSyncer::SyncCompleted,
766                    weak_ptr_factory_.GetWeakPtr(),
767                    base::Passed(&token));
768}
769
770}  // namespace drive_backend
771}  // namespace sync_file_system
772