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 "storage/common/fileapi/file_system_util.h" 33 34 namespace sync_file_system { 35 namespace drive_backend { 36 37 namespace { 38 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>(); 45 } 46 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; 55 } 56 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(); 61 } 62 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; 70 } 71 72 } // namespace 73 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 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 94 LocalToRemoteSyncer::~LocalToRemoteSyncer() { 95 } 96 97 void 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 storage::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 250 void LocalToRemoteSyncer::MoveToBackground(const Continuation& continuation, 251 scoped_ptr<SyncTaskToken> token) { 252 scoped_ptr<TaskBlocker> blocker(new TaskBlocker); 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::UpdateTaskBlocker( 272 token.Pass(), blocker.Pass(), 273 base::Bind(&LocalToRemoteSyncer::ContinueAsBackgroundTask, 274 weak_ptr_factory_.GetWeakPtr(), 275 continuation)); 276 } 277 278 void 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 // 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 316 void 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 334 void 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 = storage::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 MoveToBackground( 374 base::Bind(&LocalToRemoteSyncer::UpdateTrackerForReusedFolder, 375 weak_ptr_factory_.GetWeakPtr(), 376 remote_details), 377 token.Pass()); 378 return; 379 } 380 381 MoveToBackground(base::Bind(&LocalToRemoteSyncer::CreateRemoteFolder, 382 weak_ptr_factory_.GetWeakPtr()), 383 token.Pass()); 384 } 385 386 void LocalToRemoteSyncer::UpdateTrackerForReusedFolder( 387 const FileDetails& details, 388 scoped_ptr<SyncTaskToken> token) { 389 SyncStatusCode status = metadata_database()->UpdateTracker( 390 remote_file_tracker_->tracker_id(), details); 391 SyncCompleted(token.Pass(), status); 392 } 393 394 void LocalToRemoteSyncer::HandleExistingRemoteFile( 395 scoped_ptr<SyncTaskToken> token) { 396 DCHECK(remote_file_tracker_); 397 DCHECK(!remote_file_tracker_->dirty()); 398 DCHECK(remote_file_tracker_->active()); 399 DCHECK(remote_file_tracker_->has_synced_details()); 400 401 if (local_is_missing_) { 402 // Local file deletion for existing remote file. 403 MoveToBackground(base::Bind(&LocalToRemoteSyncer::DeleteRemoteFile, 404 weak_ptr_factory_.GetWeakPtr()), 405 token.Pass()); 406 return; 407 } 408 409 DCHECK(local_change_.IsAddOrUpdate()); 410 DCHECK(local_change_.IsFile() || local_change_.IsDirectory()); 411 412 const FileDetails& synced_details = remote_file_tracker_->synced_details(); 413 DCHECK(synced_details.file_kind() == FILE_KIND_FILE || 414 synced_details.file_kind() == FILE_KIND_FOLDER); 415 if (local_change_.IsFile()) { 416 if (synced_details.file_kind() == FILE_KIND_FILE) { 417 // Non-conflicting local file update to existing remote regular file. 418 MoveToBackground(base::Bind(&LocalToRemoteSyncer::UploadExistingFile, 419 weak_ptr_factory_.GetWeakPtr()), 420 token.Pass()); 421 return; 422 } 423 424 DCHECK_EQ(FILE_KIND_FOLDER, synced_details.file_kind()); 425 // Non-conflicting local file update to existing remote *folder*. 426 // Assuming this case as local folder deletion + local file creation, delete 427 // the remote folder and upload the file. 428 retry_on_success_ = true; 429 MoveToBackground(base::Bind(&LocalToRemoteSyncer::DeleteRemoteFile, 430 weak_ptr_factory_.GetWeakPtr()), 431 token.Pass()); 432 return; 433 } 434 435 DCHECK(local_change_.IsDirectory()); 436 if (synced_details.file_kind() == FILE_KIND_FILE) { 437 // Non-conflicting local folder creation to existing remote *file*. 438 // Assuming this case as local file deletion + local folder creation, delete 439 // the remote file and create a remote folder. 440 retry_on_success_ = true; 441 MoveToBackground(base::Bind(&LocalToRemoteSyncer::DeleteRemoteFile, 442 weak_ptr_factory_.GetWeakPtr()), 443 token.Pass()); 444 return; 445 } 446 447 // Non-conflicting local folder creation to existing remote folder. 448 DCHECK_EQ(FILE_KIND_FOLDER, synced_details.file_kind()); 449 SyncCompleted(token.Pass(), SYNC_STATUS_OK); 450 } 451 452 void LocalToRemoteSyncer::DeleteRemoteFile(scoped_ptr<SyncTaskToken> token) { 453 DCHECK(remote_file_tracker_); 454 DCHECK(remote_file_tracker_->has_synced_details()); 455 456 sync_action_ = SYNC_ACTION_DELETED; 457 drive_service()->DeleteResource( 458 remote_file_tracker_->file_id(), 459 remote_file_tracker_->synced_details().etag(), 460 base::Bind(&LocalToRemoteSyncer::DidDeleteRemoteFile, 461 weak_ptr_factory_.GetWeakPtr(), base::Passed(&token))); 462 } 463 464 void LocalToRemoteSyncer::DidDeleteRemoteFile( 465 scoped_ptr<SyncTaskToken> token, 466 google_apis::GDataErrorCode error) { 467 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error); 468 if (status != SYNC_STATUS_OK && 469 error != google_apis::HTTP_NOT_FOUND && 470 error != google_apis::HTTP_PRECONDITION && 471 error != google_apis::HTTP_CONFLICT) { 472 SyncCompleted(token.Pass(), status); 473 return; 474 } 475 476 // Handle NOT_FOUND case as SUCCESS case. 477 // For PRECONDITION / CONFLICT case, the remote file is modified since the 478 // last sync completed. As our policy for deletion-modification conflict 479 // resolution, ignore the local deletion. 480 if (status == SYNC_STATUS_OK || 481 error == google_apis::HTTP_NOT_FOUND) { 482 SyncStatusCode status = metadata_database()->UpdateByDeletedRemoteFile( 483 remote_file_tracker_->file_id()); 484 SyncCompleted(token.Pass(), status); 485 return; 486 } 487 488 SyncCompleted(token.Pass(), SYNC_STATUS_OK); 489 } 490 491 void LocalToRemoteSyncer::UploadExistingFile(scoped_ptr<SyncTaskToken> token) { 492 DCHECK(remote_file_tracker_); 493 DCHECK(remote_file_tracker_->has_synced_details()); 494 DCHECK(sync_context_->GetWorkerTaskRunner()->RunsTasksOnCurrentThread()); 495 496 const std::string local_file_md5 = drive::util::GetMd5Digest(local_path_); 497 if (local_file_md5 == remote_file_tracker_->synced_details().md5()) { 498 // Local file is not changed. 499 SyncCompleted(token.Pass(), SYNC_STATUS_OK); 500 return; 501 } 502 503 sync_action_ = SYNC_ACTION_UPDATED; 504 505 drive::DriveUploader::UploadExistingFileOptions options; 506 options.etag = remote_file_tracker_->synced_details().etag(); 507 drive_uploader()->UploadExistingFile( 508 remote_file_tracker_->file_id(), 509 local_path_, 510 "application/octet_stream", 511 options, 512 base::Bind(&LocalToRemoteSyncer::DidUploadExistingFile, 513 weak_ptr_factory_.GetWeakPtr(), base::Passed(&token)), 514 google_apis::ProgressCallback()); 515 } 516 517 void LocalToRemoteSyncer::DidUploadExistingFile( 518 scoped_ptr<SyncTaskToken> token, 519 google_apis::GDataErrorCode error, 520 const GURL&, 521 scoped_ptr<google_apis::FileResource> entry) { 522 if (error == google_apis::HTTP_PRECONDITION || 523 error == google_apis::HTTP_CONFLICT || 524 error == google_apis::HTTP_NOT_FOUND) { 525 // The remote file has unfetched remote change. Fetch latest metadata and 526 // update database with it. 527 // TODO(tzik): Consider adding local side low-priority dirtiness handling to 528 // handle this as ListChangesTask. 529 530 needs_remote_change_listing_ = true; 531 UpdateRemoteMetadata( 532 remote_file_tracker_->file_id(), 533 token.Pass()); 534 return; 535 } 536 537 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error); 538 if (status != SYNC_STATUS_OK) { 539 SyncCompleted(token.Pass(), status); 540 return; 541 } 542 543 if (!entry) { 544 NOTREACHED(); 545 SyncCompleted(token.Pass(), SYNC_STATUS_FAILED); 546 return; 547 } 548 549 DCHECK(entry); 550 status = metadata_database()->UpdateByFileResource(*entry); 551 if (status != SYNC_STATUS_OK) { 552 SyncCompleted(token.Pass(), status); 553 return; 554 } 555 556 FileMetadata file; 557 if (!metadata_database()->FindFileByFileID( 558 remote_file_tracker_->file_id(), &file)) { 559 NOTREACHED(); 560 SyncCompleted(token.Pass(), SYNC_STATUS_FAILED); 561 return; 562 } 563 564 const FileDetails& details = file.details(); 565 base::FilePath title = storage::VirtualPath::BaseName(target_path_); 566 if (!details.missing() && 567 details.file_kind() == FILE_KIND_FILE && 568 details.title() == title.AsUTF8Unsafe() && 569 HasFileAsParent(details, 570 remote_parent_folder_tracker_->file_id())) { 571 SyncStatusCode status = metadata_database()->UpdateTracker( 572 remote_file_tracker_->tracker_id(), file.details()); 573 SyncCompleted(token.Pass(), status); 574 return; 575 } 576 577 SyncCompleted(token.Pass(), SYNC_STATUS_RETRY); 578 } 579 580 void LocalToRemoteSyncer::UpdateRemoteMetadata( 581 const std::string& file_id, 582 scoped_ptr<SyncTaskToken> token) { 583 DCHECK(remote_file_tracker_); 584 585 drive_service()->GetFileResource( 586 file_id, 587 base::Bind(&LocalToRemoteSyncer::DidGetRemoteMetadata, 588 weak_ptr_factory_.GetWeakPtr(), 589 file_id, base::Passed(&token))); 590 } 591 592 void LocalToRemoteSyncer::DidGetRemoteMetadata( 593 const std::string& file_id, 594 scoped_ptr<SyncTaskToken> token, 595 google_apis::GDataErrorCode error, 596 scoped_ptr<google_apis::FileResource> entry) { 597 DCHECK(sync_context_->GetWorkerTaskRunner()->RunsTasksOnCurrentThread()); 598 599 if (error == google_apis::HTTP_NOT_FOUND) { 600 retry_on_success_ = true; 601 SyncStatusCode status = 602 metadata_database()->UpdateByDeletedRemoteFile(file_id); 603 SyncCompleted(token.Pass(), status); 604 return; 605 } 606 607 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error); 608 if (status != SYNC_STATUS_OK) { 609 SyncCompleted(token.Pass(), status); 610 return; 611 } 612 613 if (!entry) { 614 NOTREACHED(); 615 SyncCompleted(token.Pass(), SYNC_STATUS_FAILED); 616 return; 617 } 618 619 retry_on_success_ = true; 620 status = metadata_database()->UpdateByFileResource(*entry); 621 SyncCompleted(token.Pass(), status); 622 } 623 624 void LocalToRemoteSyncer::UploadNewFile(scoped_ptr<SyncTaskToken> token) { 625 DCHECK(remote_parent_folder_tracker_); 626 627 sync_action_ = SYNC_ACTION_ADDED; 628 base::FilePath title = storage::VirtualPath::BaseName(target_path_); 629 drive_uploader()->UploadNewFile( 630 remote_parent_folder_tracker_->file_id(), 631 local_path_, 632 title.AsUTF8Unsafe(), 633 GetMimeTypeFromTitle(title), 634 drive::DriveUploader::UploadNewFileOptions(), 635 base::Bind(&LocalToRemoteSyncer::DidUploadNewFile, 636 weak_ptr_factory_.GetWeakPtr(), 637 base::Passed(&token)), 638 google_apis::ProgressCallback()); 639 } 640 641 void LocalToRemoteSyncer::DidUploadNewFile( 642 scoped_ptr<SyncTaskToken> token, 643 google_apis::GDataErrorCode error, 644 const GURL& upload_location, 645 scoped_ptr<google_apis::FileResource> entry) { 646 if (error == google_apis::HTTP_NOT_FOUND) 647 needs_remote_change_listing_ = true; 648 649 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error); 650 if (status != SYNC_STATUS_OK) { 651 SyncCompleted(token.Pass(), status); 652 return; 653 } 654 655 if (!entry) { 656 NOTREACHED(); 657 SyncCompleted(token.Pass(), SYNC_STATUS_FAILED); 658 return; 659 } 660 661 status = metadata_database()->ReplaceActiveTrackerWithNewResource( 662 remote_parent_folder_tracker_->tracker_id(), *entry); 663 SyncCompleted(token.Pass(), status); 664 } 665 666 void LocalToRemoteSyncer::CreateRemoteFolder(scoped_ptr<SyncTaskToken> token) { 667 DCHECK(remote_parent_folder_tracker_); 668 669 base::FilePath title = storage::VirtualPath::BaseName(target_path_); 670 sync_action_ = SYNC_ACTION_ADDED; 671 672 DCHECK(!folder_creator_); 673 folder_creator_.reset(new FolderCreator( 674 drive_service(), metadata_database(), 675 remote_parent_folder_tracker_->file_id(), 676 title.AsUTF8Unsafe())); 677 folder_creator_->Run(base::Bind( 678 &LocalToRemoteSyncer::DidCreateRemoteFolder, 679 weak_ptr_factory_.GetWeakPtr(), 680 base::Passed(&token))); 681 } 682 683 void LocalToRemoteSyncer::DidCreateRemoteFolder( 684 scoped_ptr<SyncTaskToken> token, 685 const std::string& file_id, 686 SyncStatusCode status) { 687 if (status == SYNC_FILE_ERROR_NOT_FOUND) 688 needs_remote_change_listing_ = true; 689 690 scoped_ptr<FolderCreator> deleter = folder_creator_.Pass(); 691 if (status != SYNC_STATUS_OK) { 692 SyncCompleted(token.Pass(), status); 693 return; 694 } 695 696 status = SYNC_STATUS_FAILED; 697 MetadataDatabase::ActivationStatus activation_status = 698 metadata_database()->TryActivateTracker( 699 remote_parent_folder_tracker_->tracker_id(), 700 file_id, &status); 701 switch (activation_status) { 702 case MetadataDatabase::ACTIVATION_PENDING: 703 SyncCompleted(token.Pass(), status); 704 return; 705 case MetadataDatabase::ACTIVATION_FAILED_ANOTHER_ACTIVE_TRACKER: 706 // The activation failed due to another tracker that has another parent. 707 // Detach the folder from the current parent to avoid using this folder as 708 // active folder. 709 drive_service()->RemoveResourceFromDirectory( 710 remote_parent_folder_tracker_->file_id(), file_id, 711 base::Bind(&LocalToRemoteSyncer::DidDetachResourceForCreationConflict, 712 weak_ptr_factory_.GetWeakPtr(), base::Passed(&token))); 713 return; 714 } 715 716 NOTREACHED(); 717 SyncCompleted(token.Pass(), SYNC_STATUS_FAILED); 718 return; 719 } 720 721 void LocalToRemoteSyncer::DidDetachResourceForCreationConflict( 722 scoped_ptr<SyncTaskToken> token, 723 google_apis::GDataErrorCode error) { 724 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error); 725 if (status != SYNC_STATUS_OK) { 726 SyncCompleted(token.Pass(), status); 727 return; 728 } 729 730 SyncCompleted(token.Pass(), SYNC_STATUS_RETRY); 731 } 732 733 bool LocalToRemoteSyncer::IsContextReady() { 734 return sync_context_->GetDriveService() && 735 sync_context_->GetDriveUploader() && 736 sync_context_->GetMetadataDatabase(); 737 } 738 739 drive::DriveServiceInterface* LocalToRemoteSyncer::drive_service() { 740 set_used_network(true); 741 return sync_context_->GetDriveService(); 742 } 743 744 drive::DriveUploaderInterface* LocalToRemoteSyncer::drive_uploader() { 745 set_used_network(true); 746 return sync_context_->GetDriveUploader(); 747 } 748 749 MetadataDatabase* LocalToRemoteSyncer::metadata_database() { 750 return sync_context_->GetMetadataDatabase(); 751 } 752 753 } // namespace drive_backend 754 } // namespace sync_file_system 755