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