Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / sync_file_system / drive_backend / local_to_remote_syncer.cc
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 "storage/common/fileapi/file_system_util.h"
33
34 namespace sync_file_system {
35 namespace drive_backend {
36
37 namespace {
38
39 scoped_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
47 bool 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
57 bool 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
63 std::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
74 LocalToRemoteSyncer::LocalToRemoteSyncer(SyncEngineContext* sync_context,
75                                          const SyncFileMetadata& local_metadata,
76                                          const FileChange& local_change,
77                                          const base::FilePath& local_path,
78                                          const storage::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
94 LocalToRemoteSyncer::~LocalToRemoteSyncer() {
95 }
96
97 void 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   storage::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
250 void LocalToRemoteSyncer::MoveToBackground(const Continuation& continuation,
251                                            scoped_ptr<SyncTaskToken> token) {
252   scoped_ptr<TaskBlocker> blocker(new TaskBlocker);
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::UpdateTaskBlocker(
272       token.Pass(), blocker.Pass(),
273       base::Bind(&LocalToRemoteSyncer::ContinueAsBackgroundTask,
274                  weak_ptr_factory_.GetWeakPtr(),
275                  continuation));
276 }
277
278 void 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   //   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
316 void 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
334 void 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 = storage::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     MoveToBackground(
374         base::Bind(&LocalToRemoteSyncer::UpdateTrackerForReusedFolder,
375                    weak_ptr_factory_.GetWeakPtr(),
376                    remote_details),
377         token.Pass());
378     return;
379   }
380
381   MoveToBackground(base::Bind(&LocalToRemoteSyncer::CreateRemoteFolder,
382                               weak_ptr_factory_.GetWeakPtr()),
383                    token.Pass());
384 }
385
386 void LocalToRemoteSyncer::UpdateTrackerForReusedFolder(
387     const FileDetails& details,
388     scoped_ptr<SyncTaskToken> token) {
389   SyncStatusCode status = metadata_database()->UpdateTracker(
390       remote_file_tracker_->tracker_id(), details);
391   SyncCompleted(token.Pass(), status);
392 }
393
394 void LocalToRemoteSyncer::HandleExistingRemoteFile(
395     scoped_ptr<SyncTaskToken> token) {
396   DCHECK(remote_file_tracker_);
397   DCHECK(!remote_file_tracker_->dirty());
398   DCHECK(remote_file_tracker_->active());
399   DCHECK(remote_file_tracker_->has_synced_details());
400
401   if (local_is_missing_) {
402     // Local file deletion for existing remote file.
403     MoveToBackground(base::Bind(&LocalToRemoteSyncer::DeleteRemoteFile,
404                                 weak_ptr_factory_.GetWeakPtr()),
405                      token.Pass());
406     return;
407   }
408
409   DCHECK(local_change_.IsAddOrUpdate());
410   DCHECK(local_change_.IsFile() || local_change_.IsDirectory());
411
412   const FileDetails& synced_details = remote_file_tracker_->synced_details();
413   DCHECK(synced_details.file_kind() == FILE_KIND_FILE ||
414          synced_details.file_kind() == FILE_KIND_FOLDER);
415   if (local_change_.IsFile()) {
416     if (synced_details.file_kind() == FILE_KIND_FILE) {
417       // Non-conflicting local file update to existing remote regular file.
418       MoveToBackground(base::Bind(&LocalToRemoteSyncer::UploadExistingFile,
419                                   weak_ptr_factory_.GetWeakPtr()),
420                        token.Pass());
421       return;
422     }
423
424     DCHECK_EQ(FILE_KIND_FOLDER, synced_details.file_kind());
425     // Non-conflicting local file update to existing remote *folder*.
426     // Assuming this case as local folder deletion + local file creation, delete
427     // the remote folder and upload the file.
428     retry_on_success_ = true;
429     MoveToBackground(base::Bind(&LocalToRemoteSyncer::DeleteRemoteFile,
430                                 weak_ptr_factory_.GetWeakPtr()),
431                      token.Pass());
432     return;
433   }
434
435   DCHECK(local_change_.IsDirectory());
436   if (synced_details.file_kind() == FILE_KIND_FILE) {
437     // Non-conflicting local folder creation to existing remote *file*.
438     // Assuming this case as local file deletion + local folder creation, delete
439     // the remote file and create a remote folder.
440     retry_on_success_ = true;
441     MoveToBackground(base::Bind(&LocalToRemoteSyncer::DeleteRemoteFile,
442                                 weak_ptr_factory_.GetWeakPtr()),
443                      token.Pass());
444     return;
445   }
446
447   // Non-conflicting local folder creation to existing remote folder.
448   DCHECK_EQ(FILE_KIND_FOLDER, synced_details.file_kind());
449   SyncCompleted(token.Pass(), SYNC_STATUS_OK);
450 }
451
452 void LocalToRemoteSyncer::DeleteRemoteFile(scoped_ptr<SyncTaskToken> token) {
453   DCHECK(remote_file_tracker_);
454   DCHECK(remote_file_tracker_->has_synced_details());
455
456   sync_action_ = SYNC_ACTION_DELETED;
457   drive_service()->DeleteResource(
458       remote_file_tracker_->file_id(),
459       remote_file_tracker_->synced_details().etag(),
460       base::Bind(&LocalToRemoteSyncer::DidDeleteRemoteFile,
461                  weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)));
462 }
463
464 void LocalToRemoteSyncer::DidDeleteRemoteFile(
465     scoped_ptr<SyncTaskToken> token,
466     google_apis::GDataErrorCode error) {
467   SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
468   if (status != SYNC_STATUS_OK &&
469       error != google_apis::HTTP_NOT_FOUND &&
470       error != google_apis::HTTP_PRECONDITION &&
471       error != google_apis::HTTP_CONFLICT) {
472     SyncCompleted(token.Pass(), status);
473     return;
474   }
475
476   // Handle NOT_FOUND case as SUCCESS case.
477   // For PRECONDITION / CONFLICT case, the remote file is modified since the
478   // last sync completed.  As our policy for deletion-modification conflict
479   // resolution, ignore the local deletion.
480   if (status == SYNC_STATUS_OK ||
481       error == google_apis::HTTP_NOT_FOUND) {
482     SyncStatusCode status = metadata_database()->UpdateByDeletedRemoteFile(
483         remote_file_tracker_->file_id());
484     SyncCompleted(token.Pass(), status);
485     return;
486   }
487
488   SyncCompleted(token.Pass(), SYNC_STATUS_OK);
489 }
490
491 void LocalToRemoteSyncer::UploadExistingFile(scoped_ptr<SyncTaskToken> token) {
492   DCHECK(remote_file_tracker_);
493   DCHECK(remote_file_tracker_->has_synced_details());
494   DCHECK(sync_context_->GetWorkerTaskRunner()->RunsTasksOnCurrentThread());
495
496   const std::string local_file_md5 = drive::util::GetMd5Digest(local_path_);
497   if (local_file_md5 == remote_file_tracker_->synced_details().md5()) {
498     // Local file is not changed.
499     SyncCompleted(token.Pass(), SYNC_STATUS_OK);
500     return;
501   }
502
503   sync_action_ = SYNC_ACTION_UPDATED;
504
505   drive::DriveUploader::UploadExistingFileOptions options;
506   options.etag = remote_file_tracker_->synced_details().etag();
507   drive_uploader()->UploadExistingFile(
508       remote_file_tracker_->file_id(),
509       local_path_,
510       "application/octet_stream",
511       options,
512       base::Bind(&LocalToRemoteSyncer::DidUploadExistingFile,
513                  weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)),
514       google_apis::ProgressCallback());
515 }
516
517 void LocalToRemoteSyncer::DidUploadExistingFile(
518     scoped_ptr<SyncTaskToken> token,
519     google_apis::GDataErrorCode error,
520     const GURL&,
521     scoped_ptr<google_apis::FileResource> entry) {
522   if (error == google_apis::HTTP_PRECONDITION ||
523       error == google_apis::HTTP_CONFLICT ||
524       error == google_apis::HTTP_NOT_FOUND) {
525     // The remote file has unfetched remote change.  Fetch latest metadata and
526     // update database with it.
527     // TODO(tzik): Consider adding local side low-priority dirtiness handling to
528     // handle this as ListChangesTask.
529
530     needs_remote_change_listing_ = true;
531     UpdateRemoteMetadata(
532         remote_file_tracker_->file_id(),
533         token.Pass());
534     return;
535   }
536
537   SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
538   if (status != SYNC_STATUS_OK) {
539     SyncCompleted(token.Pass(), status);
540     return;
541   }
542
543   if (!entry) {
544     NOTREACHED();
545     SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
546     return;
547   }
548
549   DCHECK(entry);
550   status = metadata_database()->UpdateByFileResource(*entry);
551   if (status != SYNC_STATUS_OK) {
552     SyncCompleted(token.Pass(), status);
553     return;
554   }
555
556   FileMetadata file;
557   if (!metadata_database()->FindFileByFileID(
558           remote_file_tracker_->file_id(), &file)) {
559     NOTREACHED();
560     SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
561     return;
562   }
563
564   const FileDetails& details = file.details();
565   base::FilePath title = storage::VirtualPath::BaseName(target_path_);
566   if (!details.missing() &&
567       details.file_kind() == FILE_KIND_FILE &&
568       details.title() == title.AsUTF8Unsafe() &&
569       HasFileAsParent(details,
570                       remote_parent_folder_tracker_->file_id())) {
571     SyncStatusCode status = metadata_database()->UpdateTracker(
572         remote_file_tracker_->tracker_id(), file.details());
573     SyncCompleted(token.Pass(), status);
574     return;
575   }
576
577   SyncCompleted(token.Pass(), SYNC_STATUS_RETRY);
578 }
579
580 void LocalToRemoteSyncer::UpdateRemoteMetadata(
581     const std::string& file_id,
582     scoped_ptr<SyncTaskToken> token) {
583   DCHECK(remote_file_tracker_);
584
585   drive_service()->GetFileResource(
586       file_id,
587       base::Bind(&LocalToRemoteSyncer::DidGetRemoteMetadata,
588                  weak_ptr_factory_.GetWeakPtr(),
589                  file_id, base::Passed(&token)));
590 }
591
592 void LocalToRemoteSyncer::DidGetRemoteMetadata(
593     const std::string& file_id,
594     scoped_ptr<SyncTaskToken> token,
595     google_apis::GDataErrorCode error,
596     scoped_ptr<google_apis::FileResource> entry) {
597   DCHECK(sync_context_->GetWorkerTaskRunner()->RunsTasksOnCurrentThread());
598
599   if (error == google_apis::HTTP_NOT_FOUND) {
600     retry_on_success_ = true;
601     SyncStatusCode status =
602         metadata_database()->UpdateByDeletedRemoteFile(file_id);
603     SyncCompleted(token.Pass(), status);
604     return;
605   }
606
607   SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
608   if (status != SYNC_STATUS_OK) {
609     SyncCompleted(token.Pass(), status);
610     return;
611   }
612
613   if (!entry) {
614     NOTREACHED();
615     SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
616     return;
617   }
618
619   retry_on_success_ = true;
620   status = metadata_database()->UpdateByFileResource(*entry);
621   SyncCompleted(token.Pass(), status);
622 }
623
624 void LocalToRemoteSyncer::UploadNewFile(scoped_ptr<SyncTaskToken> token) {
625   DCHECK(remote_parent_folder_tracker_);
626
627   sync_action_ = SYNC_ACTION_ADDED;
628   base::FilePath title = storage::VirtualPath::BaseName(target_path_);
629   drive_uploader()->UploadNewFile(
630       remote_parent_folder_tracker_->file_id(),
631       local_path_,
632       title.AsUTF8Unsafe(),
633       GetMimeTypeFromTitle(title),
634       drive::DriveUploader::UploadNewFileOptions(),
635       base::Bind(&LocalToRemoteSyncer::DidUploadNewFile,
636                  weak_ptr_factory_.GetWeakPtr(),
637                  base::Passed(&token)),
638       google_apis::ProgressCallback());
639 }
640
641 void LocalToRemoteSyncer::DidUploadNewFile(
642     scoped_ptr<SyncTaskToken> token,
643     google_apis::GDataErrorCode error,
644     const GURL& upload_location,
645     scoped_ptr<google_apis::FileResource> entry) {
646   if (error == google_apis::HTTP_NOT_FOUND)
647     needs_remote_change_listing_ = true;
648
649   SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
650   if (status != SYNC_STATUS_OK) {
651     SyncCompleted(token.Pass(), status);
652     return;
653   }
654
655   if (!entry) {
656     NOTREACHED();
657     SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
658     return;
659   }
660
661   status = metadata_database()->ReplaceActiveTrackerWithNewResource(
662       remote_parent_folder_tracker_->tracker_id(), *entry);
663   SyncCompleted(token.Pass(), status);
664 }
665
666 void LocalToRemoteSyncer::CreateRemoteFolder(scoped_ptr<SyncTaskToken> token) {
667   DCHECK(remote_parent_folder_tracker_);
668
669   base::FilePath title = storage::VirtualPath::BaseName(target_path_);
670   sync_action_ = SYNC_ACTION_ADDED;
671
672   DCHECK(!folder_creator_);
673   folder_creator_.reset(new FolderCreator(
674       drive_service(), metadata_database(),
675       remote_parent_folder_tracker_->file_id(),
676       title.AsUTF8Unsafe()));
677   folder_creator_->Run(base::Bind(
678       &LocalToRemoteSyncer::DidCreateRemoteFolder,
679       weak_ptr_factory_.GetWeakPtr(),
680       base::Passed(&token)));
681 }
682
683 void LocalToRemoteSyncer::DidCreateRemoteFolder(
684     scoped_ptr<SyncTaskToken> token,
685     const std::string& file_id,
686     SyncStatusCode status) {
687   if (status == SYNC_FILE_ERROR_NOT_FOUND)
688     needs_remote_change_listing_ = true;
689
690   scoped_ptr<FolderCreator> deleter = folder_creator_.Pass();
691   if (status != SYNC_STATUS_OK) {
692     SyncCompleted(token.Pass(), status);
693     return;
694   }
695
696   status = SYNC_STATUS_FAILED;
697   MetadataDatabase::ActivationStatus activation_status =
698       metadata_database()->TryActivateTracker(
699           remote_parent_folder_tracker_->tracker_id(),
700           file_id, &status);
701   switch (activation_status) {
702     case MetadataDatabase::ACTIVATION_PENDING:
703       SyncCompleted(token.Pass(), status);
704       return;
705     case MetadataDatabase::ACTIVATION_FAILED_ANOTHER_ACTIVE_TRACKER:
706       // The activation failed due to another tracker that has another parent.
707       // Detach the folder from the current parent to avoid using this folder as
708       // active folder.
709       drive_service()->RemoveResourceFromDirectory(
710           remote_parent_folder_tracker_->file_id(), file_id,
711           base::Bind(&LocalToRemoteSyncer::DidDetachResourceForCreationConflict,
712                      weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)));
713       return;
714   }
715
716   NOTREACHED();
717   SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
718   return;
719 }
720
721 void LocalToRemoteSyncer::DidDetachResourceForCreationConflict(
722     scoped_ptr<SyncTaskToken> token,
723     google_apis::GDataErrorCode error) {
724   SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
725   if (status != SYNC_STATUS_OK) {
726     SyncCompleted(token.Pass(), status);
727     return;
728   }
729
730   SyncCompleted(token.Pass(), SYNC_STATUS_RETRY);
731 }
732
733 bool LocalToRemoteSyncer::IsContextReady() {
734   return sync_context_->GetDriveService() &&
735       sync_context_->GetDriveUploader() &&
736       sync_context_->GetMetadataDatabase();
737 }
738
739 drive::DriveServiceInterface* LocalToRemoteSyncer::drive_service() {
740   set_used_network(true);
741   return sync_context_->GetDriveService();
742 }
743
744 drive::DriveUploaderInterface* LocalToRemoteSyncer::drive_uploader() {
745   set_used_network(true);
746   return sync_context_->GetDriveUploader();
747 }
748
749 MetadataDatabase* LocalToRemoteSyncer::metadata_database() {
750   return sync_context_->GetMetadataDatabase();
751 }
752
753 }  // namespace drive_backend
754 }  // namespace sync_file_system