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