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_v1/local_sync_delegate.h" 6 7#include "base/bind.h" 8#include "base/callback.h" 9#include "chrome/browser/sync_file_system/conflict_resolution_resolver.h" 10#include "chrome/browser/sync_file_system/drive_backend_v1/api_util.h" 11#include "chrome/browser/sync_file_system/drive_backend_v1/drive_metadata_store.h" 12#include "chrome/browser/sync_file_system/logger.h" 13#include "chrome/browser/sync_file_system/syncable_file_system_util.h" 14 15namespace sync_file_system { 16namespace drive_backend { 17 18LocalSyncDelegate::LocalSyncDelegate( 19 DriveFileSyncService* sync_service, 20 const FileChange& local_change, 21 const base::FilePath& local_path, 22 const SyncFileMetadata& local_metadata, 23 const fileapi::FileSystemURL& url) 24 : sync_service_(sync_service), 25 operation_(SYNC_OPERATION_NONE), 26 url_(url), 27 local_change_(local_change), 28 local_path_(local_path), 29 local_metadata_(local_metadata), 30 has_drive_metadata_(false), 31 has_remote_change_(false), 32 weak_factory_(this) {} 33 34LocalSyncDelegate::~LocalSyncDelegate() {} 35 36void LocalSyncDelegate::Run(const SyncStatusCallback& callback) { 37 // TODO(nhiroki): support directory operations (http://crbug.com/161442). 38 DCHECK(IsSyncFSDirectoryOperationEnabled() || !local_change_.IsDirectory()); 39 operation_ = SYNC_OPERATION_NONE; 40 41 has_drive_metadata_ = 42 metadata_store()->ReadEntry(url_, &drive_metadata_) == SYNC_STATUS_OK; 43 44 if (!has_drive_metadata_) 45 drive_metadata_.set_md5_checksum(std::string()); 46 47 sync_service_->EnsureOriginRootDirectory( 48 url_.origin(), 49 base::Bind(&LocalSyncDelegate::DidGetOriginRoot, 50 weak_factory_.GetWeakPtr(), 51 callback)); 52} 53 54void LocalSyncDelegate::DidGetOriginRoot( 55 const SyncStatusCallback& callback, 56 SyncStatusCode status, 57 const std::string& origin_resource_id) { 58 if (status != SYNC_STATUS_OK) { 59 callback.Run(status); 60 return; 61 } 62 63 origin_resource_id_ = origin_resource_id; 64 65 has_remote_change_ = 66 remote_change_handler()->GetChangeForURL(url_, &remote_change_); 67 if (has_remote_change_ && drive_metadata_.resource_id().empty()) 68 drive_metadata_.set_resource_id(remote_change_.resource_id); 69 70 SyncFileType remote_file_type = 71 has_remote_change_ ? remote_change_.change.file_type() : 72 has_drive_metadata_ ? 73 DriveFileSyncService::DriveMetadataResourceTypeToSyncFileType( 74 drive_metadata_.type()) 75 : SYNC_FILE_TYPE_UNKNOWN; 76 77 DCHECK_EQ(SYNC_OPERATION_NONE, operation_); 78 operation_ = LocalSyncOperationResolver::Resolve( 79 local_change_, 80 has_remote_change_ ? &remote_change_.change : NULL, 81 has_drive_metadata_ ? &drive_metadata_ : NULL); 82 83 util::Log(logging::LOG_VERBOSE, FROM_HERE, 84 "ApplyLocalChange for %s local_change:%s ===> %s", 85 url_.DebugString().c_str(), 86 local_change_.DebugString().c_str(), 87 SyncOperationTypeToString(operation_)); 88 89 switch (operation_) { 90 case SYNC_OPERATION_ADD_FILE: 91 UploadNewFile(callback); 92 return; 93 case SYNC_OPERATION_ADD_DIRECTORY: 94 CreateDirectory(callback); 95 return; 96 case SYNC_OPERATION_UPDATE_FILE: 97 UploadExistingFile(callback); 98 return; 99 case SYNC_OPERATION_DELETE: 100 Delete(callback); 101 return; 102 case SYNC_OPERATION_NONE: 103 callback.Run(SYNC_STATUS_OK); 104 return; 105 case SYNC_OPERATION_CONFLICT: 106 HandleConflict(callback); 107 return; 108 case SYNC_OPERATION_RESOLVE_TO_LOCAL: 109 ResolveToLocal(callback); 110 return; 111 case SYNC_OPERATION_RESOLVE_TO_REMOTE: 112 ResolveToRemote(callback, remote_file_type); 113 return; 114 case SYNC_OPERATION_DELETE_METADATA: 115 DeleteMetadata(base::Bind( 116 &LocalSyncDelegate::DidApplyLocalChange, 117 weak_factory_.GetWeakPtr(), callback, google_apis::HTTP_SUCCESS)); 118 return; 119 case SYNC_OPERATION_FAIL: { 120 callback.Run(SYNC_STATUS_FAILED); 121 return; 122 } 123 } 124 NOTREACHED(); 125 callback.Run(SYNC_STATUS_FAILED); 126} 127 128void LocalSyncDelegate::UploadNewFile(const SyncStatusCallback& callback) { 129 api_util()->UploadNewFile( 130 origin_resource_id_, 131 local_path_, 132 DriveFileSyncService::PathToTitle(url_.path()), 133 base::Bind(&LocalSyncDelegate::DidUploadNewFile, 134 weak_factory_.GetWeakPtr(), callback)); 135} 136 137void LocalSyncDelegate::DidUploadNewFile( 138 const SyncStatusCallback& callback, 139 google_apis::GDataErrorCode error, 140 const std::string& resource_id, 141 const std::string& md5) { 142 switch (error) { 143 case google_apis::HTTP_CREATED: 144 UpdateMetadata( 145 resource_id, md5, DriveMetadata::RESOURCE_TYPE_FILE, 146 base::Bind(&LocalSyncDelegate::DidApplyLocalChange, 147 weak_factory_.GetWeakPtr(), callback, error)); 148 sync_service_->NotifyObserversFileStatusChanged( 149 url_, 150 SYNC_FILE_STATUS_SYNCED, 151 SYNC_ACTION_ADDED, 152 SYNC_DIRECTION_LOCAL_TO_REMOTE); 153 return; 154 case google_apis::HTTP_CONFLICT: 155 HandleCreationConflict(resource_id, DriveMetadata::RESOURCE_TYPE_FILE, 156 callback); 157 return; 158 default: 159 callback.Run(GDataErrorCodeToSyncStatusCodeWrapper(error)); 160 } 161} 162 163void LocalSyncDelegate::CreateDirectory(const SyncStatusCallback& callback) { 164 DCHECK(IsSyncFSDirectoryOperationEnabled()); 165 api_util()->CreateDirectory( 166 origin_resource_id_, 167 DriveFileSyncService::PathToTitle(url_.path()), 168 base::Bind(&LocalSyncDelegate::DidCreateDirectory, 169 weak_factory_.GetWeakPtr(), callback)); 170} 171 172void LocalSyncDelegate::DidCreateDirectory( 173 const SyncStatusCallback& callback, 174 google_apis::GDataErrorCode error, 175 const std::string& resource_id) { 176 switch (error) { 177 case google_apis::HTTP_SUCCESS: 178 case google_apis::HTTP_CREATED: { 179 UpdateMetadata( 180 resource_id, std::string(), DriveMetadata::RESOURCE_TYPE_FOLDER, 181 base::Bind(&LocalSyncDelegate::DidApplyLocalChange, 182 weak_factory_.GetWeakPtr(), callback, error)); 183 sync_service_->NotifyObserversFileStatusChanged( 184 url_, 185 SYNC_FILE_STATUS_SYNCED, 186 SYNC_ACTION_ADDED, 187 SYNC_DIRECTION_LOCAL_TO_REMOTE); 188 return; 189 } 190 191 case google_apis::HTTP_CONFLICT: 192 // There were conflicts and a file was left. 193 // TODO(kinuko): Handle the latter case (http://crbug.com/237090). 194 // Fall-through 195 196 default: 197 callback.Run(GDataErrorCodeToSyncStatusCodeWrapper(error)); 198 } 199} 200 201void LocalSyncDelegate::UploadExistingFile(const SyncStatusCallback& callback) { 202 DCHECK(has_drive_metadata_); 203 if (drive_metadata_.resource_id().empty()) { 204 UploadNewFile(callback); 205 return; 206 } 207 208 api_util()->UploadExistingFile( 209 drive_metadata_.resource_id(), 210 drive_metadata_.md5_checksum(), 211 local_path_, 212 base::Bind(&LocalSyncDelegate::DidUploadExistingFile, 213 weak_factory_.GetWeakPtr(), callback)); 214} 215 216void LocalSyncDelegate::DidUploadExistingFile( 217 const SyncStatusCallback& callback, 218 google_apis::GDataErrorCode error, 219 const std::string& resource_id, 220 const std::string& md5) { 221 DCHECK(has_drive_metadata_); 222 switch (error) { 223 case google_apis::HTTP_SUCCESS: 224 UpdateMetadata( 225 resource_id, md5, DriveMetadata::RESOURCE_TYPE_FILE, 226 base::Bind(&LocalSyncDelegate::DidApplyLocalChange, 227 weak_factory_.GetWeakPtr(), callback, error)); 228 sync_service_->NotifyObserversFileStatusChanged( 229 url_, 230 SYNC_FILE_STATUS_SYNCED, 231 SYNC_ACTION_UPDATED, 232 SYNC_DIRECTION_LOCAL_TO_REMOTE); 233 return; 234 case google_apis::HTTP_CONFLICT: 235 HandleConflict(callback); 236 return; 237 case google_apis::HTTP_NOT_MODIFIED: 238 DidApplyLocalChange(callback, 239 google_apis::HTTP_SUCCESS, SYNC_STATUS_OK); 240 return; 241 case google_apis::HTTP_NOT_FOUND: 242 UploadNewFile(callback); 243 return; 244 default: { 245 const SyncStatusCode status = 246 GDataErrorCodeToSyncStatusCodeWrapper(error); 247 DCHECK_NE(SYNC_STATUS_OK, status); 248 callback.Run(status); 249 return; 250 } 251 } 252} 253 254void LocalSyncDelegate::Delete(const SyncStatusCallback& callback) { 255 if (!has_drive_metadata_) { 256 callback.Run(SYNC_STATUS_OK); 257 return; 258 } 259 260 if (drive_metadata_.resource_id().empty()) { 261 DidDelete(callback, google_apis::HTTP_NOT_FOUND); 262 return; 263 } 264 265 api_util()->DeleteFile( 266 drive_metadata_.resource_id(), 267 drive_metadata_.md5_checksum(), 268 base::Bind(&LocalSyncDelegate::DidDelete, 269 weak_factory_.GetWeakPtr(), callback)); 270} 271 272void LocalSyncDelegate::DidDelete( 273 const SyncStatusCallback& callback, 274 google_apis::GDataErrorCode error) { 275 DCHECK(has_drive_metadata_); 276 277 switch (error) { 278 case google_apis::HTTP_SUCCESS: 279 case google_apis::HTTP_NOT_FOUND: 280 DeleteMetadata(base::Bind( 281 &LocalSyncDelegate::DidApplyLocalChange, 282 weak_factory_.GetWeakPtr(), callback, google_apis::HTTP_SUCCESS)); 283 sync_service_->NotifyObserversFileStatusChanged( 284 url_, 285 SYNC_FILE_STATUS_SYNCED, 286 SYNC_ACTION_DELETED, 287 SYNC_DIRECTION_LOCAL_TO_REMOTE); 288 return; 289 case google_apis::HTTP_PRECONDITION: 290 case google_apis::HTTP_CONFLICT: 291 // Delete |drive_metadata| on the conflict case. 292 // Conflicted remote change should be applied as a future remote change. 293 DeleteMetadata(base::Bind( 294 &LocalSyncDelegate::DidDeleteMetadataForDeletionConflict, 295 weak_factory_.GetWeakPtr(), callback)); 296 sync_service_->NotifyObserversFileStatusChanged( 297 url_, 298 SYNC_FILE_STATUS_SYNCED, 299 SYNC_ACTION_DELETED, 300 SYNC_DIRECTION_LOCAL_TO_REMOTE); 301 return; 302 default: { 303 const SyncStatusCode status = 304 GDataErrorCodeToSyncStatusCodeWrapper(error); 305 DCHECK_NE(SYNC_STATUS_OK, status); 306 callback.Run(status); 307 return; 308 } 309 } 310} 311 312void LocalSyncDelegate::DidDeleteMetadataForDeletionConflict( 313 const SyncStatusCallback& callback, 314 SyncStatusCode status) { 315 callback.Run(SYNC_STATUS_OK); 316} 317 318void LocalSyncDelegate::ResolveToLocal(const SyncStatusCallback& callback) { 319 if (drive_metadata_.resource_id().empty()) { 320 DidDeleteFileToResolveToLocal(callback, google_apis::HTTP_NOT_FOUND); 321 return; 322 } 323 324 api_util()->DeleteFile( 325 drive_metadata_.resource_id(), 326 drive_metadata_.md5_checksum(), 327 base::Bind( 328 &LocalSyncDelegate::DidDeleteFileToResolveToLocal, 329 weak_factory_.GetWeakPtr(), callback)); 330} 331 332void LocalSyncDelegate::DidDeleteFileToResolveToLocal( 333 const SyncStatusCallback& callback, 334 google_apis::GDataErrorCode error) { 335 if (error != google_apis::HTTP_SUCCESS && 336 error != google_apis::HTTP_NOT_FOUND) { 337 callback.Run(GDataErrorCodeToSyncStatusCodeWrapper(error)); 338 return; 339 } 340 341 DCHECK_NE(SYNC_FILE_TYPE_UNKNOWN, local_metadata_.file_type); 342 if (local_metadata_.file_type == SYNC_FILE_TYPE_FILE) { 343 UploadNewFile(callback); 344 return; 345 } 346 347 DCHECK(IsSyncFSDirectoryOperationEnabled()); 348 DCHECK_EQ(SYNC_FILE_TYPE_DIRECTORY, local_metadata_.file_type); 349 CreateDirectory(callback); 350} 351 352void LocalSyncDelegate::ResolveToRemote( 353 const SyncStatusCallback& callback, 354 SyncFileType remote_file_type) { 355 // Mark the file as to-be-fetched. 356 DCHECK(!drive_metadata_.resource_id().empty()); 357 358 SetMetadataToBeFetched( 359 DriveFileSyncService::SyncFileTypeToDriveMetadataResourceType( 360 remote_file_type), 361 base::Bind(&LocalSyncDelegate::DidResolveToRemote, 362 weak_factory_.GetWeakPtr(), callback)); 363 // The synced notification will be dispatched when the remote file is 364 // downloaded. 365} 366 367void LocalSyncDelegate::DidResolveToRemote( 368 const SyncStatusCallback& callback, 369 SyncStatusCode status) { 370 DCHECK(has_drive_metadata_); 371 if (status != SYNC_STATUS_OK) { 372 callback.Run(status); 373 return; 374 } 375 376 SyncFileType file_type = SYNC_FILE_TYPE_FILE; 377 if (drive_metadata_.type() == DriveMetadata::RESOURCE_TYPE_FOLDER) 378 file_type = SYNC_FILE_TYPE_DIRECTORY; 379 sync_service_->AppendFetchChange( 380 url_.origin(), url_.path(), drive_metadata_.resource_id(), file_type); 381 callback.Run(status); 382} 383 384void LocalSyncDelegate::DidApplyLocalChange( 385 const SyncStatusCallback& callback, 386 const google_apis::GDataErrorCode error, 387 SyncStatusCode status) { 388 if ((operation_ == SYNC_OPERATION_DELETE || 389 operation_ == SYNC_OPERATION_DELETE_METADATA) && 390 (status == SYNC_FILE_ERROR_NOT_FOUND || 391 status == SYNC_DATABASE_ERROR_NOT_FOUND)) { 392 status = SYNC_STATUS_OK; 393 } 394 395 if (status == SYNC_STATUS_OK) { 396 remote_change_handler()->RemoveChangeForURL(url_); 397 status = GDataErrorCodeToSyncStatusCodeWrapper(error); 398 } 399 callback.Run(status); 400} 401 402void LocalSyncDelegate::UpdateMetadata( 403 const std::string& resource_id, 404 const std::string& md5, 405 DriveMetadata::ResourceType type, 406 const SyncStatusCallback& callback) { 407 has_drive_metadata_ = true; 408 drive_metadata_.set_resource_id(resource_id); 409 drive_metadata_.set_md5_checksum(md5); 410 drive_metadata_.set_conflicted(false); 411 drive_metadata_.set_to_be_fetched(false); 412 drive_metadata_.set_type(type); 413 metadata_store()->UpdateEntry(url_, drive_metadata_, callback); 414} 415 416void LocalSyncDelegate::ResetMetadataForStartOver( 417 const SyncStatusCallback& callback) { 418 has_drive_metadata_ = true; 419 DCHECK(!drive_metadata_.resource_id().empty()); 420 drive_metadata_.set_md5_checksum(std::string()); 421 drive_metadata_.set_conflicted(false); 422 drive_metadata_.set_to_be_fetched(false); 423 metadata_store()->UpdateEntry(url_, drive_metadata_, callback); 424} 425 426void LocalSyncDelegate::SetMetadataToBeFetched( 427 DriveMetadata::ResourceType type, 428 const SyncStatusCallback& callback) { 429 has_drive_metadata_ = true; 430 drive_metadata_.set_md5_checksum(std::string()); 431 drive_metadata_.set_conflicted(false); 432 drive_metadata_.set_to_be_fetched(true); 433 drive_metadata_.set_type(type); 434 metadata_store()->UpdateEntry(url_, drive_metadata_, callback); 435} 436 437void LocalSyncDelegate::DeleteMetadata(const SyncStatusCallback& callback) { 438 metadata_store()->DeleteEntry(url_, callback); 439} 440 441void LocalSyncDelegate::HandleCreationConflict( 442 const std::string& resource_id, 443 DriveMetadata::ResourceType type, 444 const SyncStatusCallback& callback) { 445 // File-file conflict is found. 446 // Populates a fake drive_metadata and set has_drive_metadata = true. 447 // In HandleConflictLocalSync: 448 // - If conflict_resolution is manual, we'll change conflicted to true 449 // and save the metadata. 450 // - Otherwise we'll save the metadata with empty md5 and will start 451 // over local sync as UploadExistingFile. 452 drive_metadata_.set_resource_id(resource_id); 453 drive_metadata_.set_md5_checksum(std::string()); 454 drive_metadata_.set_conflicted(false); 455 drive_metadata_.set_to_be_fetched(false); 456 drive_metadata_.set_type(type); 457 has_drive_metadata_ = true; 458 HandleConflict(callback); 459} 460 461void LocalSyncDelegate::HandleConflict(const SyncStatusCallback& callback) { 462 DCHECK(!drive_metadata_.resource_id().empty()); 463 api_util()->GetResourceEntry( 464 drive_metadata_.resource_id(), 465 base::Bind( 466 &LocalSyncDelegate::DidGetEntryForConflictResolution, 467 weak_factory_.GetWeakPtr(), callback)); 468} 469 470void LocalSyncDelegate::DidGetEntryForConflictResolution( 471 const SyncStatusCallback& callback, 472 google_apis::GDataErrorCode error, 473 scoped_ptr<google_apis::ResourceEntry> entry) { 474 SyncFileType remote_file_type = SYNC_FILE_TYPE_UNKNOWN; 475 ConflictResolution resolution = CONFLICT_RESOLUTION_UNKNOWN; 476 477 if (error != google_apis::HTTP_SUCCESS || 478 entry->updated_time().is_null()) { 479 resolution = CONFLICT_RESOLUTION_LOCAL_WIN; 480 } else { 481 SyncFileType local_file_type = local_metadata_.file_type; 482 base::Time local_modification_time = local_metadata_.last_modified; 483 base::Time remote_modification_time = entry->updated_time(); 484 if (entry->is_file()) 485 remote_file_type = SYNC_FILE_TYPE_FILE; 486 else if (entry->is_folder()) 487 remote_file_type = SYNC_FILE_TYPE_DIRECTORY; 488 else 489 remote_file_type = SYNC_FILE_TYPE_UNKNOWN; 490 491 resolution = conflict_resolution_resolver()->Resolve( 492 local_file_type, local_modification_time, 493 remote_file_type, remote_modification_time); 494 } 495 496 switch (resolution) { 497 case CONFLICT_RESOLUTION_MARK_CONFLICT: 498 HandleManualResolutionCase(callback); 499 return; 500 case CONFLICT_RESOLUTION_LOCAL_WIN: 501 HandleLocalWinCase(callback); 502 return; 503 case CONFLICT_RESOLUTION_REMOTE_WIN: 504 HandleRemoteWinCase(callback, remote_file_type); 505 return; 506 case CONFLICT_RESOLUTION_UNKNOWN: 507 NOTREACHED(); 508 } 509 NOTREACHED(); 510 callback.Run(SYNC_STATUS_FAILED); 511} 512 513void LocalSyncDelegate::HandleManualResolutionCase( 514 const SyncStatusCallback& callback) { 515 if (drive_metadata_.conflicted()) { 516 callback.Run(SYNC_STATUS_HAS_CONFLICT); 517 return; 518 } 519 520 has_drive_metadata_ = true; 521 sync_service_->MarkConflict( 522 url_, &drive_metadata_, 523 base::Bind(&LocalSyncDelegate::DidMarkConflict, 524 weak_factory_.GetWeakPtr(), callback)); 525} 526 527void LocalSyncDelegate::DidMarkConflict( 528 const SyncStatusCallback& callback, 529 SyncStatusCode status) { 530 DidApplyLocalChange(callback, google_apis::HTTP_CONFLICT, status); 531} 532 533void LocalSyncDelegate::HandleLocalWinCase( 534 const SyncStatusCallback& callback) { 535 util::Log(logging::LOG_VERBOSE, FROM_HERE, 536 "Resolving conflict for local sync: %s: LOCAL WIN", 537 url_.DebugString().c_str()); 538 539 DCHECK(!drive_metadata_.resource_id().empty()); 540 if (!has_drive_metadata_) { 541 StartOver(callback, SYNC_STATUS_OK); 542 return; 543 } 544 545 ResetMetadataForStartOver(base::Bind(&LocalSyncDelegate::StartOver, 546 weak_factory_.GetWeakPtr(), callback)); 547} 548 549void LocalSyncDelegate::HandleRemoteWinCase( 550 const SyncStatusCallback& callback, 551 SyncFileType remote_file_type) { 552 util::Log(logging::LOG_VERBOSE, FROM_HERE, 553 "Resolving conflict for local sync: %s: REMOTE WIN", 554 url_.DebugString().c_str()); 555 ResolveToRemote(callback, remote_file_type); 556} 557 558void LocalSyncDelegate::StartOver(const SyncStatusCallback& callback, 559 SyncStatusCode status) { 560 if (status != SYNC_STATUS_OK) { 561 callback.Run(status); 562 return; 563 } 564 565 remote_change_handler()->RemoveChangeForURL(url_); 566 567 // Return the control back to the sync service once. 568 callback.Run(SYNC_STATUS_RETRY); 569} 570 571SyncStatusCode 572LocalSyncDelegate::GDataErrorCodeToSyncStatusCodeWrapper( 573 google_apis::GDataErrorCode error) { 574 return sync_service_->GDataErrorCodeToSyncStatusCodeWrapper(error); 575} 576 577DriveMetadataStore* LocalSyncDelegate::metadata_store() { 578 return sync_service_->metadata_store_.get(); 579} 580 581APIUtilInterface* LocalSyncDelegate::api_util() { 582 return sync_service_->api_util_.get(); 583} 584 585RemoteChangeHandler* LocalSyncDelegate::remote_change_handler() { 586 return &sync_service_->remote_change_handler_; 587} 588 589ConflictResolutionResolver* LocalSyncDelegate::conflict_resolution_resolver() { 590 return &sync_service_->conflict_resolution_resolver_; 591} 592 593} // namespace drive_backend 594} // namespace sync_file_system 595