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/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"
28 namespace sync_file_system
{
29 namespace drive_backend
{
33 scoped_ptr
<FileTracker
> FindTrackerByID(MetadataDatabase
* metadata_database
,
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
>();
41 void ReturnRetryOnSuccess(const SyncStatusCallback
& callback
,
42 SyncStatusCode status
) {
43 if (status
== SYNC_STATUS_OK
)
44 status
= SYNC_STATUS_RETRY
;
48 bool IsLocalFileMissing(const SyncFileMetadata
& local_metadata
,
49 const FileChange
& local_change
) {
50 return local_metadata
.file_type
== SYNC_FILE_TYPE_UNKNOWN
||
51 local_change
.IsDelete();
56 LocalToRemoteSyncer::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
),
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
;
74 LocalToRemoteSyncer::~LocalToRemoteSyncer() {
77 void LocalToRemoteSyncer::Run(const SyncStatusCallback
& callback
) {
78 if (!IsContextReady()) {
79 util::Log(logging::LOG_VERBOSE
, FROM_HERE
,
80 "[Local -> Remote] Context not ready.");
82 callback
.Run(SYNC_STATUS_FAILED
);
86 SyncStatusCallback wrapped_callback
= base::Bind(
87 &LocalToRemoteSyncer::SyncCompleted
, weak_ptr_factory_
.GetWeakPtr(),
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)" : "");
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
);
105 std::string app_id
= url_
.origin().host();
106 base::FilePath path
= url_
.path();
108 scoped_ptr
<FileTracker
> active_ancestor_tracker(new FileTracker
);
109 base::FilePath active_ancestor_path
;
110 if (!metadata_database()->FindNearestActiveAncestor(
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
);
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();
124 // TODO(tzik): Consider handling
125 // active_ancestor_tracker->synced_details().missing() case.
127 DCHECK(active_ancestor_details
.file_kind() == FILE_KIND_FILE
||
128 active_ancestor_details
.file_kind() == FILE_KIND_FOLDER
);
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
);
144 std::vector
<base::FilePath::StringType
> missing_components
;
145 fileapi::VirtualPath::GetComponents(missing_entries
, &missing_components
);
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.
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
);
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
);
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(),
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
);
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
);
207 // Non-conflicting file/folder update case.
208 HandleExistingRemoteFile(wrapped_callback
);
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
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
);
226 util::Log(logging::LOG_VERBOSE
, FROM_HERE
,
227 "[Local -> Remote]: Detected a new folder.");
228 CreateRemoteFolder(wrapped_callback
);
231 void LocalToRemoteSyncer::SyncCompleted(const SyncStatusCallback
& callback
,
232 SyncStatusCode status
) {
233 if (status
== SYNC_STATUS_OK
&& target_path_
!= url_
.path())
234 status
= SYNC_STATUS_RETRY
;
236 if (needs_remote_change_listing_
)
237 status
= SYNC_STATUS_FILE_BUSY
;
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());
246 callback
.Run(status
);
249 void 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());
255 if (local_is_missing_
) {
256 callback
.Run(SYNC_STATUS_OK
);
260 if (local_change_
.IsFile()) {
261 UploadNewFile(callback
);
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
) {
272 CreateRemoteFolder(callback
);
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
);
288 // Create new remote folder.
289 CreateRemoteFolder(callback
);
292 void 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());
299 if (local_is_missing_
) {
300 // Local file deletion for existing remote file.
301 DeleteRemoteFile(callback
);
305 DCHECK(local_change_
.IsAddOrUpdate());
306 DCHECK(local_change_
.IsFile() || local_change_
.IsDirectory());
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
);
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(),
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
));
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
);
343 void LocalToRemoteSyncer::DeleteRemoteFile(
344 const SyncStatusCallback
& callback
) {
345 DCHECK(remote_file_tracker_
);
346 DCHECK(remote_file_tracker_
->has_synced_details());
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(),
357 void 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
);
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 callback
.Run(SYNC_STATUS_OK
);
376 void LocalToRemoteSyncer::UploadExistingFile(
377 const SyncStatusCallback
& callback
) {
378 DCHECK(remote_file_tracker_
);
379 DCHECK(remote_file_tracker_
->has_synced_details());
381 base::PostTaskAndReplyWithResult(
382 sync_context_
->GetBlockingTaskRunner(), FROM_HERE
,
383 base::Bind(&drive::util::GetMd5Digest
, local_path_
),
384 base::Bind(&LocalToRemoteSyncer::DidGetMD5ForUpload
,
385 weak_ptr_factory_
.GetWeakPtr(),
389 void LocalToRemoteSyncer::DidGetMD5ForUpload(
390 const SyncStatusCallback
& callback
,
391 const std::string
& local_file_md5
) {
392 if (local_file_md5
== remote_file_tracker_
->synced_details().md5()) {
393 // Local file is not changed.
394 callback
.Run(SYNC_STATUS_OK
);
398 sync_action_
= SYNC_ACTION_UPDATED
;
399 drive_uploader()->UploadExistingFile(
400 remote_file_tracker_
->file_id(),
402 "application/octet_stream",
403 remote_file_tracker_
->synced_details().etag(),
404 base::Bind(&LocalToRemoteSyncer::DidUploadExistingFile
,
405 weak_ptr_factory_
.GetWeakPtr(),
407 google_apis::ProgressCallback());
410 void LocalToRemoteSyncer::DidUploadExistingFile(
411 const SyncStatusCallback
& callback
,
412 google_apis::GDataErrorCode error
,
414 scoped_ptr
<google_apis::ResourceEntry
> entry
) {
415 if (error
== google_apis::HTTP_PRECONDITION
||
416 error
== google_apis::HTTP_CONFLICT
||
417 error
== google_apis::HTTP_NOT_FOUND
) {
418 // The remote file has unfetched remote change. Fetch latest metadata and
419 // update database with it.
420 // TODO(tzik): Consider adding local side low-priority dirtiness handling to
421 // handle this as ListChangesTask.
423 needs_remote_change_listing_
= true;
424 UpdateRemoteMetadata(remote_file_tracker_
->file_id(),
425 base::Bind(&ReturnRetryOnSuccess
, callback
));
429 SyncStatusCode status
= GDataErrorCodeToSyncStatusCode(error
);
430 if (status
!= SYNC_STATUS_OK
) {
431 callback
.Run(status
);
437 callback
.Run(SYNC_STATUS_FAILED
);
442 metadata_database()->UpdateByFileResource(
443 *drive::util::ConvertResourceEntryToFileResource(*entry
),
444 base::Bind(&LocalToRemoteSyncer::DidUpdateDatabaseForUploadExistingFile
,
445 weak_ptr_factory_
.GetWeakPtr(),
449 void LocalToRemoteSyncer::DidUpdateDatabaseForUploadExistingFile(
450 const SyncStatusCallback
& callback
,
451 SyncStatusCode status
) {
452 if (status
!= SYNC_STATUS_OK
) {
453 callback
.Run(status
);
458 bool should_success
= metadata_database()->FindFileByFileID(
459 remote_file_tracker_
->file_id(), &file
);
460 if (!should_success
) {
462 callback
.Run(SYNC_STATUS_FAILED
);
466 const FileDetails
& details
= file
.details();
467 base::FilePath title
= fileapi::VirtualPath::BaseName(target_path_
);
468 if (!details
.missing() &&
469 details
.file_kind() == FILE_KIND_FILE
&&
470 details
.title() == title
.AsUTF8Unsafe() &&
471 HasFileAsParent(details
,
472 remote_parent_folder_tracker_
->file_id())) {
473 metadata_database()->UpdateTracker(
474 remote_file_tracker_
->tracker_id(),
480 callback
.Run(SYNC_STATUS_RETRY
);
483 void LocalToRemoteSyncer::UpdateRemoteMetadata(
484 const std::string
& file_id
,
485 const SyncStatusCallback
& callback
) {
486 DCHECK(remote_file_tracker_
);
487 drive_service()->GetResourceEntry(
489 base::Bind(&LocalToRemoteSyncer::DidGetRemoteMetadata
,
490 weak_ptr_factory_
.GetWeakPtr(),
494 void LocalToRemoteSyncer::DidGetRemoteMetadata(
495 const std::string
& file_id
,
496 const SyncStatusCallback
& callback
,
497 google_apis::GDataErrorCode error
,
498 scoped_ptr
<google_apis::ResourceEntry
> entry
) {
499 if (error
== google_apis::HTTP_NOT_FOUND
) {
500 metadata_database()->UpdateByDeletedRemoteFile(file_id
, callback
);
504 SyncStatusCode status
= GDataErrorCodeToSyncStatusCode(error
);
505 if (status
!= SYNC_STATUS_OK
) {
506 callback
.Run(status
);
512 callback
.Run(SYNC_STATUS_FAILED
);
516 metadata_database()->UpdateByFileResource(
517 *drive::util::ConvertResourceEntryToFileResource(*entry
), callback
);
520 void LocalToRemoteSyncer::DidDeleteForUploadNewFile(
521 const SyncStatusCallback
& callback
,
522 SyncStatusCode status
) {
523 if (status
== SYNC_STATUS_HAS_CONFLICT
) {
524 UpdateRemoteMetadata(remote_file_tracker_
->file_id(),
525 base::Bind(&ReturnRetryOnSuccess
, callback
));
529 if (status
!= SYNC_STATUS_OK
) {
530 callback
.Run(status
);
534 UploadNewFile(callback
);
537 void LocalToRemoteSyncer::DidDeleteForCreateFolder(
538 const SyncStatusCallback
& callback
,
539 SyncStatusCode status
) {
540 if (status
== SYNC_STATUS_HAS_CONFLICT
) {
541 UpdateRemoteMetadata(remote_file_tracker_
->file_id(),
542 base::Bind(&ReturnRetryOnSuccess
, callback
));
546 if (status
!= SYNC_STATUS_OK
) {
547 callback
.Run(status
);
551 CreateRemoteFolder(callback
);
554 void LocalToRemoteSyncer::UploadNewFile(const SyncStatusCallback
& callback
) {
555 DCHECK(remote_parent_folder_tracker_
);
557 sync_action_
= SYNC_ACTION_ADDED
;
558 base::FilePath title
= fileapi::VirtualPath::BaseName(target_path_
);
559 drive_uploader()->UploadNewFile(
560 remote_parent_folder_tracker_
->file_id(),
562 title
.AsUTF8Unsafe(),
563 GetMimeTypeFromTitle(title
),
564 base::Bind(&LocalToRemoteSyncer::DidUploadNewFile
,
565 weak_ptr_factory_
.GetWeakPtr(),
567 google_apis::ProgressCallback());
570 void LocalToRemoteSyncer::DidUploadNewFile(
571 const SyncStatusCallback
& callback
,
572 google_apis::GDataErrorCode error
,
573 const GURL
& upload_location
,
574 scoped_ptr
<google_apis::ResourceEntry
> entry
) {
575 if (error
== google_apis::HTTP_NOT_FOUND
)
576 needs_remote_change_listing_
= true;
578 SyncStatusCode status
= GDataErrorCodeToSyncStatusCode(error
);
579 if (status
!= SYNC_STATUS_OK
) {
580 callback
.Run(status
);
586 callback
.Run(SYNC_STATUS_FAILED
);
590 metadata_database()->ReplaceActiveTrackerWithNewResource(
591 remote_parent_folder_tracker_
->tracker_id(),
592 *drive::util::ConvertResourceEntryToFileResource(*entry
),
596 void LocalToRemoteSyncer::CreateRemoteFolder(
597 const SyncStatusCallback
& callback
) {
598 DCHECK(remote_parent_folder_tracker_
);
600 base::FilePath title
= fileapi::VirtualPath::BaseName(target_path_
);
601 sync_action_
= SYNC_ACTION_ADDED
;
603 DCHECK(!folder_creator_
);
604 folder_creator_
.reset(new FolderCreator(
605 drive_service(), metadata_database(),
606 remote_parent_folder_tracker_
->file_id(),
607 title
.AsUTF8Unsafe()));
608 folder_creator_
->Run(base::Bind(
609 &LocalToRemoteSyncer::DidCreateRemoteFolder
,
610 weak_ptr_factory_
.GetWeakPtr(),
614 void LocalToRemoteSyncer::DidCreateRemoteFolder(
615 const SyncStatusCallback
& callback
,
616 const std::string
& file_id
,
617 SyncStatusCode status
) {
618 if (status
== SYNC_FILE_ERROR_NOT_FOUND
)
619 needs_remote_change_listing_
= true;
621 scoped_ptr
<FolderCreator
> deleter
= folder_creator_
.Pass();
622 if (status
!= SYNC_STATUS_OK
) {
623 callback
.Run(status
);
627 if (metadata_database()->TryNoSideEffectActivation(
628 remote_parent_folder_tracker_
->tracker_id(),
629 file_id
, callback
)) {
630 // |callback| will be invoked by MetadataDatabase in this case.
634 drive_service()->RemoveResourceFromDirectory(
635 remote_parent_folder_tracker_
->file_id(), file_id
,
636 base::Bind(&LocalToRemoteSyncer::DidDetachResourceForCreationConflict
,
637 weak_ptr_factory_
.GetWeakPtr(),
641 void LocalToRemoteSyncer::DidDetachResourceForCreationConflict(
642 const SyncStatusCallback
& callback
,
643 google_apis::GDataErrorCode error
) {
644 SyncStatusCode status
= GDataErrorCodeToSyncStatusCode(error
);
645 if (status
!= SYNC_STATUS_OK
) {
646 callback
.Run(status
);
650 callback
.Run(SYNC_STATUS_RETRY
);
653 bool LocalToRemoteSyncer::IsContextReady() {
654 return sync_context_
->GetDriveService() &&
655 sync_context_
->GetDriveUploader() &&
656 sync_context_
->GetMetadataDatabase();
659 drive::DriveServiceInterface
* LocalToRemoteSyncer::drive_service() {
660 set_used_network(true);
661 return sync_context_
->GetDriveService();
664 drive::DriveUploaderInterface
* LocalToRemoteSyncer::drive_uploader() {
665 set_used_network(true);
666 return sync_context_
->GetDriveUploader();
669 MetadataDatabase
* LocalToRemoteSyncer::metadata_database() {
670 return sync_context_
->GetMetadataDatabase();
673 } // namespace drive_backend
674 } // namespace sync_file_system