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 if (error == google_apis::HTTP_NOT_FOUND) {
374 metadata_database()->UpdateByDeletedRemoteFile(
375 remote_file_tracker_->file_id(), callback);
378 callback.Run(SYNC_STATUS_OK);
381 void LocalToRemoteSyncer::UploadExistingFile(
382 const SyncStatusCallback& callback) {
383 DCHECK(remote_file_tracker_);
384 DCHECK(remote_file_tracker_->has_synced_details());
386 base::PostTaskAndReplyWithResult(
387 sync_context_->GetBlockingTaskRunner(), FROM_HERE,
388 base::Bind(&drive::util::GetMd5Digest, local_path_),
389 base::Bind(&LocalToRemoteSyncer::DidGetMD5ForUpload,
390 weak_ptr_factory_.GetWeakPtr(),
394 void LocalToRemoteSyncer::DidGetMD5ForUpload(
395 const SyncStatusCallback& callback,
396 const std::string& local_file_md5) {
397 if (local_file_md5 == remote_file_tracker_->synced_details().md5()) {
398 // Local file is not changed.
399 callback.Run(SYNC_STATUS_OK);
403 sync_action_ = SYNC_ACTION_UPDATED;
405 drive::DriveUploader::UploadExistingFileOptions options;
406 options.etag = remote_file_tracker_->synced_details().etag();
407 drive_uploader()->UploadExistingFile(
408 remote_file_tracker_->file_id(),
410 "application/octet_stream",
412 base::Bind(&LocalToRemoteSyncer::DidUploadExistingFile,
413 weak_ptr_factory_.GetWeakPtr(),
415 google_apis::ProgressCallback());
418 void LocalToRemoteSyncer::DidUploadExistingFile(
419 const SyncStatusCallback& callback,
420 google_apis::GDataErrorCode error,
422 scoped_ptr<google_apis::ResourceEntry> entry) {
423 if (error == google_apis::HTTP_PRECONDITION ||
424 error == google_apis::HTTP_CONFLICT ||
425 error == google_apis::HTTP_NOT_FOUND) {
426 // The remote file has unfetched remote change. Fetch latest metadata and
427 // update database with it.
428 // TODO(tzik): Consider adding local side low-priority dirtiness handling to
429 // handle this as ListChangesTask.
431 needs_remote_change_listing_ = true;
432 UpdateRemoteMetadata(remote_file_tracker_->file_id(),
433 base::Bind(&ReturnRetryOnSuccess, callback));
437 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
438 if (status != SYNC_STATUS_OK) {
439 callback.Run(status);
445 callback.Run(SYNC_STATUS_FAILED);
450 metadata_database()->UpdateByFileResource(
451 *drive::util::ConvertResourceEntryToFileResource(*entry),
452 base::Bind(&LocalToRemoteSyncer::DidUpdateDatabaseForUploadExistingFile,
453 weak_ptr_factory_.GetWeakPtr(),
457 void LocalToRemoteSyncer::DidUpdateDatabaseForUploadExistingFile(
458 const SyncStatusCallback& callback,
459 SyncStatusCode status) {
460 if (status != SYNC_STATUS_OK) {
461 callback.Run(status);
466 bool should_success = metadata_database()->FindFileByFileID(
467 remote_file_tracker_->file_id(), &file);
468 if (!should_success) {
470 callback.Run(SYNC_STATUS_FAILED);
474 const FileDetails& details = file.details();
475 base::FilePath title = fileapi::VirtualPath::BaseName(target_path_);
476 if (!details.missing() &&
477 details.file_kind() == FILE_KIND_FILE &&
478 details.title() == title.AsUTF8Unsafe() &&
479 HasFileAsParent(details,
480 remote_parent_folder_tracker_->file_id())) {
481 metadata_database()->UpdateTracker(
482 remote_file_tracker_->tracker_id(),
488 callback.Run(SYNC_STATUS_RETRY);
491 void LocalToRemoteSyncer::UpdateRemoteMetadata(
492 const std::string& file_id,
493 const SyncStatusCallback& callback) {
494 DCHECK(remote_file_tracker_);
495 drive_service()->GetResourceEntry(
497 base::Bind(&LocalToRemoteSyncer::DidGetRemoteMetadata,
498 weak_ptr_factory_.GetWeakPtr(),
502 void LocalToRemoteSyncer::DidGetRemoteMetadata(
503 const std::string& file_id,
504 const SyncStatusCallback& callback,
505 google_apis::GDataErrorCode error,
506 scoped_ptr<google_apis::ResourceEntry> entry) {
507 if (error == google_apis::HTTP_NOT_FOUND) {
508 metadata_database()->UpdateByDeletedRemoteFile(file_id, callback);
512 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
513 if (status != SYNC_STATUS_OK) {
514 callback.Run(status);
520 callback.Run(SYNC_STATUS_FAILED);
524 metadata_database()->UpdateByFileResource(
525 *drive::util::ConvertResourceEntryToFileResource(*entry), callback);
528 void LocalToRemoteSyncer::DidDeleteForUploadNewFile(
529 const SyncStatusCallback& callback,
530 SyncStatusCode status) {
531 if (status == SYNC_STATUS_HAS_CONFLICT) {
532 UpdateRemoteMetadata(remote_file_tracker_->file_id(),
533 base::Bind(&ReturnRetryOnSuccess, callback));
537 if (status != SYNC_STATUS_OK) {
538 callback.Run(status);
542 UploadNewFile(callback);
545 void LocalToRemoteSyncer::DidDeleteForCreateFolder(
546 const SyncStatusCallback& callback,
547 SyncStatusCode status) {
548 if (status == SYNC_STATUS_HAS_CONFLICT) {
549 UpdateRemoteMetadata(remote_file_tracker_->file_id(),
550 base::Bind(&ReturnRetryOnSuccess, callback));
554 if (status != SYNC_STATUS_OK) {
555 callback.Run(status);
559 CreateRemoteFolder(callback);
562 void LocalToRemoteSyncer::UploadNewFile(const SyncStatusCallback& callback) {
563 DCHECK(remote_parent_folder_tracker_);
565 sync_action_ = SYNC_ACTION_ADDED;
566 base::FilePath title = fileapi::VirtualPath::BaseName(target_path_);
567 drive_uploader()->UploadNewFile(
568 remote_parent_folder_tracker_->file_id(),
570 title.AsUTF8Unsafe(),
571 GetMimeTypeFromTitle(title),
572 drive::DriveUploader::UploadNewFileOptions(),
573 base::Bind(&LocalToRemoteSyncer::DidUploadNewFile,
574 weak_ptr_factory_.GetWeakPtr(),
576 google_apis::ProgressCallback());
579 void LocalToRemoteSyncer::DidUploadNewFile(
580 const SyncStatusCallback& callback,
581 google_apis::GDataErrorCode error,
582 const GURL& upload_location,
583 scoped_ptr<google_apis::ResourceEntry> entry) {
584 if (error == google_apis::HTTP_NOT_FOUND)
585 needs_remote_change_listing_ = true;
587 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
588 if (status != SYNC_STATUS_OK) {
589 callback.Run(status);
595 callback.Run(SYNC_STATUS_FAILED);
599 metadata_database()->ReplaceActiveTrackerWithNewResource(
600 remote_parent_folder_tracker_->tracker_id(),
601 *drive::util::ConvertResourceEntryToFileResource(*entry),
605 void LocalToRemoteSyncer::CreateRemoteFolder(
606 const SyncStatusCallback& callback) {
607 DCHECK(remote_parent_folder_tracker_);
609 base::FilePath title = fileapi::VirtualPath::BaseName(target_path_);
610 sync_action_ = SYNC_ACTION_ADDED;
612 DCHECK(!folder_creator_);
613 folder_creator_.reset(new FolderCreator(
614 drive_service(), metadata_database(),
615 remote_parent_folder_tracker_->file_id(),
616 title.AsUTF8Unsafe()));
617 folder_creator_->Run(base::Bind(
618 &LocalToRemoteSyncer::DidCreateRemoteFolder,
619 weak_ptr_factory_.GetWeakPtr(),
623 void LocalToRemoteSyncer::DidCreateRemoteFolder(
624 const SyncStatusCallback& callback,
625 const std::string& file_id,
626 SyncStatusCode status) {
627 if (status == SYNC_FILE_ERROR_NOT_FOUND)
628 needs_remote_change_listing_ = true;
630 scoped_ptr<FolderCreator> deleter = folder_creator_.Pass();
631 if (status != SYNC_STATUS_OK) {
632 callback.Run(status);
636 MetadataDatabase::ActivationStatus activation_status =
637 metadata_database()->TryActivateTracker(
638 remote_parent_folder_tracker_->tracker_id(),
640 switch (activation_status) {
641 case MetadataDatabase::ACTIVATION_PENDING:
642 // |callback| will be invoked by MetadataDatabase later in this case.
644 case MetadataDatabase::ACTIVATION_FAILED_ANOTHER_ACTIVE_TRACKER:
645 // The activation failed due to another tracker that has another parent.
646 // Detach the folder from the current parent to avoid using this folder as
648 drive_service()->RemoveResourceFromDirectory(
649 remote_parent_folder_tracker_->file_id(), file_id,
650 base::Bind(&LocalToRemoteSyncer::DidDetachResourceForCreationConflict,
651 weak_ptr_factory_.GetWeakPtr(),
657 callback.Run(SYNC_STATUS_FAILED);
661 void LocalToRemoteSyncer::DidDetachResourceForCreationConflict(
662 const SyncStatusCallback& callback,
663 google_apis::GDataErrorCode error) {
664 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error);
665 if (status != SYNC_STATUS_OK) {
666 callback.Run(status);
670 callback.Run(SYNC_STATUS_RETRY);
673 bool LocalToRemoteSyncer::IsContextReady() {
674 return sync_context_->GetDriveService() &&
675 sync_context_->GetDriveUploader() &&
676 sync_context_->GetMetadataDatabase();
679 drive::DriveServiceInterface* LocalToRemoteSyncer::drive_service() {
680 set_used_network(true);
681 return sync_context_->GetDriveService();
684 drive::DriveUploaderInterface* LocalToRemoteSyncer::drive_uploader() {
685 set_used_network(true);
686 return sync_context_->GetDriveUploader();
689 MetadataDatabase* LocalToRemoteSyncer::metadata_database() {
690 return sync_context_->GetMetadataDatabase();
693 } // namespace drive_backend
694 } // namespace sync_file_system