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