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