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