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.
5 #include "chrome/browser/sync_file_system/drive_backend/local_to_remote_syncer.h"
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"
34 namespace sync_file_system {
35 namespace drive_backend {
39 scoped_ptr<FileTracker> FindTrackerByID(MetadataDatabase* metadata_database,
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>();
47 bool GetKnownChangeID(MetadataDatabase* metadata_database,
48 const std::string& file_id,
50 FileMetadata remote_file_metadata;
51 if (!metadata_database->FindFileByFileID(file_id, &remote_file_metadata))
53 *change_id = remote_file_metadata.details().change_id();
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();
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;
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),
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;
94 LocalToRemoteSyncer::~LocalToRemoteSyncer() {
97 void LocalToRemoteSyncer::RunPreflight(scoped_ptr<SyncTaskToken> token) {
98 token->InitializeTaskLog("Local -> Remote");
100 if (!IsContextReady()) {
101 token->RecordLog("Context not ready.");
102 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED);
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)" : ""));
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);
120 std::string app_id = url_.origin().host();
121 base::FilePath path = url_.path();
123 scoped_ptr<FileTracker> active_ancestor_tracker(new FileTracker);
124 base::FilePath active_ancestor_path;
125 if (!metadata_database()->FindNearestActiveAncestor(
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);
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();
138 // TODO(tzik): Consider handling
139 // active_ancestor_tracker->synced_details().missing() case.
141 DCHECK(active_ancestor_details.file_kind() == FILE_KIND_FILE ||
142 active_ancestor_details.file_kind() == FILE_KIND_FOLDER);
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)) {
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);
158 std::vector<base::FilePath::StringType> missing_components;
159 storage::VirtualPath::GetComponents(missing_entries, &missing_components);
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.
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);
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.");
183 retry_on_success_ = true;
184 MoveToBackground(base::Bind(&LocalToRemoteSyncer::CreateRemoteFolder,
185 weak_ptr_factory_.GetWeakPtr()),
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.");
198 retry_on_success_ = true;
199 MoveToBackground(base::Bind(&LocalToRemoteSyncer::DeleteRemoteFile,
200 weak_ptr_factory_.GetWeakPtr()),
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);
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());
223 // Non-conflicting file/folder update case.
224 HandleExistingRemoteFile(token.Pass());
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
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()),
244 token->RecordLog("Detected a new folder.");
245 MoveToBackground(base::Bind(&LocalToRemoteSyncer::CreateRemoteFolder,
246 weak_ptr_factory_.GetWeakPtr()),
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_);
256 if (remote_file_tracker_) {
257 if (!GetKnownChangeID(metadata_database(),
258 remote_file_tracker_->file_id(),
259 &remote_file_change_id_)) {
261 SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
265 blocker->tracker_ids.push_back(remote_file_tracker_->tracker_id());
266 blocker->file_ids.push_back(remote_file_tracker_->file_id());
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(),
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.
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);
306 if (!metadata_database()->FindTrackerByTrackerID(
307 remote_file_tracker_->tracker_id(), NULL)) {
308 SyncCompleted(token.Pass(), SYNC_STATUS_RETRY);
313 continuation.Run(token.Pass());
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;
321 if (needs_remote_change_listing_)
322 status = SYNC_STATUS_FILE_BUSY;
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()));
331 SyncTaskManager::NotifyTaskDone(token.Pass(), status);
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());
340 if (local_is_missing_) {
341 SyncCompleted(token.Pass(), SYNC_STATUS_OK);
345 if (local_change_.IsFile()) {
346 // Upload the conflicting file as a new file and let ConflictResolver
348 MoveToBackground(base::Bind(&LocalToRemoteSyncer::UploadNewFile,
349 weak_ptr_factory_.GetWeakPtr()),
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)) {
360 MoveToBackground(base::Bind(&LocalToRemoteSyncer::CreateRemoteFolder,
361 weak_ptr_factory_.GetWeakPtr()),
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())) {
374 base::Bind(&LocalToRemoteSyncer::UpdateTrackerForReusedFolder,
375 weak_ptr_factory_.GetWeakPtr(),
381 MoveToBackground(base::Bind(&LocalToRemoteSyncer::CreateRemoteFolder,
382 weak_ptr_factory_.GetWeakPtr()),
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);
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());
401 if (local_is_missing_) {
402 // Local file deletion for existing remote file.
403 MoveToBackground(base::Bind(&LocalToRemoteSyncer::DeleteRemoteFile,
404 weak_ptr_factory_.GetWeakPtr()),
409 DCHECK(local_change_.IsAddOrUpdate());
410 DCHECK(local_change_.IsFile() || local_change_.IsDirectory());
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()),
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()),
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()),
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);
452 void LocalToRemoteSyncer::DeleteRemoteFile(scoped_ptr<SyncTaskToken> token) {
453 DCHECK(remote_file_tracker_);
454 DCHECK(remote_file_tracker_->has_synced_details());
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)));
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);
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);
488 SyncCompleted(token.Pass(), SYNC_STATUS_OK);
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());
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);
503 sync_action_ = SYNC_ACTION_UPDATED;
505 drive::DriveUploader::UploadExistingFileOptions options;
506 options.etag = remote_file_tracker_->synced_details().etag();
507 drive_uploader()->UploadExistingFile(
508 remote_file_tracker_->file_id(),
510 "application/octet_stream",
512 base::Bind(&LocalToRemoteSyncer::DidUploadExistingFile,
513 weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)),
514 google_apis::ProgressCallback());
517 void LocalToRemoteSyncer::DidUploadExistingFile(
518 scoped_ptr<SyncTaskToken> token,
519 google_apis::GDataErrorCode error,
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.
530 needs_remote_change_listing_ = true;
531 UpdateRemoteMetadata(
532 remote_file_tracker_->file_id(),
537 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
538 if (status != SYNC_STATUS_OK) {
539 SyncCompleted(token.Pass(), status);
545 SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
550 status = metadata_database()->UpdateByFileResource(*entry);
551 if (status != SYNC_STATUS_OK) {
552 SyncCompleted(token.Pass(), status);
557 if (!metadata_database()->FindFileByFileID(
558 remote_file_tracker_->file_id(), &file)) {
560 SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
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);
577 SyncCompleted(token.Pass(), SYNC_STATUS_RETRY);
580 void LocalToRemoteSyncer::UpdateRemoteMetadata(
581 const std::string& file_id,
582 scoped_ptr<SyncTaskToken> token) {
583 DCHECK(remote_file_tracker_);
585 drive_service()->GetFileResource(
587 base::Bind(&LocalToRemoteSyncer::DidGetRemoteMetadata,
588 weak_ptr_factory_.GetWeakPtr(),
589 file_id, base::Passed(&token)));
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());
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);
607 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
608 if (status != SYNC_STATUS_OK) {
609 SyncCompleted(token.Pass(), status);
615 SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
619 retry_on_success_ = true;
620 status = metadata_database()->UpdateByFileResource(*entry);
621 SyncCompleted(token.Pass(), status);
624 void LocalToRemoteSyncer::UploadNewFile(scoped_ptr<SyncTaskToken> token) {
625 DCHECK(remote_parent_folder_tracker_);
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(),
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());
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;
649 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
650 if (status != SYNC_STATUS_OK) {
651 SyncCompleted(token.Pass(), status);
657 SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
661 status = metadata_database()->ReplaceActiveTrackerWithNewResource(
662 remote_parent_folder_tracker_->tracker_id(), *entry);
663 SyncCompleted(token.Pass(), status);
666 void LocalToRemoteSyncer::CreateRemoteFolder(scoped_ptr<SyncTaskToken> token) {
667 DCHECK(remote_parent_folder_tracker_);
669 base::FilePath title = storage::VirtualPath::BaseName(target_path_);
670 sync_action_ = SYNC_ACTION_ADDED;
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)));
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;
690 scoped_ptr<FolderCreator> deleter = folder_creator_.Pass();
691 if (status != SYNC_STATUS_OK) {
692 SyncCompleted(token.Pass(), status);
696 status = SYNC_STATUS_FAILED;
697 MetadataDatabase::ActivationStatus activation_status =
698 metadata_database()->TryActivateTracker(
699 remote_parent_folder_tracker_->tracker_id(),
701 switch (activation_status) {
702 case MetadataDatabase::ACTIVATION_PENDING:
703 SyncCompleted(token.Pass(), status);
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
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)));
717 SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
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);
730 SyncCompleted(token.Pass(), SYNC_STATUS_RETRY);
733 bool LocalToRemoteSyncer::IsContextReady() {
734 return sync_context_->GetDriveService() &&
735 sync_context_->GetDriveUploader() &&
736 sync_context_->GetMetadataDatabase();
739 drive::DriveServiceInterface* LocalToRemoteSyncer::drive_service() {
740 set_used_network(true);
741 return sync_context_->GetDriveService();
744 drive::DriveUploaderInterface* LocalToRemoteSyncer::drive_uploader() {
745 set_used_network(true);
746 return sync_context_->GetDriveUploader();
749 MetadataDatabase* LocalToRemoteSyncer::metadata_database() {
750 return sync_context_->GetMetadataDatabase();
753 } // namespace drive_backend
754 } // namespace sync_file_system