local_to_remote_syncer.cc revision 6e8cce623b6e4fe0c9e4af605d675dd9d0338c38
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/local_to_remote_syncer.h"
6
7#include <string>
8#include <vector>
9
10#include "base/callback.h"
11#include "base/format_macros.h"
12#include "base/location.h"
13#include "base/logging.h"
14#include "base/sequenced_task_runner.h"
15#include "base/strings/stringprintf.h"
16#include "base/task_runner_util.h"
17#include "chrome/browser/drive/drive_api_util.h"
18#include "chrome/browser/drive/drive_service_interface.h"
19#include "chrome/browser/drive/drive_uploader.h"
20#include "chrome/browser/sync_file_system/drive_backend/callback_helper.h"
21#include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h"
22#include "chrome/browser/sync_file_system/drive_backend/drive_backend_util.h"
23#include "chrome/browser/sync_file_system/drive_backend/folder_creator.h"
24#include "chrome/browser/sync_file_system/drive_backend/metadata_database.h"
25#include "chrome/browser/sync_file_system/drive_backend/metadata_database.pb.h"
26#include "chrome/browser/sync_file_system/drive_backend/sync_engine_context.h"
27#include "chrome/browser/sync_file_system/drive_backend/sync_task_manager.h"
28#include "chrome/browser/sync_file_system/drive_backend/sync_task_token.h"
29#include "chrome/browser/sync_file_system/logger.h"
30#include "google_apis/drive/drive_api_parser.h"
31#include "net/base/mime_util.h"
32#include "webkit/common/fileapi/file_system_util.h"
33
34namespace sync_file_system {
35namespace drive_backend {
36
37namespace {
38
39scoped_ptr<FileTracker> FindTrackerByID(MetadataDatabase* metadata_database,
40                                        int64 tracker_id) {
41  scoped_ptr<FileTracker> tracker(new FileTracker);
42  if (metadata_database->FindTrackerByTrackerID(tracker_id, tracker.get()))
43    return tracker.Pass();
44  return scoped_ptr<FileTracker>();
45}
46
47bool GetKnownChangeID(MetadataDatabase* metadata_database,
48                      const std::string& file_id,
49                      int64* change_id) {
50  FileMetadata remote_file_metadata;
51  if (!metadata_database->FindFileByFileID(file_id, &remote_file_metadata))
52    return false;
53  *change_id = remote_file_metadata.details().change_id();
54  return true;
55}
56
57bool IsLocalFileMissing(const SyncFileMetadata& local_metadata,
58                        const FileChange& local_change) {
59  return local_metadata.file_type == SYNC_FILE_TYPE_UNKNOWN ||
60         local_change.IsDelete();
61}
62
63std::string GetMimeTypeFromTitle(const base::FilePath& title) {
64  base::FilePath::StringType extension = title.Extension();
65  std::string mime_type;
66  if (extension.empty() ||
67      !net::GetWellKnownMimeTypeFromExtension(extension.substr(1), &mime_type))
68    return kMimeTypeOctetStream;
69  return mime_type;
70}
71
72}  // namespace
73
74LocalToRemoteSyncer::LocalToRemoteSyncer(SyncEngineContext* sync_context,
75                                         const SyncFileMetadata& local_metadata,
76                                         const FileChange& local_change,
77                                         const base::FilePath& local_path,
78                                         const fileapi::FileSystemURL& url)
79    : sync_context_(sync_context),
80      local_change_(local_change),
81      local_is_missing_(IsLocalFileMissing(local_metadata, local_change)),
82      local_path_(local_path),
83      url_(url),
84      sync_action_(SYNC_ACTION_NONE),
85      remote_file_change_id_(0),
86      retry_on_success_(false),
87      needs_remote_change_listing_(false),
88      weak_ptr_factory_(this) {
89  DCHECK(local_is_missing_ ||
90         local_change.file_type() == local_metadata.file_type)
91      << local_change.DebugString() << " metadata:" << local_metadata.file_type;
92}
93
94LocalToRemoteSyncer::~LocalToRemoteSyncer() {
95}
96
97void LocalToRemoteSyncer::RunPreflight(scoped_ptr<SyncTaskToken> token) {
98  token->InitializeTaskLog("Local -> Remote");
99
100  if (!IsContextReady()) {
101    token->RecordLog("Context not ready.");
102    SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED);
103    return;
104  }
105
106  token->RecordLog(base::StringPrintf(
107      "Start: %s on %s@%s %s",
108      local_change_.DebugString().c_str(),
109      url_.path().AsUTF8Unsafe().c_str(),
110      url_.origin().host().c_str(),
111      local_is_missing_ ? "(missing)" : ""));
112
113  if (local_is_missing_ && !local_change_.IsDelete()) {
114    // Stray file, we can just return.
115    token->RecordLog("Missing file for non-delete change.");
116    SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_OK);
117    return;
118  }
119
120  std::string app_id = url_.origin().host();
121  base::FilePath path = url_.path();
122
123  scoped_ptr<FileTracker> active_ancestor_tracker(new FileTracker);
124  base::FilePath active_ancestor_path;
125  if (!metadata_database()->FindNearestActiveAncestor(
126          app_id, path,
127          active_ancestor_tracker.get(), &active_ancestor_path)) {
128    // The app is disabled or not registered.
129    token->RecordLog("App is disabled or not registered");
130    SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_UNKNOWN_ORIGIN);
131    return;
132  }
133  DCHECK(active_ancestor_tracker->active());
134  DCHECK(active_ancestor_tracker->has_synced_details());
135  const FileDetails& active_ancestor_details =
136      active_ancestor_tracker->synced_details();
137
138  // TODO(tzik): Consider handling
139  // active_ancestor_tracker->synced_details().missing() case.
140
141  DCHECK(active_ancestor_details.file_kind() == FILE_KIND_FILE ||
142         active_ancestor_details.file_kind() == FILE_KIND_FOLDER);
143
144  base::FilePath missing_entries;
145  if (active_ancestor_path.empty()) {
146    missing_entries = path;
147  } else if (active_ancestor_path != path) {
148    if (!active_ancestor_path.AppendRelativePath(path, &missing_entries)) {
149      NOTREACHED();
150      token->RecordLog(base::StringPrintf(
151          "Detected invalid ancestor: %s",
152          active_ancestor_path.value().c_str()));
153      SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED);
154      return;
155    }
156  }
157
158  std::vector<base::FilePath::StringType> missing_components;
159  fileapi::VirtualPath::GetComponents(missing_entries, &missing_components);
160
161  if (!missing_components.empty()) {
162    if (local_is_missing_) {
163      token->RecordLog("Both local and remote are marked missing");
164      // !IsDelete() but SYNC_FILE_TYPE_UNKNOWN could happen when a file is
165      // deleted by recursive deletion (which is not recorded by tracker)
166      // but there're remaining changes for the same file in the tracker.
167
168      // Local file is deleted and remote file is missing, already deleted or
169      // not yet synced.  There is nothing to do for the file.
170      SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_OK);
171      return;
172    }
173  }
174
175  if (missing_components.size() > 1) {
176    // The original target doesn't have remote file and parent.
177    // Try creating the parent first.
178    if (active_ancestor_details.file_kind() == FILE_KIND_FOLDER) {
179      remote_parent_folder_tracker_ = active_ancestor_tracker.Pass();
180      target_path_ = active_ancestor_path.Append(missing_components[0]);
181      token->RecordLog("Detected missing parent folder.");
182
183      retry_on_success_ = true;
184      MoveToBackground(base::Bind(&LocalToRemoteSyncer::CreateRemoteFolder,
185                                  weak_ptr_factory_.GetWeakPtr()),
186                       token.Pass());
187      return;
188    }
189
190    DCHECK(active_ancestor_details.file_kind() == FILE_KIND_FILE);
191    remote_parent_folder_tracker_ =
192        FindTrackerByID(metadata_database(),
193                        active_ancestor_tracker->parent_tracker_id());
194    remote_file_tracker_ = active_ancestor_tracker.Pass();
195    target_path_ = active_ancestor_path;
196    token->RecordLog("Detected non-folder file in its path.");
197
198    retry_on_success_ = true;
199    MoveToBackground(base::Bind(&LocalToRemoteSyncer::DeleteRemoteFile,
200                                weak_ptr_factory_.GetWeakPtr()),
201                     token.Pass());
202    return;
203  }
204
205  if (missing_components.empty()) {
206    // The original target has remote active file/folder.
207    remote_parent_folder_tracker_ =
208        FindTrackerByID(metadata_database(),
209                        active_ancestor_tracker->parent_tracker_id());
210    remote_file_tracker_ = active_ancestor_tracker.Pass();
211    target_path_ = url_.path();
212    DCHECK(target_path_ == active_ancestor_path);
213
214    if (remote_file_tracker_->dirty()) {
215      token->RecordLog(base::StringPrintf(
216          "Detected conflicting dirty tracker:%" PRId64,
217           remote_file_tracker_->tracker_id()));
218      // Both local and remote file has pending modification.
219      HandleConflict(token.Pass());
220      return;
221    }
222
223    // Non-conflicting file/folder update case.
224    HandleExistingRemoteFile(token.Pass());
225    return;
226  }
227
228  DCHECK(local_change_.IsAddOrUpdate());
229  DCHECK_EQ(1u, missing_components.size());
230  // The original target has remote parent folder and doesn't have remote active
231  // file.
232  // Upload the file as a new file or create a folder.
233  remote_parent_folder_tracker_ = active_ancestor_tracker.Pass();
234  target_path_ = url_.path();
235  DCHECK(target_path_ == active_ancestor_path.Append(missing_components[0]));
236  if (local_change_.file_type() == SYNC_FILE_TYPE_FILE) {
237    token->RecordLog("Detected a new file.");
238    MoveToBackground(base::Bind(&LocalToRemoteSyncer::UploadNewFile,
239                                weak_ptr_factory_.GetWeakPtr()),
240                     token.Pass());
241    return;
242  }
243
244  token->RecordLog("Detected a new folder.");
245  MoveToBackground(base::Bind(&LocalToRemoteSyncer::CreateRemoteFolder,
246                              weak_ptr_factory_.GetWeakPtr()),
247                   token.Pass());
248}
249
250void LocalToRemoteSyncer::MoveToBackground(const Continuation& continuation,
251                                           scoped_ptr<SyncTaskToken> token) {
252  scoped_ptr<BlockingFactor> blocker(new BlockingFactor);
253  blocker->app_id = url_.origin().host();
254  blocker->paths.push_back(target_path_);
255
256  if (remote_file_tracker_) {
257    if (!GetKnownChangeID(metadata_database(),
258                          remote_file_tracker_->file_id(),
259                          &remote_file_change_id_)) {
260      NOTREACHED();
261      SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
262      return;
263    }
264
265    blocker->tracker_ids.push_back(remote_file_tracker_->tracker_id());
266    blocker->file_ids.push_back(remote_file_tracker_->file_id());
267  }
268
269  // Run current task as a background task with |blocker|.
270  // After the invocation of ContinueAsBackgroundTask
271  SyncTaskManager::UpdateBlockingFactor(
272      token.Pass(), blocker.Pass(),
273      base::Bind(&LocalToRemoteSyncer::ContinueAsBackgroundTask,
274                 weak_ptr_factory_.GetWeakPtr(),
275                 continuation));
276}
277
278void LocalToRemoteSyncer::ContinueAsBackgroundTask(
279    const Continuation& continuation,
280    scoped_ptr<SyncTaskToken> token) {
281  // The SyncTask runs as a background task beyond this point.
282  // Note that any task can run between MoveToBackground() and
283  // ContinueAsBackgroundTask(), so we need to make sure other tasks didn't
284  // affect to the current LocalToRemoteSyncer task.
285  //
286  // - For RemoteToLocalSyncer, it doesn't actually run beyond
287  //   PrepareForProcessRemoteChange() since it should be blocked in
288  //   LocalFileSyncService.
289  // - For ListChangesTask, it may update FileMetatada together with |change_id|
290  //   and may delete FileTracker.  So, ensure |change_id| is not changed and
291  //   check if FileTracker still exists.
292  // - For UninstallAppTask, it may also delete FileMetadata and FileTracker.
293  //   And also, check if FileTracker still exists.
294  // - Others, SyncEngineInitializer and RegisterAppTask doesn't affect to
295  //   LocalToRemoteSyncer.
296  if (remote_file_tracker_) {
297    int64 latest_change_id = 0;
298    if (!GetKnownChangeID(metadata_database(),
299                          remote_file_tracker_->file_id(),
300                          &latest_change_id) ||
301        latest_change_id > remote_file_change_id_) {
302      SyncCompleted(token.Pass(), SYNC_STATUS_RETRY);
303      return;
304    }
305
306    if (!metadata_database()->FindTrackerByTrackerID(
307            remote_file_tracker_->tracker_id(), NULL)) {
308      SyncCompleted(token.Pass(), SYNC_STATUS_RETRY);
309      return;
310    }
311  }
312
313  continuation.Run(token.Pass());
314}
315
316void LocalToRemoteSyncer::SyncCompleted(scoped_ptr<SyncTaskToken> token,
317                                        SyncStatusCode status) {
318  if (status == SYNC_STATUS_OK && retry_on_success_)
319    status = SYNC_STATUS_RETRY;
320
321  if (needs_remote_change_listing_)
322    status = SYNC_STATUS_FILE_BUSY;
323
324  token->RecordLog(base::StringPrintf(
325      "Finished: action=%s, status=%s for %s@%s",
326      SyncActionToString(sync_action_),
327      SyncStatusCodeToString(status),
328      target_path_.AsUTF8Unsafe().c_str(),
329      url_.origin().host().c_str()));
330
331  SyncTaskManager::NotifyTaskDone(token.Pass(), status);
332}
333
334void LocalToRemoteSyncer::HandleConflict(scoped_ptr<SyncTaskToken> token) {
335  DCHECK(remote_file_tracker_);
336  DCHECK(remote_file_tracker_->has_synced_details());
337  DCHECK(remote_file_tracker_->active());
338  DCHECK(remote_file_tracker_->dirty());
339
340  if (local_is_missing_) {
341    SyncCompleted(token.Pass(), SYNC_STATUS_OK);
342    return;
343  }
344
345  if (local_change_.IsFile()) {
346    // Upload the conflicting file as a new file and let ConflictResolver
347    // resolve it.
348    MoveToBackground(base::Bind(&LocalToRemoteSyncer::UploadNewFile,
349                                weak_ptr_factory_.GetWeakPtr()),
350                     token.Pass());
351    return;
352  }
353
354  DCHECK(local_change_.IsDirectory());
355  // Check if we can reuse the remote folder.
356  FileMetadata remote_file_metadata;
357  if (!metadata_database()->FindFileByFileID(
358          remote_file_tracker_->file_id(), &remote_file_metadata)) {
359    NOTREACHED();
360    MoveToBackground(base::Bind(&LocalToRemoteSyncer::CreateRemoteFolder,
361                                weak_ptr_factory_.GetWeakPtr()),
362                     token.Pass());
363    return;
364  }
365
366  const FileDetails& remote_details = remote_file_metadata.details();
367  base::FilePath title = fileapi::VirtualPath::BaseName(target_path_);
368  if (!remote_details.missing() &&
369      remote_details.file_kind() == FILE_KIND_FOLDER &&
370      remote_details.title() == title.AsUTF8Unsafe() &&
371      HasFileAsParent(remote_details,
372                      remote_parent_folder_tracker_->file_id())) {
373
374    MoveToBackground(
375        base::Bind(&LocalToRemoteSyncer::UpdateTrackerForReusedFolder,
376                   weak_ptr_factory_.GetWeakPtr(),
377                   remote_details),
378        token.Pass());
379    return;
380  }
381
382  MoveToBackground(base::Bind(&LocalToRemoteSyncer::CreateRemoteFolder,
383                              weak_ptr_factory_.GetWeakPtr()),
384                   token.Pass());
385}
386
387void LocalToRemoteSyncer::UpdateTrackerForReusedFolder(
388    const FileDetails& details,
389    scoped_ptr<SyncTaskToken> token) {
390  metadata_database()->UpdateTracker(
391      remote_file_tracker_->tracker_id(), details,
392      base::Bind(&LocalToRemoteSyncer::SyncCompleted,
393                 weak_ptr_factory_.GetWeakPtr(),
394                 base::Passed(&token)));
395}
396
397void LocalToRemoteSyncer::HandleExistingRemoteFile(
398    scoped_ptr<SyncTaskToken> token) {
399  DCHECK(remote_file_tracker_);
400  DCHECK(!remote_file_tracker_->dirty());
401  DCHECK(remote_file_tracker_->active());
402  DCHECK(remote_file_tracker_->has_synced_details());
403
404  if (local_is_missing_) {
405    // Local file deletion for existing remote file.
406    MoveToBackground(base::Bind(&LocalToRemoteSyncer::DeleteRemoteFile,
407                                weak_ptr_factory_.GetWeakPtr()),
408                     token.Pass());
409    return;
410  }
411
412  DCHECK(local_change_.IsAddOrUpdate());
413  DCHECK(local_change_.IsFile() || local_change_.IsDirectory());
414
415  const FileDetails& synced_details = remote_file_tracker_->synced_details();
416  DCHECK(synced_details.file_kind() == FILE_KIND_FILE ||
417         synced_details.file_kind() == FILE_KIND_FOLDER);
418  if (local_change_.IsFile()) {
419    if (synced_details.file_kind() == FILE_KIND_FILE) {
420      // Non-conflicting local file update to existing remote regular file.
421      MoveToBackground(base::Bind(&LocalToRemoteSyncer::UploadExistingFile,
422                                  weak_ptr_factory_.GetWeakPtr()),
423                       token.Pass());
424      return;
425    }
426
427    DCHECK_EQ(FILE_KIND_FOLDER, synced_details.file_kind());
428    // Non-conflicting local file update to existing remote *folder*.
429    // Assuming this case as local folder deletion + local file creation, delete
430    // the remote folder and upload the file.
431    retry_on_success_ = true;
432    MoveToBackground(base::Bind(&LocalToRemoteSyncer::DeleteRemoteFile,
433                                weak_ptr_factory_.GetWeakPtr()),
434                     token.Pass());
435    return;
436  }
437
438  DCHECK(local_change_.IsDirectory());
439  if (synced_details.file_kind() == FILE_KIND_FILE) {
440    // Non-conflicting local folder creation to existing remote *file*.
441    // Assuming this case as local file deletion + local folder creation, delete
442    // the remote file and create a remote folder.
443    retry_on_success_ = true;
444    MoveToBackground(base::Bind(&LocalToRemoteSyncer::DeleteRemoteFile,
445                                weak_ptr_factory_.GetWeakPtr()),
446                     token.Pass());
447    return;
448  }
449
450  // Non-conflicting local folder creation to existing remote folder.
451  DCHECK_EQ(FILE_KIND_FOLDER, synced_details.file_kind());
452  SyncCompleted(token.Pass(), SYNC_STATUS_OK);
453}
454
455void LocalToRemoteSyncer::DeleteRemoteFile(scoped_ptr<SyncTaskToken> token) {
456  DCHECK(remote_file_tracker_);
457  DCHECK(remote_file_tracker_->has_synced_details());
458
459  sync_action_ = SYNC_ACTION_DELETED;
460  drive_service()->DeleteResource(
461      remote_file_tracker_->file_id(),
462      remote_file_tracker_->synced_details().etag(),
463      base::Bind(&LocalToRemoteSyncer::DidDeleteRemoteFile,
464                 weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)));
465}
466
467void LocalToRemoteSyncer::DidDeleteRemoteFile(
468    scoped_ptr<SyncTaskToken> token,
469    google_apis::GDataErrorCode error) {
470  SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
471  if (status != SYNC_STATUS_OK &&
472      error != google_apis::HTTP_NOT_FOUND &&
473      error != google_apis::HTTP_PRECONDITION &&
474      error != google_apis::HTTP_CONFLICT) {
475    SyncCompleted(token.Pass(), status);
476    return;
477  }
478
479  // Handle NOT_FOUND case as SUCCESS case.
480  // For PRECONDITION / CONFLICT case, the remote file is modified since the
481  // last sync completed.  As our policy for deletion-modification conflict
482  // resolution, ignore the local deletion.
483  if (status == SYNC_STATUS_OK ||
484      error == google_apis::HTTP_NOT_FOUND) {
485    metadata_database()->UpdateByDeletedRemoteFile(
486        remote_file_tracker_->file_id(),
487        base::Bind(&LocalToRemoteSyncer::SyncCompleted,
488                   weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)));
489    return;
490  }
491
492  SyncCompleted(token.Pass(), SYNC_STATUS_OK);
493}
494
495void LocalToRemoteSyncer::UploadExistingFile(scoped_ptr<SyncTaskToken> token) {
496  DCHECK(remote_file_tracker_);
497  DCHECK(remote_file_tracker_->has_synced_details());
498  DCHECK(sync_context_->GetWorkerTaskRunner()->RunsTasksOnCurrentThread());
499
500  const std::string local_file_md5 = drive::util::GetMd5Digest(local_path_);
501  if (local_file_md5 == remote_file_tracker_->synced_details().md5()) {
502    // Local file is not changed.
503    SyncCompleted(token.Pass(), SYNC_STATUS_OK);
504    return;
505  }
506
507  sync_action_ = SYNC_ACTION_UPDATED;
508
509  drive::DriveUploader::UploadExistingFileOptions options;
510  options.etag = remote_file_tracker_->synced_details().etag();
511  drive_uploader()->UploadExistingFile(
512      remote_file_tracker_->file_id(),
513      local_path_,
514      "application/octet_stream",
515      options,
516      base::Bind(&LocalToRemoteSyncer::DidUploadExistingFile,
517                 weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)),
518      google_apis::ProgressCallback());
519}
520
521void LocalToRemoteSyncer::DidUploadExistingFile(
522    scoped_ptr<SyncTaskToken> token,
523    google_apis::GDataErrorCode error,
524    const GURL&,
525    scoped_ptr<google_apis::FileResource> entry) {
526  if (error == google_apis::HTTP_PRECONDITION ||
527      error == google_apis::HTTP_CONFLICT ||
528      error == google_apis::HTTP_NOT_FOUND) {
529    // The remote file has unfetched remote change.  Fetch latest metadata and
530    // update database with it.
531    // TODO(tzik): Consider adding local side low-priority dirtiness handling to
532    // handle this as ListChangesTask.
533
534    needs_remote_change_listing_ = true;
535    UpdateRemoteMetadata(
536        remote_file_tracker_->file_id(),
537        token.Pass());
538    return;
539  }
540
541  SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
542  if (status != SYNC_STATUS_OK) {
543    SyncCompleted(token.Pass(), status);
544    return;
545  }
546
547  if (!entry) {
548    NOTREACHED();
549    SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
550    return;
551  }
552
553  DCHECK(entry);
554  metadata_database()->UpdateByFileResource(
555      *entry,
556      base::Bind(&LocalToRemoteSyncer::DidUpdateDatabaseForUploadExistingFile,
557                 weak_ptr_factory_.GetWeakPtr(),
558                 base::Passed(&token)));
559}
560
561void LocalToRemoteSyncer::DidUpdateDatabaseForUploadExistingFile(
562    scoped_ptr<SyncTaskToken> token,
563    SyncStatusCode status) {
564  if (status != SYNC_STATUS_OK) {
565    SyncCompleted(token.Pass(), status);
566    return;
567  }
568
569  FileMetadata file;
570  if (!metadata_database()->FindFileByFileID(
571          remote_file_tracker_->file_id(), &file)) {
572    NOTREACHED();
573    SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
574    return;
575  }
576
577  const FileDetails& details = file.details();
578  base::FilePath title = fileapi::VirtualPath::BaseName(target_path_);
579  if (!details.missing() &&
580      details.file_kind() == FILE_KIND_FILE &&
581      details.title() == title.AsUTF8Unsafe() &&
582      HasFileAsParent(details,
583                      remote_parent_folder_tracker_->file_id())) {
584    metadata_database()->UpdateTracker(
585        remote_file_tracker_->tracker_id(),
586        file.details(),
587        base::Bind(&LocalToRemoteSyncer::SyncCompleted,
588                   weak_ptr_factory_.GetWeakPtr(),
589                   base::Passed(&token)));
590    return;
591  }
592
593  SyncCompleted(token.Pass(), SYNC_STATUS_RETRY);
594}
595
596void LocalToRemoteSyncer::UpdateRemoteMetadata(
597    const std::string& file_id,
598    scoped_ptr<SyncTaskToken> token) {
599  DCHECK(remote_file_tracker_);
600
601  drive_service()->GetFileResource(
602      file_id,
603      base::Bind(&LocalToRemoteSyncer::DidGetRemoteMetadata,
604                 weak_ptr_factory_.GetWeakPtr(),
605                 file_id, base::Passed(&token)));
606}
607
608void LocalToRemoteSyncer::DidGetRemoteMetadata(
609    const std::string& file_id,
610    scoped_ptr<SyncTaskToken> token,
611    google_apis::GDataErrorCode error,
612    scoped_ptr<google_apis::FileResource> entry) {
613  DCHECK(sync_context_->GetWorkerTaskRunner()->RunsTasksOnCurrentThread());
614
615  if (error == google_apis::HTTP_NOT_FOUND) {
616    retry_on_success_ = true;
617    metadata_database()->UpdateByDeletedRemoteFile(
618        file_id,
619        base::Bind(&LocalToRemoteSyncer::SyncCompleted,
620                   weak_ptr_factory_.GetWeakPtr(),
621                   base::Passed(&token)));
622    return;
623  }
624
625  SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
626  if (status != SYNC_STATUS_OK) {
627    SyncCompleted(token.Pass(), status);
628    return;
629  }
630
631  if (!entry) {
632    NOTREACHED();
633    SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
634    return;
635  }
636
637  retry_on_success_ = true;
638  metadata_database()->UpdateByFileResource(
639      *entry,
640      base::Bind(&LocalToRemoteSyncer::SyncCompleted,
641                 weak_ptr_factory_.GetWeakPtr(),
642                 base::Passed(&token)));
643}
644
645void LocalToRemoteSyncer::UploadNewFile(scoped_ptr<SyncTaskToken> token) {
646  DCHECK(remote_parent_folder_tracker_);
647
648  sync_action_ = SYNC_ACTION_ADDED;
649  base::FilePath title = fileapi::VirtualPath::BaseName(target_path_);
650  drive_uploader()->UploadNewFile(
651      remote_parent_folder_tracker_->file_id(),
652      local_path_,
653      title.AsUTF8Unsafe(),
654      GetMimeTypeFromTitle(title),
655      drive::DriveUploader::UploadNewFileOptions(),
656      base::Bind(&LocalToRemoteSyncer::DidUploadNewFile,
657                 weak_ptr_factory_.GetWeakPtr(),
658                 base::Passed(&token)),
659      google_apis::ProgressCallback());
660}
661
662void LocalToRemoteSyncer::DidUploadNewFile(
663    scoped_ptr<SyncTaskToken> token,
664    google_apis::GDataErrorCode error,
665    const GURL& upload_location,
666    scoped_ptr<google_apis::FileResource> entry) {
667  if (error == google_apis::HTTP_NOT_FOUND)
668    needs_remote_change_listing_ = true;
669
670  SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
671  if (status != SYNC_STATUS_OK) {
672    SyncCompleted(token.Pass(), status);
673    return;
674  }
675
676  if (!entry) {
677    NOTREACHED();
678    SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
679    return;
680  }
681
682  metadata_database()->ReplaceActiveTrackerWithNewResource(
683      remote_parent_folder_tracker_->tracker_id(), *entry,
684      base::Bind(&LocalToRemoteSyncer::SyncCompleted,
685                 weak_ptr_factory_.GetWeakPtr(),
686                 base::Passed(&token)));
687}
688
689void LocalToRemoteSyncer::CreateRemoteFolder(
690    scoped_ptr<SyncTaskToken> token) {
691  DCHECK(remote_parent_folder_tracker_);
692
693  base::FilePath title = fileapi::VirtualPath::BaseName(target_path_);
694  sync_action_ = SYNC_ACTION_ADDED;
695
696  DCHECK(!folder_creator_);
697  folder_creator_.reset(new FolderCreator(
698      drive_service(), metadata_database(),
699      remote_parent_folder_tracker_->file_id(),
700      title.AsUTF8Unsafe()));
701  folder_creator_->Run(base::Bind(
702      &LocalToRemoteSyncer::DidCreateRemoteFolder,
703      weak_ptr_factory_.GetWeakPtr(),
704      base::Passed(&token)));
705}
706
707void LocalToRemoteSyncer::DidCreateRemoteFolder(
708    scoped_ptr<SyncTaskToken> token,
709    const std::string& file_id,
710    SyncStatusCode status) {
711  if (status == SYNC_FILE_ERROR_NOT_FOUND)
712    needs_remote_change_listing_ = true;
713
714  scoped_ptr<FolderCreator> deleter = folder_creator_.Pass();
715  if (status != SYNC_STATUS_OK) {
716    SyncCompleted(token.Pass(), status);
717    return;
718  }
719
720  MetadataDatabase::ActivationStatus activation_status =
721      metadata_database()->TryActivateTracker(
722          remote_parent_folder_tracker_->tracker_id(),
723          file_id,
724          base::Bind(&LocalToRemoteSyncer::SyncCompleted,
725                     weak_ptr_factory_.GetWeakPtr(),
726                     base::Passed(token.Pass())));
727  switch (activation_status) {
728    case MetadataDatabase::ACTIVATION_PENDING:
729      // The task will be finalized by the callback passed to MetadataDatabase
730      // in this case.
731      return;
732    case MetadataDatabase::ACTIVATION_FAILED_ANOTHER_ACTIVE_TRACKER:
733      // The activation failed due to another tracker that has another parent.
734      // Detach the folder from the current parent to avoid using this folder as
735      // active folder.
736      drive_service()->RemoveResourceFromDirectory(
737          remote_parent_folder_tracker_->file_id(), file_id,
738          base::Bind(&LocalToRemoteSyncer::DidDetachResourceForCreationConflict,
739                     weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)));
740      return;
741  }
742
743  NOTREACHED();
744  SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
745  return;
746}
747
748void LocalToRemoteSyncer::DidDetachResourceForCreationConflict(
749    scoped_ptr<SyncTaskToken> token,
750    google_apis::GDataErrorCode error) {
751  SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
752  if (status != SYNC_STATUS_OK) {
753    SyncCompleted(token.Pass(), status);
754    return;
755  }
756
757  SyncCompleted(token.Pass(), SYNC_STATUS_RETRY);
758}
759
760bool LocalToRemoteSyncer::IsContextReady() {
761  return sync_context_->GetDriveService() &&
762      sync_context_->GetDriveUploader() &&
763      sync_context_->GetMetadataDatabase();
764}
765
766drive::DriveServiceInterface* LocalToRemoteSyncer::drive_service() {
767  set_used_network(true);
768  return sync_context_->GetDriveService();
769}
770
771drive::DriveUploaderInterface* LocalToRemoteSyncer::drive_uploader() {
772  set_used_network(true);
773  return sync_context_->GetDriveUploader();
774}
775
776MetadataDatabase* LocalToRemoteSyncer::metadata_database() {
777  return sync_context_->GetMetadataDatabase();
778}
779
780}  // namespace drive_backend
781}  // namespace sync_file_system
782