remote_to_local_syncer.cc revision 03b57e008b61dfcb1fbad3aea950ae0e001748b0
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/remote_to_local_syncer.h" 6 7#include "base/bind.h" 8#include "base/callback.h" 9#include "base/file_util.h" 10#include "base/format_macros.h" 11#include "base/location.h" 12#include "base/logging.h" 13#include "base/strings/stringprintf.h" 14#include "base/task_runner_util.h" 15#include "chrome/browser/drive/drive_api_util.h" 16#include "chrome/browser/drive/drive_service_interface.h" 17#include "chrome/browser/sync_file_system/drive_backend/callback_helper.h" 18#include "chrome/browser/sync_file_system/drive_backend/drive_backend_util.h" 19#include "chrome/browser/sync_file_system/drive_backend/metadata_database.h" 20#include "chrome/browser/sync_file_system/drive_backend/sync_engine_context.h" 21#include "chrome/browser/sync_file_system/drive_backend/sync_task_manager.h" 22#include "chrome/browser/sync_file_system/drive_backend/sync_task_token.h" 23#include "chrome/browser/sync_file_system/drive_backend/task_dependency_manager.h" 24#include "chrome/browser/sync_file_system/logger.h" 25#include "chrome/browser/sync_file_system/syncable_file_system_util.h" 26#include "extensions/common/extension.h" 27#include "google_apis/drive/drive_api_parser.h" 28#include "google_apis/drive/gdata_wapi_parser.h" 29#include "webkit/common/fileapi/file_system_util.h" 30 31namespace sync_file_system { 32namespace drive_backend { 33 34namespace { 35 36bool BuildFileSystemURL(MetadataDatabase* metadata_database, 37 const FileTracker& tracker, 38 storage::FileSystemURL* url) { 39 base::FilePath path; 40 if (!metadata_database->BuildPathForTracker( 41 tracker.tracker_id(), &path)) 42 return false; 43 44 GURL origin = 45 extensions::Extension::GetBaseURLFromExtensionId(tracker.app_id()); 46 *url = sync_file_system::CreateSyncableFileSystemURL(origin, path); 47 48 return true; 49} 50 51bool HasFolderAsParent(const FileDetails& details, 52 const std::string& folder_id) { 53 for (int i = 0; i < details.parent_folder_ids_size(); ++i) { 54 if (details.parent_folder_ids(i) == folder_id) 55 return true; 56 } 57 return false; 58} 59 60bool HasDisabledAppRoot(MetadataDatabase* database, 61 const FileTracker& tracker) { 62 DCHECK(tracker.active()); 63 FileTracker app_root_tracker; 64 if (database->FindAppRootTracker(tracker.app_id(), &app_root_tracker)) { 65 DCHECK(app_root_tracker.tracker_kind() == TRACKER_KIND_APP_ROOT || 66 app_root_tracker.tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT); 67 return app_root_tracker.tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT; 68 } 69 return false; 70} 71 72scoped_ptr<FileMetadata> GetFileMetadata(MetadataDatabase* database, 73 const std::string& file_id) { 74 scoped_ptr<FileMetadata> metadata(new FileMetadata); 75 if (!database->FindFileByFileID(file_id, metadata.get())) 76 metadata.reset(); 77 return metadata.Pass(); 78} 79 80// Creates a temporary file in |dir_path|. This must be called on an 81// IO-allowed task runner, and the runner must be given as |file_task_runner|. 82storage::ScopedFile CreateTemporaryFile(base::TaskRunner* file_task_runner) { 83 base::FilePath temp_file_path; 84 if (!base::CreateTemporaryFile(&temp_file_path)) 85 return storage::ScopedFile(); 86 87 return storage::ScopedFile(temp_file_path, 88 storage::ScopedFile::DELETE_ON_SCOPE_OUT, 89 file_task_runner); 90} 91 92} // namespace 93 94RemoteToLocalSyncer::RemoteToLocalSyncer(SyncEngineContext* sync_context) 95 : sync_context_(sync_context), 96 sync_action_(SYNC_ACTION_NONE), 97 prepared_(false), 98 sync_root_deletion_(false), 99 weak_ptr_factory_(this) { 100} 101 102RemoteToLocalSyncer::~RemoteToLocalSyncer() { 103} 104 105void RemoteToLocalSyncer::RunPreflight(scoped_ptr<SyncTaskToken> token) { 106 token->InitializeTaskLog("Remote -> Local"); 107 108 scoped_ptr<BlockingFactor> blocking_factor(new BlockingFactor); 109 blocking_factor->exclusive = true; 110 SyncTaskManager::UpdateBlockingFactor( 111 token.Pass(), blocking_factor.Pass(), 112 base::Bind(&RemoteToLocalSyncer::RunExclusive, 113 weak_ptr_factory_.GetWeakPtr())); 114} 115 116void RemoteToLocalSyncer::RunExclusive(scoped_ptr<SyncTaskToken> token) { 117 if (!drive_service() || !metadata_database() || !remote_change_processor()) { 118 token->RecordLog("Context not ready."); 119 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED); 120 return; 121 } 122 123 dirty_tracker_ = make_scoped_ptr(new FileTracker); 124 if (metadata_database()->GetNormalPriorityDirtyTracker( 125 dirty_tracker_.get())) { 126 token->RecordLog(base::StringPrintf( 127 "Start: tracker_id=%" PRId64, dirty_tracker_->tracker_id())); 128 metadata_database()->LowerTrackerPriority(dirty_tracker_->tracker_id()); 129 ResolveRemoteChange(token.Pass()); 130 return; 131 } 132 133 token->RecordLog("Nothing to do."); 134 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_NO_CHANGE_TO_SYNC); 135} 136 137void RemoteToLocalSyncer::ResolveRemoteChange(scoped_ptr<SyncTaskToken> token) { 138 DCHECK(dirty_tracker_); 139 remote_metadata_ = GetFileMetadata( 140 metadata_database(), dirty_tracker_->file_id()); 141 142 if (!remote_metadata_ || !remote_metadata_->has_details()) { 143 if (remote_metadata_ && !remote_metadata_->has_details()) { 144 token->RecordLog( 145 "Missing details of a remote file: " + remote_metadata_->file_id()); 146 NOTREACHED(); 147 } 148 token->RecordLog("Missing remote metadata case."); 149 HandleMissingRemoteMetadata(token.Pass()); 150 return; 151 } 152 153 DCHECK(remote_metadata_); 154 DCHECK(remote_metadata_->has_details()); 155 const FileDetails& remote_details = remote_metadata_->details(); 156 157 if (!dirty_tracker_->active() || 158 HasDisabledAppRoot(metadata_database(), *dirty_tracker_)) { 159 // Handle inactive tracker in SyncCompleted. 160 token->RecordLog("Inactive tracker case."); 161 SyncCompleted(token.Pass(), SYNC_STATUS_OK); 162 return; 163 } 164 165 DCHECK(dirty_tracker_->active()); 166 DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_)); 167 168 if (!dirty_tracker_->has_synced_details()) { 169 token->RecordLog(base::StringPrintf( 170 "Missing synced_details of an active tracker: %" PRId64, 171 dirty_tracker_->tracker_id())); 172 NOTREACHED(); 173 SyncCompleted(token.Pass(), SYNC_STATUS_FAILED); 174 return; 175 } 176 177 DCHECK(dirty_tracker_->has_synced_details()); 178 const FileDetails& synced_details = dirty_tracker_->synced_details(); 179 180 if (dirty_tracker_->tracker_id() == 181 metadata_database()->GetSyncRootTrackerID()) { 182 if (remote_details.missing() || 183 synced_details.title() != remote_details.title() || 184 remote_details.parent_folder_ids_size()) { 185 token->RecordLog("Sync-root deletion."); 186 HandleSyncRootDeletion(token.Pass()); 187 return; 188 } 189 token->RecordLog("Trivial sync-root change."); 190 SyncCompleted(token.Pass(), SYNC_STATUS_OK); 191 return; 192 } 193 194 DCHECK_NE(dirty_tracker_->tracker_id(), 195 metadata_database()->GetSyncRootTrackerID()); 196 197 if (remote_details.missing()) { 198 if (!synced_details.missing()) { 199 token->RecordLog("Remote file deletion."); 200 HandleDeletion(token.Pass()); 201 return; 202 } 203 204 DCHECK(synced_details.missing()); 205 token->RecordLog("Found a stray missing tracker: " + 206 dirty_tracker_->file_id()); 207 NOTREACHED(); 208 SyncCompleted(token.Pass(), SYNC_STATUS_OK); 209 return; 210 } 211 212 // Most of remote_details field is valid from here. 213 DCHECK(!remote_details.missing()); 214 215 if (synced_details.file_kind() != remote_details.file_kind()) { 216 token->RecordLog(base::StringPrintf( 217 "Found type mismatch between remote and local file: %s" 218 " type: (local) %d vs (remote) %d", 219 dirty_tracker_->file_id().c_str(), 220 synced_details.file_kind(), 221 remote_details.file_kind())); 222 NOTREACHED(); 223 SyncCompleted(token.Pass(), SYNC_STATUS_FAILED); 224 return; 225 } 226 DCHECK_EQ(synced_details.file_kind(), remote_details.file_kind()); 227 228 if (synced_details.file_kind() == FILE_KIND_UNSUPPORTED) { 229 token->RecordLog("Found an unsupported active file: " + 230 remote_metadata_->file_id()); 231 NOTREACHED(); 232 SyncCompleted(token.Pass(), SYNC_STATUS_FAILED); 233 return; 234 } 235 DCHECK(remote_details.file_kind() == FILE_KIND_FILE || 236 remote_details.file_kind() == FILE_KIND_FOLDER); 237 238 if (synced_details.title() != remote_details.title()) { 239 // Handle rename as deletion + addition. 240 token->RecordLog("Detected file rename."); 241 Prepare(base::Bind(&RemoteToLocalSyncer::DidPrepareForDeletion, 242 weak_ptr_factory_.GetWeakPtr(), 243 base::Passed(&token))); 244 return; 245 } 246 DCHECK_EQ(synced_details.title(), remote_details.title()); 247 248 FileTracker parent_tracker; 249 if (!metadata_database()->FindTrackerByTrackerID( 250 dirty_tracker_->parent_tracker_id(), &parent_tracker)) { 251 token->RecordLog("Missing parent tracker for a non sync-root tracker: " 252 + dirty_tracker_->file_id()); 253 NOTREACHED(); 254 SyncCompleted(token.Pass(), SYNC_STATUS_FAILED); 255 return; 256 } 257 258 if (!HasFolderAsParent(remote_details, parent_tracker.file_id())) { 259 // Handle reorganize as deletion + addition. 260 token->RecordLog("Detected file reorganize."); 261 Prepare(base::Bind(&RemoteToLocalSyncer::DidPrepareForDeletion, 262 weak_ptr_factory_.GetWeakPtr(), 263 base::Passed(&token))); 264 return; 265 } 266 267 if (synced_details.file_kind() == FILE_KIND_FILE) { 268 if (synced_details.md5() != remote_details.md5()) { 269 token->RecordLog("Detected file content update."); 270 HandleContentUpdate(token.Pass()); 271 return; 272 } 273 } else { 274 DCHECK_EQ(FILE_KIND_FOLDER, synced_details.file_kind()); 275 if (synced_details.missing()) { 276 token->RecordLog("Detected folder update."); 277 HandleFolderUpdate(token.Pass()); 278 return; 279 } 280 if (dirty_tracker_->needs_folder_listing()) { 281 token->RecordLog("Needs listing folder."); 282 ListFolderContent(token.Pass()); 283 return; 284 } 285 SyncCompleted(token.Pass(), SYNC_STATUS_OK); 286 return; 287 } 288 289 token->RecordLog("Trivial file change."); 290 SyncCompleted(token.Pass(), SYNC_STATUS_OK); 291} 292 293void RemoteToLocalSyncer::HandleMissingRemoteMetadata( 294 scoped_ptr<SyncTaskToken> token) { 295 DCHECK(dirty_tracker_); 296 297 drive_service()->GetFileResource( 298 dirty_tracker_->file_id(), 299 base::Bind(&RemoteToLocalSyncer::DidGetRemoteMetadata, 300 weak_ptr_factory_.GetWeakPtr(), 301 base::Passed(&token))); 302} 303 304void RemoteToLocalSyncer::DidGetRemoteMetadata( 305 scoped_ptr<SyncTaskToken> token, 306 google_apis::GDataErrorCode error, 307 scoped_ptr<google_apis::FileResource> entry) { 308 DCHECK(sync_context_->GetWorkerTaskRunner()->RunsTasksOnCurrentThread()); 309 310 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error); 311 if (status != SYNC_STATUS_OK && 312 error != google_apis::HTTP_NOT_FOUND) { 313 SyncCompleted(token.Pass(), status); 314 return; 315 } 316 317 if (error == google_apis::HTTP_NOT_FOUND) { 318 metadata_database()->UpdateByDeletedRemoteFile( 319 dirty_tracker_->file_id(), SyncCompletedCallback(token.Pass())); 320 return; 321 } 322 323 if (!entry) { 324 NOTREACHED(); 325 SyncCompleted(token.Pass(), SYNC_STATUS_FAILED); 326 return; 327 } 328 329 metadata_database()->UpdateByFileResource( 330 *entry, 331 base::Bind(&RemoteToLocalSyncer::DidUpdateDatabaseForRemoteMetadata, 332 weak_ptr_factory_.GetWeakPtr(), 333 base::Passed(&token))); 334} 335 336void RemoteToLocalSyncer::DidUpdateDatabaseForRemoteMetadata( 337 scoped_ptr<SyncTaskToken> token, 338 SyncStatusCode status) { 339 if (status != SYNC_STATUS_OK) { 340 SyncCompleted(token.Pass(), status); 341 return; 342 } 343 344 metadata_database()->PromoteDemotedTracker(dirty_tracker_->tracker_id()); 345 346 // Do not update |dirty_tracker_|. 347 SyncCompleted(token.Pass(), SYNC_STATUS_RETRY); 348} 349 350void RemoteToLocalSyncer::DidPrepareForAddOrUpdateFile( 351 scoped_ptr<SyncTaskToken> token, 352 SyncStatusCode status) { 353 if (status != SYNC_STATUS_OK) { 354 SyncCompleted(token.Pass(), status); 355 return; 356 } 357 358 DCHECK(url_.is_valid()); 359 DCHECK(local_metadata_); 360 DCHECK(local_changes_); 361 362 // Check if the local file exists. 363 if (local_metadata_->file_type == SYNC_FILE_TYPE_UNKNOWN || 364 (!local_changes_->empty() && local_changes_->back().IsDelete())) { 365 sync_action_ = SYNC_ACTION_ADDED; 366 // Missing local file case. 367 // Download the file and add it to local as a new file. 368 DownloadFile(token.Pass()); 369 return; 370 } 371 372 DCHECK(local_changes_->empty() || local_changes_->back().IsAddOrUpdate()); 373 if (local_changes_->empty()) { 374 if (local_metadata_->file_type == SYNC_FILE_TYPE_FILE) { 375 sync_action_ = SYNC_ACTION_UPDATED; 376 // Download the file and overwrite the existing local file. 377 DownloadFile(token.Pass()); 378 return; 379 } 380 381 DCHECK_EQ(SYNC_FILE_TYPE_DIRECTORY, local_metadata_->file_type); 382 383 // Got a remote regular file modification for existing local folder. 384 // Our policy prioritize folders in this case. 385 // Let local-to-remote sync phase process this change. 386 remote_change_processor()->RecordFakeLocalChange( 387 url_, 388 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, 389 local_metadata_->file_type), 390 SyncCompletedCallback(token.Pass())); 391 return; 392 } 393 394 DCHECK(local_changes_->back().IsAddOrUpdate()); 395 // Conflict case. 396 // Do nothing for the change now, and handle this in LocalToRemoteSync phase. 397 SyncCompleted(token.Pass(), SYNC_STATUS_RETRY); 398} 399 400void RemoteToLocalSyncer::HandleFolderUpdate( 401 scoped_ptr<SyncTaskToken> token) { 402 DCHECK(dirty_tracker_); 403 DCHECK(dirty_tracker_->active()); 404 DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_)); 405 406 DCHECK(remote_metadata_); 407 DCHECK(remote_metadata_->has_details()); 408 DCHECK(!remote_metadata_->details().missing()); 409 DCHECK_EQ(FILE_KIND_FOLDER, remote_metadata_->details().file_kind()); 410 411 Prepare(base::Bind(&RemoteToLocalSyncer::DidPrepareForFolderUpdate, 412 weak_ptr_factory_.GetWeakPtr(), 413 base::Passed(&token))); 414} 415 416void RemoteToLocalSyncer::DidPrepareForFolderUpdate( 417 scoped_ptr<SyncTaskToken> token, 418 SyncStatusCode status) { 419 if (status != SYNC_STATUS_OK) { 420 SyncCompleted(token.Pass(), status); 421 return; 422 } 423 424 DCHECK(url_.is_valid()); 425 DCHECK(local_metadata_); 426 DCHECK(local_changes_); 427 428 // Check if the local file exists. 429 if (local_metadata_->file_type == SYNC_FILE_TYPE_UNKNOWN || 430 (!local_changes_->empty() && local_changes_->back().IsDelete())) { 431 sync_action_ = SYNC_ACTION_ADDED; 432 // No local file exists at the path. 433 CreateFolder(token.Pass()); 434 return; 435 } 436 437 if (local_metadata_->file_type == SYNC_FILE_TYPE_DIRECTORY) { 438 // There already exists a folder, nothing left to do. 439 if (dirty_tracker_->needs_folder_listing() && 440 !dirty_tracker_->synced_details().missing()) { 441 ListFolderContent(token.Pass()); 442 } else { 443 SyncCompleted(token.Pass(), SYNC_STATUS_OK); 444 } 445 return; 446 } 447 448 DCHECK_EQ(SYNC_FILE_TYPE_FILE, local_metadata_->file_type); 449 sync_action_ = SYNC_ACTION_ADDED; 450 // Got a remote folder for existing local file. 451 // Our policy prioritize folders in this case. 452 CreateFolder(token.Pass()); 453} 454 455void RemoteToLocalSyncer::HandleSyncRootDeletion( 456 scoped_ptr<SyncTaskToken> token) { 457 sync_root_deletion_ = true; 458 SyncCompleted(token.Pass(), SYNC_STATUS_OK); 459} 460 461void RemoteToLocalSyncer::HandleDeletion( 462 scoped_ptr<SyncTaskToken> token) { 463 DCHECK(dirty_tracker_); 464 DCHECK(dirty_tracker_->active()); 465 DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_)); 466 DCHECK(dirty_tracker_->has_synced_details()); 467 DCHECK(!dirty_tracker_->synced_details().missing()); 468 469 DCHECK(remote_metadata_); 470 DCHECK(remote_metadata_->has_details()); 471 DCHECK(remote_metadata_->details().missing()); 472 473 Prepare(base::Bind(&RemoteToLocalSyncer::DidPrepareForDeletion, 474 weak_ptr_factory_.GetWeakPtr(), 475 base::Passed(&token))); 476} 477 478void RemoteToLocalSyncer::DidPrepareForDeletion( 479 scoped_ptr<SyncTaskToken> token, 480 SyncStatusCode status) { 481 if (status != SYNC_STATUS_OK) { 482 SyncCompleted(token.Pass(), status); 483 return; 484 } 485 486 DCHECK(url_.is_valid()); 487 DCHECK(local_metadata_); 488 DCHECK(local_changes_); 489 490 // Check if the local file exists. 491 if (local_metadata_->file_type == SYNC_FILE_TYPE_UNKNOWN || 492 (!local_changes_->empty() && local_changes_->back().IsDelete())) { 493 // No local file exists at the path. 494 SyncCompleted(token.Pass(), SYNC_STATUS_OK); 495 return; 496 } 497 498 DCHECK(local_changes_->empty() || local_changes_->back().IsAddOrUpdate()); 499 if (local_changes_->empty()) { 500 sync_action_ = SYNC_ACTION_DELETED; 501 DeleteLocalFile(token.Pass()); 502 return; 503 } 504 505 DCHECK(local_changes_->back().IsAddOrUpdate()); 506 // File is remotely deleted and locally updated. 507 // Ignore the remote deletion and handle it as if applied successfully. 508 SyncCompleted(token.Pass(), SYNC_STATUS_OK); 509} 510 511void RemoteToLocalSyncer::HandleContentUpdate( 512 scoped_ptr<SyncTaskToken> token) { 513 DCHECK(dirty_tracker_); 514 DCHECK(dirty_tracker_->active()); 515 DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_)); 516 DCHECK(dirty_tracker_->has_synced_details()); 517 DCHECK_EQ(FILE_KIND_FILE, dirty_tracker_->synced_details().file_kind()); 518 519 DCHECK(remote_metadata_); 520 DCHECK(remote_metadata_->has_details()); 521 DCHECK(!remote_metadata_->details().missing()); 522 523 DCHECK_NE(dirty_tracker_->synced_details().md5(), 524 remote_metadata_->details().md5()); 525 526 Prepare(base::Bind(&RemoteToLocalSyncer::DidPrepareForAddOrUpdateFile, 527 weak_ptr_factory_.GetWeakPtr(), base::Passed(&token))); 528} 529 530void RemoteToLocalSyncer::ListFolderContent( 531 scoped_ptr<SyncTaskToken> token) { 532 DCHECK(dirty_tracker_); 533 DCHECK(dirty_tracker_->active()); 534 DCHECK(!HasDisabledAppRoot(metadata_database(), *dirty_tracker_)); 535 DCHECK(dirty_tracker_->has_synced_details()); 536 DCHECK(!dirty_tracker_->synced_details().missing()); 537 DCHECK_EQ(FILE_KIND_FOLDER, dirty_tracker_->synced_details().file_kind()); 538 DCHECK(dirty_tracker_->needs_folder_listing()); 539 540 DCHECK(remote_metadata_); 541 DCHECK(remote_metadata_->has_details()); 542 DCHECK(!remote_metadata_->details().missing()); 543 544 // TODO(tzik): Replace this call with ChildList version. 545 drive_service()->GetFileListInDirectory( 546 dirty_tracker_->file_id(), 547 base::Bind(&RemoteToLocalSyncer::DidListFolderContent, 548 weak_ptr_factory_.GetWeakPtr(), 549 base::Passed(&token), 550 base::Passed(make_scoped_ptr(new FileIDList)))); 551} 552 553void RemoteToLocalSyncer::DidListFolderContent( 554 scoped_ptr<SyncTaskToken> token, 555 scoped_ptr<FileIDList> children, 556 google_apis::GDataErrorCode error, 557 scoped_ptr<google_apis::FileList> file_list) { 558 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error); 559 if (status != SYNC_STATUS_OK) { 560 SyncCompleted(token.Pass(), status); 561 return; 562 } 563 564 if (!file_list) { 565 NOTREACHED(); 566 SyncCompleted(token.Pass(), SYNC_STATUS_FAILED); 567 return; 568 } 569 570 children->reserve(children->size() + file_list->items().size()); 571 for (ScopedVector<google_apis::FileResource>::const_iterator itr = 572 file_list->items().begin(); 573 itr != file_list->items().end(); 574 ++itr) { 575 children->push_back((*itr)->file_id()); 576 } 577 578 if (!file_list->next_link().is_empty()) { 579 drive_service()->GetRemainingFileList( 580 file_list->next_link(), 581 base::Bind(&RemoteToLocalSyncer::DidListFolderContent, 582 weak_ptr_factory_.GetWeakPtr(), 583 base::Passed(&token), base::Passed(&children))); 584 return; 585 } 586 587 metadata_database()->PopulateFolderByChildList( 588 dirty_tracker_->file_id(), *children, 589 SyncCompletedCallback(token.Pass())); 590} 591 592void RemoteToLocalSyncer::SyncCompleted(scoped_ptr<SyncTaskToken> token, 593 SyncStatusCode status) { 594 token->RecordLog(base::StringPrintf( 595 "[Remote -> Local]: Finished: action=%s, tracker=%" PRId64 " status=%s", 596 SyncActionToString(sync_action_), dirty_tracker_->tracker_id(), 597 SyncStatusCodeToString(status))); 598 599 if (sync_root_deletion_) { 600 FinalizeSync(token.Pass(), SYNC_STATUS_OK); 601 return; 602 } 603 604 if (status == SYNC_STATUS_RETRY) { 605 FinalizeSync(token.Pass(), SYNC_STATUS_OK); 606 return; 607 } 608 609 if (status != SYNC_STATUS_OK) { 610 FinalizeSync(token.Pass(), status); 611 return; 612 } 613 614 DCHECK(dirty_tracker_); 615 DCHECK(remote_metadata_); 616 DCHECK(remote_metadata_->has_details()); 617 618 FileDetails updated_details = remote_metadata_->details(); 619 if (!dirty_tracker_->active() || 620 HasDisabledAppRoot(metadata_database(), *dirty_tracker_)) { 621 // Operations for an inactive tracker don't update file content. 622 if (dirty_tracker_->has_synced_details()) 623 updated_details.set_md5(dirty_tracker_->synced_details().md5()); 624 if (!dirty_tracker_->active()) { 625 // Keep missing true, as the change hasn't been synced to local. 626 updated_details.clear_md5(); 627 updated_details.set_missing(true); 628 } 629 } 630 metadata_database()->UpdateTracker( 631 dirty_tracker_->tracker_id(), 632 updated_details, 633 base::Bind(&RemoteToLocalSyncer::FinalizeSync, 634 weak_ptr_factory_.GetWeakPtr(), 635 base::Passed(&token))); 636} 637 638void RemoteToLocalSyncer::FinalizeSync(scoped_ptr<SyncTaskToken> token, 639 SyncStatusCode status) { 640 if (prepared_) { 641 remote_change_processor()->FinalizeRemoteSync( 642 url_, false /* clear_local_change */, 643 base::Bind(SyncTaskManager::NotifyTaskDone, 644 base::Passed(&token), status)); 645 return; 646 } 647 648 SyncTaskManager::NotifyTaskDone(token.Pass(), status); 649} 650 651void RemoteToLocalSyncer::Prepare(const SyncStatusCallback& callback) { 652 bool should_success = BuildFileSystemURL( 653 metadata_database(), *dirty_tracker_, &url_); 654 DCHECK(should_success); 655 DCHECK(url_.is_valid()); 656 remote_change_processor()->PrepareForProcessRemoteChange( 657 url_, 658 base::Bind(&RemoteToLocalSyncer::DidPrepare, 659 weak_ptr_factory_.GetWeakPtr(), 660 callback)); 661} 662 663void RemoteToLocalSyncer::DidPrepare(const SyncStatusCallback& callback, 664 SyncStatusCode status, 665 const SyncFileMetadata& local_metadata, 666 const FileChangeList& local_changes) { 667 if (status != SYNC_STATUS_OK) { 668 callback.Run(status); 669 return; 670 } 671 prepared_ = true; 672 673 local_metadata_.reset(new SyncFileMetadata(local_metadata)); 674 local_changes_.reset(new FileChangeList(local_changes)); 675 676 callback.Run(status); 677} 678 679void RemoteToLocalSyncer::DeleteLocalFile(scoped_ptr<SyncTaskToken> token) { 680 remote_change_processor()->ApplyRemoteChange( 681 FileChange(FileChange::FILE_CHANGE_DELETE, SYNC_FILE_TYPE_UNKNOWN), 682 base::FilePath(), 683 url_, 684 SyncCompletedCallback(token.Pass())); 685} 686 687void RemoteToLocalSyncer::DownloadFile(scoped_ptr<SyncTaskToken> token) { 688 DCHECK(sync_context_->GetWorkerTaskRunner()->RunsTasksOnCurrentThread()); 689 690 storage::ScopedFile file = CreateTemporaryFile( 691 make_scoped_refptr(sync_context_->GetWorkerTaskRunner())); 692 693 base::FilePath path = file.path(); 694 drive_service()->DownloadFile( 695 path, remote_metadata_->file_id(), 696 base::Bind(&RemoteToLocalSyncer::DidDownloadFile, 697 weak_ptr_factory_.GetWeakPtr(), 698 base::Passed(&token), base::Passed(&file)), 699 google_apis::GetContentCallback(), 700 google_apis::ProgressCallback()); 701} 702 703void RemoteToLocalSyncer::DidDownloadFile(scoped_ptr<SyncTaskToken> token, 704 storage::ScopedFile file, 705 google_apis::GDataErrorCode error, 706 const base::FilePath&) { 707 DCHECK(sync_context_->GetWorkerTaskRunner()->RunsTasksOnCurrentThread()); 708 709 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error); 710 if (status != SYNC_STATUS_OK) { 711 SyncCompleted(token.Pass(), status); 712 return; 713 } 714 715 base::FilePath path = file.path(); 716 const std::string md5 = drive::util::GetMd5Digest(path); 717 if (md5.empty()) { 718 SyncCompleted(token.Pass(), SYNC_FILE_ERROR_NOT_FOUND); 719 return; 720 } 721 722 if (md5 != remote_metadata_->details().md5()) { 723 // File has been modified since last metadata retrieval. 724 SyncCompleted(token.Pass(), SYNC_STATUS_RETRY); 725 return; 726 } 727 728 remote_change_processor()->ApplyRemoteChange( 729 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, SYNC_FILE_TYPE_FILE), 730 path, url_, 731 base::Bind(&RemoteToLocalSyncer::DidApplyDownload, 732 weak_ptr_factory_.GetWeakPtr(), 733 base::Passed(&token), base::Passed(&file))); 734} 735 736void RemoteToLocalSyncer::DidApplyDownload(scoped_ptr<SyncTaskToken> token, 737 storage::ScopedFile, 738 SyncStatusCode status) { 739 SyncCompleted(token.Pass(), status); 740} 741 742void RemoteToLocalSyncer::CreateFolder(scoped_ptr<SyncTaskToken> token) { 743 remote_change_processor()->ApplyRemoteChange( 744 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, 745 SYNC_FILE_TYPE_DIRECTORY), 746 base::FilePath(), url_, 747 SyncCompletedCallback(token.Pass())); 748} 749 750drive::DriveServiceInterface* RemoteToLocalSyncer::drive_service() { 751 return sync_context_->GetDriveService(); 752} 753 754MetadataDatabase* RemoteToLocalSyncer::metadata_database() { 755 return sync_context_->GetMetadataDatabase(); 756} 757 758RemoteChangeProcessor* RemoteToLocalSyncer::remote_change_processor() { 759 DCHECK(sync_context_->GetRemoteChangeProcessor()); 760 return sync_context_->GetRemoteChangeProcessor(); 761} 762 763SyncStatusCallback RemoteToLocalSyncer::SyncCompletedCallback( 764 scoped_ptr<SyncTaskToken> token) { 765 return base::Bind(&RemoteToLocalSyncer::SyncCompleted, 766 weak_ptr_factory_.GetWeakPtr(), 767 base::Passed(&token)); 768} 769 770} // namespace drive_backend 771} // namespace sync_file_system 772