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