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