Check USB device path access when prompting users to select a device.
[chromium-blink-merge.git] / chrome / browser / sync_file_system / drive_backend / local_to_remote_syncer.cc
blob990067521c9cc13d4dc369e57fd30025233d3cf4
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_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 {
37 namespace {
39 scoped_ptr<FileTracker> FindTrackerByID(MetadataDatabase* metadata_database,
40 int64 tracker_id) {
41 scoped_ptr<FileTracker> tracker(new FileTracker);
42 if (metadata_database->FindTrackerByTrackerID(tracker_id, tracker.get()))
43 return tracker.Pass();
44 return scoped_ptr<FileTracker>();
47 bool GetKnownChangeID(MetadataDatabase* metadata_database,
48 const std::string& file_id,
49 int64* change_id) {
50 FileMetadata remote_file_metadata;
51 if (!metadata_database->FindFileByFileID(file_id, &remote_file_metadata))
52 return false;
53 *change_id = remote_file_metadata.details().change_id();
54 return true;
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;
69 return mime_type;
72 } // namespace
74 LocalToRemoteSyncer::LocalToRemoteSyncer(SyncEngineContext* sync_context,
75 const SyncFileMetadata& local_metadata,
76 const FileChange& local_change,
77 const base::FilePath& local_path,
78 const storage::FileSystemURL& url)
79 : sync_context_(sync_context),
80 local_change_(local_change),
81 local_is_missing_(IsLocalFileMissing(local_metadata, local_change)),
82 local_path_(local_path),
83 url_(url),
84 file_type_(SYNC_FILE_TYPE_UNKNOWN),
85 sync_action_(SYNC_ACTION_NONE),
86 remote_file_change_id_(0),
87 retry_on_success_(false),
88 needs_remote_change_listing_(false),
89 weak_ptr_factory_(this) {
90 DCHECK(local_is_missing_ ||
91 local_change.file_type() == local_metadata.file_type)
92 << local_change.DebugString() << " metadata:" << local_metadata.file_type;
95 LocalToRemoteSyncer::~LocalToRemoteSyncer() {
98 void LocalToRemoteSyncer::RunPreflight(scoped_ptr<SyncTaskToken> token) {
99 token->InitializeTaskLog("Local -> Remote");
101 if (!IsContextReady()) {
102 token->RecordLog("Context not ready.");
103 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED);
104 return;
107 token->RecordLog(base::StringPrintf(
108 "Start: %s on %s@%s %s",
109 local_change_.DebugString().c_str(),
110 url_.path().AsUTF8Unsafe().c_str(),
111 url_.origin().host().c_str(),
112 local_is_missing_ ? "(missing)" : ""));
114 if (local_is_missing_ && !local_change_.IsDelete()) {
115 // Stray file, we can just return.
116 token->RecordLog("Missing file for non-delete change.");
117 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_OK);
118 return;
121 std::string app_id = url_.origin().host();
122 base::FilePath path = url_.path();
124 scoped_ptr<FileTracker> active_ancestor_tracker(new FileTracker);
125 base::FilePath active_ancestor_path;
126 if (!metadata_database()->FindNearestActiveAncestor(
127 app_id, path,
128 active_ancestor_tracker.get(), &active_ancestor_path)) {
129 // The app is disabled or not registered.
130 token->RecordLog("App is disabled or not registered");
131 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_UNKNOWN_ORIGIN);
132 return;
134 DCHECK(active_ancestor_tracker->active());
135 DCHECK(active_ancestor_tracker->has_synced_details());
136 const FileDetails& active_ancestor_details =
137 active_ancestor_tracker->synced_details();
139 // TODO(tzik): Consider handling
140 // active_ancestor_tracker->synced_details().missing() case.
142 DCHECK(active_ancestor_details.file_kind() == FILE_KIND_FILE ||
143 active_ancestor_details.file_kind() == FILE_KIND_FOLDER);
145 base::FilePath missing_entries;
146 if (active_ancestor_path.empty()) {
147 missing_entries = path;
148 } else if (active_ancestor_path != path) {
149 if (!active_ancestor_path.AppendRelativePath(path, &missing_entries)) {
150 NOTREACHED();
151 token->RecordLog(base::StringPrintf(
152 "Detected invalid ancestor: %s",
153 active_ancestor_path.value().c_str()));
154 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED);
155 return;
159 std::vector<base::FilePath::StringType> missing_components;
160 storage::VirtualPath::GetComponents(missing_entries, &missing_components);
162 if (!missing_components.empty()) {
163 if (local_is_missing_) {
164 token->RecordLog("Both local and remote are marked missing");
165 // !IsDelete() but SYNC_FILE_TYPE_UNKNOWN could happen when a file is
166 // deleted by recursive deletion (which is not recorded by tracker)
167 // but there're remaining changes for the same file in the tracker.
169 // Local file is deleted and remote file is missing, already deleted or
170 // not yet synced. There is nothing to do for the file.
171 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_OK);
172 return;
176 if (missing_components.size() > 1) {
177 // The original target doesn't have remote file and parent.
178 // Try creating the parent first.
179 if (active_ancestor_details.file_kind() == FILE_KIND_FOLDER) {
180 remote_parent_folder_tracker_ = active_ancestor_tracker.Pass();
181 target_path_ = active_ancestor_path.Append(missing_components[0]);
182 token->RecordLog("Detected missing parent folder.");
184 retry_on_success_ = true;
185 MoveToBackground(base::Bind(&LocalToRemoteSyncer::CreateRemoteFolder,
186 weak_ptr_factory_.GetWeakPtr()),
187 token.Pass());
188 return;
191 DCHECK(active_ancestor_details.file_kind() == FILE_KIND_FILE);
192 remote_parent_folder_tracker_ =
193 FindTrackerByID(metadata_database(),
194 active_ancestor_tracker->parent_tracker_id());
195 remote_file_tracker_ = active_ancestor_tracker.Pass();
196 target_path_ = active_ancestor_path;
197 token->RecordLog("Detected non-folder file in its path.");
199 retry_on_success_ = true;
200 MoveToBackground(base::Bind(&LocalToRemoteSyncer::DeleteRemoteFile,
201 weak_ptr_factory_.GetWeakPtr()),
202 token.Pass());
203 return;
206 if (missing_components.empty()) {
207 // The original target has remote active file/folder.
208 remote_parent_folder_tracker_ =
209 FindTrackerByID(metadata_database(),
210 active_ancestor_tracker->parent_tracker_id());
211 remote_file_tracker_ = active_ancestor_tracker.Pass();
212 target_path_ = url_.path();
213 DCHECK(target_path_ == active_ancestor_path);
215 if (remote_file_tracker_->dirty()) {
216 token->RecordLog(base::StringPrintf(
217 "Detected conflicting dirty tracker:%" PRId64,
218 remote_file_tracker_->tracker_id()));
219 // Both local and remote file has pending modification.
220 HandleConflict(token.Pass());
221 return;
224 // Non-conflicting file/folder update case.
225 HandleExistingRemoteFile(token.Pass());
226 return;
229 DCHECK(local_change_.IsAddOrUpdate());
230 DCHECK_EQ(1u, missing_components.size());
231 // The original target has remote parent folder and doesn't have remote active
232 // file.
233 // Upload the file as a new file or create a folder.
234 remote_parent_folder_tracker_ = active_ancestor_tracker.Pass();
235 target_path_ = url_.path();
236 DCHECK(target_path_ == active_ancestor_path.Append(missing_components[0]));
237 if (local_change_.file_type() == SYNC_FILE_TYPE_FILE) {
238 token->RecordLog("Detected a new file.");
239 MoveToBackground(base::Bind(&LocalToRemoteSyncer::UploadNewFile,
240 weak_ptr_factory_.GetWeakPtr()),
241 token.Pass());
242 return;
245 token->RecordLog("Detected a new folder.");
246 MoveToBackground(base::Bind(&LocalToRemoteSyncer::CreateRemoteFolder,
247 weak_ptr_factory_.GetWeakPtr()),
248 token.Pass());
251 void LocalToRemoteSyncer::MoveToBackground(const Continuation& continuation,
252 scoped_ptr<SyncTaskToken> token) {
253 scoped_ptr<TaskBlocker> blocker(new TaskBlocker);
254 blocker->app_id = url_.origin().host();
255 blocker->paths.push_back(target_path_);
257 if (remote_file_tracker_) {
258 if (!GetKnownChangeID(metadata_database(),
259 remote_file_tracker_->file_id(),
260 &remote_file_change_id_)) {
261 NOTREACHED();
262 SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
263 return;
266 blocker->tracker_ids.push_back(remote_file_tracker_->tracker_id());
267 blocker->file_ids.push_back(remote_file_tracker_->file_id());
270 // Run current task as a background task with |blocker|.
271 // After the invocation of ContinueAsBackgroundTask
272 SyncTaskManager::UpdateTaskBlocker(
273 token.Pass(), blocker.Pass(),
274 base::Bind(&LocalToRemoteSyncer::ContinueAsBackgroundTask,
275 weak_ptr_factory_.GetWeakPtr(),
276 continuation));
279 void LocalToRemoteSyncer::ContinueAsBackgroundTask(
280 const Continuation& continuation,
281 scoped_ptr<SyncTaskToken> token) {
282 // The SyncTask runs as a background task beyond this point.
283 // Note that any task can run between MoveToBackground() and
284 // ContinueAsBackgroundTask(), so we need to make sure other tasks didn't
285 // affect to the current LocalToRemoteSyncer task.
287 // - For RemoteToLocalSyncer, it doesn't actually run beyond
288 // PrepareForProcessRemoteChange() since it should be blocked in
289 // LocalFileSyncService.
290 // - For ListChangesTask, it may update FileMetatada together with |change_id|
291 // and may delete FileTracker. So, ensure |change_id| is not changed and
292 // check if FileTracker still exists.
293 // - For UninstallAppTask, it may also delete FileMetadata and FileTracker.
294 // Check if FileTracker still exists.
295 // - Others, SyncEngineInitializer and RegisterAppTask doesn't affect to
296 // LocalToRemoteSyncer.
297 if (remote_file_tracker_) {
298 int64 latest_change_id = 0;
299 if (!GetKnownChangeID(metadata_database(),
300 remote_file_tracker_->file_id(),
301 &latest_change_id) ||
302 latest_change_id > remote_file_change_id_) {
303 SyncCompleted(token.Pass(), SYNC_STATUS_RETRY);
304 return;
307 if (!metadata_database()->FindTrackerByTrackerID(
308 remote_file_tracker_->tracker_id(), nullptr)) {
309 SyncCompleted(token.Pass(), SYNC_STATUS_RETRY);
310 return;
314 continuation.Run(token.Pass());
317 void LocalToRemoteSyncer::SyncCompleted(scoped_ptr<SyncTaskToken> token,
318 SyncStatusCode status) {
319 if (status == SYNC_STATUS_OK && retry_on_success_)
320 status = SYNC_STATUS_RETRY;
322 if (needs_remote_change_listing_)
323 status = SYNC_STATUS_FILE_BUSY;
325 token->RecordLog(base::StringPrintf(
326 "Finished: action=%s, status=%s for %s@%s",
327 SyncActionToString(sync_action_),
328 SyncStatusCodeToString(status),
329 target_path_.AsUTF8Unsafe().c_str(),
330 url_.origin().host().c_str()));
332 SyncTaskManager::NotifyTaskDone(token.Pass(), status);
335 void LocalToRemoteSyncer::HandleConflict(scoped_ptr<SyncTaskToken> token) {
336 DCHECK(remote_file_tracker_);
337 DCHECK(remote_file_tracker_->has_synced_details());
338 DCHECK(remote_file_tracker_->active());
339 DCHECK(remote_file_tracker_->dirty());
341 if (local_is_missing_) {
342 SyncCompleted(token.Pass(), SYNC_STATUS_OK);
343 return;
346 if (local_change_.IsFile()) {
347 // Upload the conflicting file as a new file and let ConflictResolver
348 // resolve it.
349 MoveToBackground(base::Bind(&LocalToRemoteSyncer::UploadNewFile,
350 weak_ptr_factory_.GetWeakPtr()),
351 token.Pass());
352 return;
355 DCHECK(local_change_.IsDirectory());
356 // Check if we can reuse the remote folder.
357 FileMetadata remote_file_metadata;
358 if (!metadata_database()->FindFileByFileID(
359 remote_file_tracker_->file_id(), &remote_file_metadata)) {
360 NOTREACHED();
361 MoveToBackground(base::Bind(&LocalToRemoteSyncer::CreateRemoteFolder,
362 weak_ptr_factory_.GetWeakPtr()),
363 token.Pass());
364 return;
367 const FileDetails& remote_details = remote_file_metadata.details();
368 base::FilePath title = storage::VirtualPath::BaseName(target_path_);
369 if (!remote_details.missing() &&
370 remote_details.file_kind() == FILE_KIND_FOLDER &&
371 remote_details.title() == title.AsUTF8Unsafe() &&
372 HasFileAsParent(remote_details,
373 remote_parent_folder_tracker_->file_id())) {
374 MoveToBackground(
375 base::Bind(&LocalToRemoteSyncer::UpdateTrackerForReusedFolder,
376 weak_ptr_factory_.GetWeakPtr(),
377 remote_details),
378 token.Pass());
379 return;
382 MoveToBackground(base::Bind(&LocalToRemoteSyncer::CreateRemoteFolder,
383 weak_ptr_factory_.GetWeakPtr()),
384 token.Pass());
387 void LocalToRemoteSyncer::UpdateTrackerForReusedFolder(
388 const FileDetails& details,
389 scoped_ptr<SyncTaskToken> token) {
390 SyncStatusCode status = metadata_database()->UpdateTracker(
391 remote_file_tracker_->tracker_id(), details);
392 SyncCompleted(token.Pass(), status);
395 void LocalToRemoteSyncer::HandleExistingRemoteFile(
396 scoped_ptr<SyncTaskToken> token) {
397 DCHECK(remote_file_tracker_);
398 DCHECK(!remote_file_tracker_->dirty());
399 DCHECK(remote_file_tracker_->active());
400 DCHECK(remote_file_tracker_->has_synced_details());
402 if (local_is_missing_) {
403 // Local file deletion for existing remote file.
404 MoveToBackground(base::Bind(&LocalToRemoteSyncer::DeleteRemoteFile,
405 weak_ptr_factory_.GetWeakPtr()),
406 token.Pass());
407 return;
410 DCHECK(local_change_.IsAddOrUpdate());
411 DCHECK(local_change_.IsFile() || local_change_.IsDirectory());
413 const FileDetails& synced_details = remote_file_tracker_->synced_details();
414 DCHECK(synced_details.file_kind() == FILE_KIND_FILE ||
415 synced_details.file_kind() == FILE_KIND_FOLDER);
416 if (local_change_.IsFile()) {
417 if (synced_details.file_kind() == FILE_KIND_FILE) {
418 // Non-conflicting local file update to existing remote regular file.
419 MoveToBackground(base::Bind(&LocalToRemoteSyncer::UploadExistingFile,
420 weak_ptr_factory_.GetWeakPtr()),
421 token.Pass());
422 return;
425 DCHECK_EQ(FILE_KIND_FOLDER, synced_details.file_kind());
426 // Non-conflicting local file update to existing remote *folder*.
427 // Assuming this case as local folder deletion + local file creation, delete
428 // the remote folder and upload the file.
429 retry_on_success_ = true;
430 MoveToBackground(base::Bind(&LocalToRemoteSyncer::DeleteRemoteFile,
431 weak_ptr_factory_.GetWeakPtr()),
432 token.Pass());
433 return;
436 DCHECK(local_change_.IsDirectory());
437 if (synced_details.file_kind() == FILE_KIND_FILE) {
438 // Non-conflicting local folder creation to existing remote *file*.
439 // Assuming this case as local file deletion + local folder creation, delete
440 // the remote file and create a remote folder.
441 retry_on_success_ = true;
442 MoveToBackground(base::Bind(&LocalToRemoteSyncer::DeleteRemoteFile,
443 weak_ptr_factory_.GetWeakPtr()),
444 token.Pass());
445 return;
448 // Non-conflicting local folder creation to existing remote folder.
449 DCHECK_EQ(FILE_KIND_FOLDER, synced_details.file_kind());
450 SyncCompleted(token.Pass(), SYNC_STATUS_OK);
453 void LocalToRemoteSyncer::DeleteRemoteFile(scoped_ptr<SyncTaskToken> token) {
454 DCHECK(remote_file_tracker_);
455 DCHECK(remote_file_tracker_->has_synced_details());
457 switch (remote_file_tracker_->synced_details().file_kind()) {
458 case FILE_KIND_UNSUPPORTED:
459 NOTREACHED();
460 file_type_ = SYNC_FILE_TYPE_UNKNOWN;
461 break;
462 case FILE_KIND_FILE:
463 file_type_ = SYNC_FILE_TYPE_FILE;
464 break;
465 case FILE_KIND_FOLDER:
466 file_type_ = SYNC_FILE_TYPE_DIRECTORY;
467 break;
469 sync_action_ = SYNC_ACTION_DELETED;
470 drive_service()->DeleteResource(
471 remote_file_tracker_->file_id(),
472 remote_file_tracker_->synced_details().etag(),
473 base::Bind(&LocalToRemoteSyncer::DidDeleteRemoteFile,
474 weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)));
477 void LocalToRemoteSyncer::DidDeleteRemoteFile(
478 scoped_ptr<SyncTaskToken> token,
479 google_apis::DriveApiErrorCode error) {
480 SyncStatusCode status = DriveApiErrorCodeToSyncStatusCode(error);
481 if (status != SYNC_STATUS_OK &&
482 error != google_apis::HTTP_NOT_FOUND &&
483 error != google_apis::HTTP_PRECONDITION &&
484 error != google_apis::HTTP_CONFLICT) {
485 SyncCompleted(token.Pass(), status);
486 return;
489 // Handle NOT_FOUND case as SUCCESS case.
490 // For PRECONDITION / CONFLICT case, the remote file is modified since the
491 // last sync completed. As our policy for deletion-modification conflict
492 // resolution, ignore the local deletion.
493 if (status == SYNC_STATUS_OK ||
494 error == google_apis::HTTP_NOT_FOUND) {
495 SyncStatusCode status = metadata_database()->UpdateByDeletedRemoteFile(
496 remote_file_tracker_->file_id());
497 SyncCompleted(token.Pass(), status);
498 return;
501 SyncCompleted(token.Pass(), SYNC_STATUS_OK);
504 void LocalToRemoteSyncer::UploadExistingFile(scoped_ptr<SyncTaskToken> token) {
505 DCHECK(remote_file_tracker_);
506 DCHECK(remote_file_tracker_->has_synced_details());
507 DCHECK(sync_context_->GetWorkerTaskRunner()->RunsTasksOnCurrentThread());
509 const std::string local_file_md5 = drive::util::GetMd5Digest(local_path_);
510 if (local_file_md5 == remote_file_tracker_->synced_details().md5()) {
511 // Local file is not changed.
512 SyncCompleted(token.Pass(), SYNC_STATUS_OK);
513 return;
516 file_type_ = SYNC_FILE_TYPE_FILE;
517 sync_action_ = SYNC_ACTION_UPDATED;
519 drive::UploadExistingFileOptions options;
520 options.etag = remote_file_tracker_->synced_details().etag();
521 drive_uploader()->UploadExistingFile(
522 remote_file_tracker_->file_id(),
523 local_path_,
524 "application/octet_stream",
525 options,
526 base::Bind(&LocalToRemoteSyncer::DidUploadExistingFile,
527 weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)),
528 google_apis::ProgressCallback());
531 void LocalToRemoteSyncer::DidUploadExistingFile(
532 scoped_ptr<SyncTaskToken> token,
533 google_apis::DriveApiErrorCode error,
534 const GURL&,
535 scoped_ptr<google_apis::FileResource> entry) {
536 if (error == google_apis::HTTP_PRECONDITION ||
537 error == google_apis::HTTP_CONFLICT ||
538 error == google_apis::HTTP_NOT_FOUND) {
539 // The remote file has unfetched remote change. Fetch latest metadata and
540 // update database with it.
541 // TODO(tzik): Consider adding local side low-priority dirtiness handling to
542 // handle this as ListChangesTask.
544 needs_remote_change_listing_ = true;
545 UpdateRemoteMetadata(
546 remote_file_tracker_->file_id(),
547 token.Pass());
548 return;
551 SyncStatusCode status = DriveApiErrorCodeToSyncStatusCode(error);
552 if (status != SYNC_STATUS_OK) {
553 SyncCompleted(token.Pass(), status);
554 return;
557 if (!entry) {
558 NOTREACHED();
559 SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
560 return;
563 DCHECK(entry);
564 status = metadata_database()->UpdateByFileResource(*entry);
565 if (status != SYNC_STATUS_OK) {
566 SyncCompleted(token.Pass(), status);
567 return;
570 FileMetadata file;
571 if (!metadata_database()->FindFileByFileID(
572 remote_file_tracker_->file_id(), &file)) {
573 NOTREACHED();
574 SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
575 return;
578 const FileDetails& details = file.details();
579 base::FilePath title = storage::VirtualPath::BaseName(target_path_);
580 if (!details.missing() &&
581 details.file_kind() == FILE_KIND_FILE &&
582 details.title() == title.AsUTF8Unsafe() &&
583 HasFileAsParent(details,
584 remote_parent_folder_tracker_->file_id())) {
585 SyncStatusCode status = metadata_database()->UpdateTracker(
586 remote_file_tracker_->tracker_id(), file.details());
587 SyncCompleted(token.Pass(), status);
588 return;
591 SyncCompleted(token.Pass(), SYNC_STATUS_RETRY);
594 void LocalToRemoteSyncer::UpdateRemoteMetadata(
595 const std::string& file_id,
596 scoped_ptr<SyncTaskToken> token) {
597 DCHECK(remote_file_tracker_);
599 drive_service()->GetFileResource(
600 file_id,
601 base::Bind(&LocalToRemoteSyncer::DidGetRemoteMetadata,
602 weak_ptr_factory_.GetWeakPtr(),
603 file_id, base::Passed(&token)));
606 void LocalToRemoteSyncer::DidGetRemoteMetadata(
607 const std::string& file_id,
608 scoped_ptr<SyncTaskToken> token,
609 google_apis::DriveApiErrorCode error,
610 scoped_ptr<google_apis::FileResource> entry) {
611 DCHECK(sync_context_->GetWorkerTaskRunner()->RunsTasksOnCurrentThread());
613 if (error == google_apis::HTTP_NOT_FOUND) {
614 retry_on_success_ = true;
615 SyncStatusCode status =
616 metadata_database()->UpdateByDeletedRemoteFile(file_id);
617 SyncCompleted(token.Pass(), status);
618 return;
621 SyncStatusCode status = DriveApiErrorCodeToSyncStatusCode(error);
622 if (status != SYNC_STATUS_OK) {
623 SyncCompleted(token.Pass(), status);
624 return;
627 if (!entry) {
628 NOTREACHED();
629 SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
630 return;
633 retry_on_success_ = true;
634 status = metadata_database()->UpdateByFileResource(*entry);
635 SyncCompleted(token.Pass(), status);
638 void LocalToRemoteSyncer::UploadNewFile(scoped_ptr<SyncTaskToken> token) {
639 DCHECK(remote_parent_folder_tracker_);
641 file_type_ = SYNC_FILE_TYPE_FILE;
642 sync_action_ = SYNC_ACTION_ADDED;
643 base::FilePath title = storage::VirtualPath::BaseName(target_path_);
644 drive_uploader()->UploadNewFile(
645 remote_parent_folder_tracker_->file_id(), local_path_,
646 title.AsUTF8Unsafe(), GetMimeTypeFromTitle(title),
647 drive::UploadNewFileOptions(),
648 base::Bind(&LocalToRemoteSyncer::DidUploadNewFile,
649 weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)),
650 google_apis::ProgressCallback());
653 void LocalToRemoteSyncer::DidUploadNewFile(
654 scoped_ptr<SyncTaskToken> token,
655 google_apis::DriveApiErrorCode error,
656 const GURL& upload_location,
657 scoped_ptr<google_apis::FileResource> entry) {
658 if (error == google_apis::HTTP_NOT_FOUND)
659 needs_remote_change_listing_ = true;
661 SyncStatusCode status = DriveApiErrorCodeToSyncStatusCode(error);
662 if (status != SYNC_STATUS_OK) {
663 SyncCompleted(token.Pass(), status);
664 return;
667 if (!entry) {
668 NOTREACHED();
669 SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
670 return;
673 status = metadata_database()->ReplaceActiveTrackerWithNewResource(
674 remote_parent_folder_tracker_->tracker_id(), *entry);
675 SyncCompleted(token.Pass(), status);
678 void LocalToRemoteSyncer::CreateRemoteFolder(scoped_ptr<SyncTaskToken> token) {
679 DCHECK(remote_parent_folder_tracker_);
681 base::FilePath title = storage::VirtualPath::BaseName(target_path_);
682 file_type_ = SYNC_FILE_TYPE_DIRECTORY;
683 sync_action_ = SYNC_ACTION_ADDED;
685 DCHECK(!folder_creator_);
686 folder_creator_.reset(new FolderCreator(
687 drive_service(), metadata_database(),
688 remote_parent_folder_tracker_->file_id(),
689 title.AsUTF8Unsafe()));
690 folder_creator_->Run(base::Bind(
691 &LocalToRemoteSyncer::DidCreateRemoteFolder,
692 weak_ptr_factory_.GetWeakPtr(),
693 base::Passed(&token)));
696 void LocalToRemoteSyncer::DidCreateRemoteFolder(
697 scoped_ptr<SyncTaskToken> token,
698 const std::string& file_id,
699 SyncStatusCode status) {
700 if (status == SYNC_FILE_ERROR_NOT_FOUND)
701 needs_remote_change_listing_ = true;
703 scoped_ptr<FolderCreator> deleter = folder_creator_.Pass();
704 if (status != SYNC_STATUS_OK) {
705 SyncCompleted(token.Pass(), status);
706 return;
709 status = SYNC_STATUS_FAILED;
710 MetadataDatabase::ActivationStatus activation_status =
711 metadata_database()->TryActivateTracker(
712 remote_parent_folder_tracker_->tracker_id(),
713 file_id, &status);
714 switch (activation_status) {
715 case MetadataDatabase::ACTIVATION_PENDING:
716 SyncCompleted(token.Pass(), status);
717 return;
718 case MetadataDatabase::ACTIVATION_FAILED_ANOTHER_ACTIVE_TRACKER:
719 // The activation failed due to another tracker that has another parent.
720 // Detach the folder from the current parent to avoid using this folder as
721 // active folder.
722 drive_service()->RemoveResourceFromDirectory(
723 remote_parent_folder_tracker_->file_id(), file_id,
724 base::Bind(&LocalToRemoteSyncer::DidDetachResourceForCreationConflict,
725 weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)));
726 return;
729 NOTREACHED();
730 SyncCompleted(token.Pass(), SYNC_STATUS_FAILED);
731 return;
734 void LocalToRemoteSyncer::DidDetachResourceForCreationConflict(
735 scoped_ptr<SyncTaskToken> token,
736 google_apis::DriveApiErrorCode error) {
737 SyncStatusCode status = DriveApiErrorCodeToSyncStatusCode(error);
738 if (status != SYNC_STATUS_OK) {
739 SyncCompleted(token.Pass(), status);
740 return;
743 SyncCompleted(token.Pass(), SYNC_STATUS_RETRY);
746 bool LocalToRemoteSyncer::IsContextReady() {
747 return sync_context_->GetDriveService() &&
748 sync_context_->GetDriveUploader() &&
749 sync_context_->GetMetadataDatabase();
752 drive::DriveServiceInterface* LocalToRemoteSyncer::drive_service() {
753 set_used_network(true);
754 return sync_context_->GetDriveService();
757 drive::DriveUploaderInterface* LocalToRemoteSyncer::drive_uploader() {
758 set_used_network(true);
759 return sync_context_->GetDriveUploader();
762 MetadataDatabase* LocalToRemoteSyncer::metadata_database() {
763 return sync_context_->GetMetadataDatabase();
766 } // namespace drive_backend
767 } // namespace sync_file_system