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/remote_sync_delegate.h" 6 7 #include "base/file_util.h" 8 #include "chrome/browser/sync_file_system/drive_backend_v1/remote_sync_operation_resolver.h" 9 #include "chrome/browser/sync_file_system/logger.h" 10 #include "chrome/browser/sync_file_system/remote_change_processor.h" 11 #include "chrome/browser/sync_file_system/syncable_file_system_util.h" 12 13 using fileapi::FileSystemURL; 14 15 namespace { 16 17 void EmptyStatusCallback(sync_file_system::SyncStatusCode status) {} 18 19 } // namespace 20 21 namespace sync_file_system { 22 namespace drive_backend { 23 24 RemoteSyncDelegate::RemoteSyncDelegate( 25 DriveFileSyncService* sync_service, 26 const RemoteChange& remote_change) 27 : sync_service_(sync_service), 28 remote_change_(remote_change), 29 sync_action_(SYNC_ACTION_NONE), 30 metadata_updated_(false), 31 clear_local_changes_(true) { 32 } 33 34 RemoteSyncDelegate::~RemoteSyncDelegate() {} 35 36 void RemoteSyncDelegate::Run(const SyncStatusCallback& callback) { 37 util::Log(logging::LOG_VERBOSE, FROM_HERE, 38 "ProcessRemoteChange for %s change:%s", 39 url().DebugString().c_str(), 40 remote_file_change().DebugString().c_str()); 41 42 remote_change_processor()->PrepareForProcessRemoteChange( 43 url(), 44 base::Bind(&RemoteSyncDelegate::DidPrepareForProcessRemoteChange, 45 AsWeakPtr(), callback)); 46 } 47 48 void RemoteSyncDelegate::DidPrepareForProcessRemoteChange( 49 const SyncStatusCallback& callback, 50 SyncStatusCode status, 51 const SyncFileMetadata& metadata, 52 const FileChangeList& local_changes) { 53 if (status != SYNC_STATUS_OK) { 54 AbortSync(callback, status); 55 return; 56 } 57 58 local_metadata_ = metadata; 59 status = metadata_store()->ReadEntry(url(), &drive_metadata_); 60 DCHECK(status == SYNC_STATUS_OK || status == SYNC_DATABASE_ERROR_NOT_FOUND); 61 62 bool missing_db_entry = (status != SYNC_STATUS_OK); 63 if (missing_db_entry) { 64 drive_metadata_.set_resource_id(remote_change_.resource_id); 65 drive_metadata_.set_md5_checksum(std::string()); 66 drive_metadata_.set_conflicted(false); 67 drive_metadata_.set_to_be_fetched(false); 68 } 69 bool missing_local_file = (metadata.file_type == SYNC_FILE_TYPE_UNKNOWN); 70 71 if (drive_metadata_.resource_id().empty()) { 72 // This (missing_db_entry is false but resource_id is empty) could 73 // happen when the remote file gets deleted (this clears resource_id 74 // in drive_metadata) but then a file is added with the same name. 75 drive_metadata_.set_resource_id(remote_change_.resource_id); 76 } 77 78 SyncOperationType operation = 79 RemoteSyncOperationResolver::Resolve(remote_file_change(), 80 local_changes, 81 local_metadata_.file_type, 82 drive_metadata_.conflicted()); 83 84 util::Log(logging::LOG_VERBOSE, FROM_HERE, 85 "ProcessRemoteChange for %s %s%sremote_change: %s ==> %s", 86 url().DebugString().c_str(), 87 drive_metadata_.conflicted() ? " (conflicted)" : " ", 88 missing_local_file ? " (missing local file)" : " ", 89 remote_file_change().DebugString().c_str(), 90 SyncOperationTypeToString(operation)); 91 DCHECK_NE(SYNC_OPERATION_FAIL, operation); 92 93 switch (operation) { 94 case SYNC_OPERATION_ADD_FILE: 95 case SYNC_OPERATION_ADD_DIRECTORY: 96 sync_action_ = SYNC_ACTION_ADDED; 97 break; 98 case SYNC_OPERATION_UPDATE_FILE: 99 sync_action_ = SYNC_ACTION_UPDATED; 100 break; 101 case SYNC_OPERATION_DELETE: 102 sync_action_ = SYNC_ACTION_DELETED; 103 break; 104 case SYNC_OPERATION_NONE: 105 case SYNC_OPERATION_DELETE_METADATA: 106 sync_action_ = SYNC_ACTION_NONE; 107 break; 108 default: 109 break; 110 } 111 112 switch (operation) { 113 case SYNC_OPERATION_ADD_FILE: 114 case SYNC_OPERATION_UPDATE_FILE: 115 DownloadFile(callback); 116 return; 117 case SYNC_OPERATION_ADD_DIRECTORY: 118 case SYNC_OPERATION_DELETE: 119 ApplyRemoteChange(callback); 120 return; 121 case SYNC_OPERATION_NONE: 122 CompleteSync(callback, SYNC_STATUS_OK); 123 return; 124 case SYNC_OPERATION_CONFLICT: 125 HandleConflict(callback, remote_file_change().file_type()); 126 return; 127 case SYNC_OPERATION_RESOLVE_TO_LOCAL: 128 ResolveToLocal(callback); 129 return; 130 case SYNC_OPERATION_RESOLVE_TO_REMOTE: 131 ResolveToRemote(callback); 132 return; 133 case SYNC_OPERATION_DELETE_METADATA: 134 if (missing_db_entry) 135 CompleteSync(callback, SYNC_STATUS_OK); 136 else 137 DeleteMetadata(callback); 138 return; 139 case SYNC_OPERATION_FAIL: 140 AbortSync(callback, SYNC_STATUS_FAILED); 141 return; 142 } 143 NOTREACHED(); 144 AbortSync(callback, SYNC_STATUS_FAILED); 145 } 146 147 void RemoteSyncDelegate::ApplyRemoteChange(const SyncStatusCallback& callback) { 148 remote_change_processor()->ApplyRemoteChange( 149 remote_file_change(), temporary_file_.path(), url(), 150 base::Bind(&RemoteSyncDelegate::DidApplyRemoteChange, AsWeakPtr(), 151 callback)); 152 } 153 154 void RemoteSyncDelegate::DidApplyRemoteChange( 155 const SyncStatusCallback& callback, 156 SyncStatusCode status) { 157 if (status != SYNC_STATUS_OK) { 158 AbortSync(callback, status); 159 return; 160 } 161 162 if (remote_file_change().IsDelete()) { 163 DeleteMetadata(callback); 164 return; 165 } 166 167 drive_metadata_.set_resource_id(remote_change_.resource_id); 168 drive_metadata_.set_conflicted(false); 169 if (remote_file_change().IsFile()) { 170 drive_metadata_.set_type(DriveMetadata::RESOURCE_TYPE_FILE); 171 } else { 172 DCHECK(IsSyncFSDirectoryOperationEnabled()); 173 drive_metadata_.set_type(DriveMetadata::RESOURCE_TYPE_FOLDER); 174 } 175 176 metadata_store()->UpdateEntry( 177 url(), drive_metadata_, 178 base::Bind(&RemoteSyncDelegate::CompleteSync, 179 AsWeakPtr(), callback)); 180 } 181 182 void RemoteSyncDelegate::DeleteMetadata(const SyncStatusCallback& callback) { 183 metadata_store()->DeleteEntry( 184 url(), 185 base::Bind(&RemoteSyncDelegate::CompleteSync, AsWeakPtr(), callback)); 186 } 187 188 void RemoteSyncDelegate::DownloadFile(const SyncStatusCallback& callback) { 189 // We should not use the md5 in metadata for FETCH type to avoid the download 190 // finishes due to NOT_MODIFIED. 191 std::string md5_checksum; 192 if (!drive_metadata_.to_be_fetched()) 193 md5_checksum = drive_metadata_.md5_checksum(); 194 195 api_util()->DownloadFile( 196 remote_change_.resource_id, 197 md5_checksum, 198 base::Bind(&RemoteSyncDelegate::DidDownloadFile, 199 AsWeakPtr(), 200 callback)); 201 } 202 203 void RemoteSyncDelegate::DidDownloadFile( 204 const SyncStatusCallback& callback, 205 google_apis::GDataErrorCode error, 206 const std::string& md5_checksum, 207 int64 file_size, 208 const base::Time& updated_time, 209 webkit_blob::ScopedFile downloaded_file) { 210 if (error == google_apis::HTTP_NOT_MODIFIED) { 211 sync_action_ = SYNC_ACTION_NONE; 212 DidApplyRemoteChange(callback, SYNC_STATUS_OK); 213 return; 214 } 215 216 // File may be deleted. If this was for new file it's ok, if this was 217 // for existing file we'll process the delete change later. 218 if (error == google_apis::HTTP_NOT_FOUND) { 219 sync_action_ = SYNC_ACTION_NONE; 220 DidApplyRemoteChange(callback, SYNC_STATUS_OK); 221 return; 222 } 223 224 SyncStatusCode status = GDataErrorCodeToSyncStatusCodeWrapper(error); 225 if (status != SYNC_STATUS_OK) { 226 AbortSync(callback, status); 227 return; 228 } 229 230 temporary_file_ = downloaded_file.Pass(); 231 drive_metadata_.set_md5_checksum(md5_checksum); 232 remote_change_processor()->ApplyRemoteChange( 233 remote_file_change(), temporary_file_.path(), url(), 234 base::Bind(&RemoteSyncDelegate::DidApplyRemoteChange, 235 AsWeakPtr(), callback)); 236 } 237 238 void RemoteSyncDelegate::HandleConflict( 239 const SyncStatusCallback& callback, 240 SyncFileType remote_file_type) { 241 ConflictResolution resolution = conflict_resolution_resolver()->Resolve( 242 local_metadata_.file_type, 243 local_metadata_.last_modified, 244 remote_file_type, 245 remote_change_.updated_time); 246 247 switch (resolution) { 248 case CONFLICT_RESOLUTION_LOCAL_WIN: 249 HandleLocalWin(callback); 250 return; 251 case CONFLICT_RESOLUTION_REMOTE_WIN: 252 HandleRemoteWin(callback, remote_file_type); 253 return; 254 case CONFLICT_RESOLUTION_MARK_CONFLICT: 255 HandleManualResolutionCase(callback); 256 return; 257 case CONFLICT_RESOLUTION_UNKNOWN: 258 // Get remote file time and call this method again. 259 api_util()->GetResourceEntry( 260 remote_change_.resource_id, 261 base::Bind( 262 &RemoteSyncDelegate::DidGetEntryForConflictResolution, 263 AsWeakPtr(), callback)); 264 return; 265 } 266 NOTREACHED(); 267 AbortSync(callback, SYNC_STATUS_FAILED); 268 } 269 270 void RemoteSyncDelegate::HandleLocalWin( 271 const SyncStatusCallback& callback) { 272 util::Log(logging::LOG_VERBOSE, FROM_HERE, 273 "Resolving conflict for remote sync: %s: LOCAL WIN", 274 url().DebugString().c_str()); 275 ResolveToLocal(callback); 276 } 277 278 void RemoteSyncDelegate::HandleRemoteWin( 279 const SyncStatusCallback& callback, 280 SyncFileType remote_file_type) { 281 // Make sure we reset the conflict flag and start over the remote sync 282 // with empty local changes. 283 util::Log(logging::LOG_VERBOSE, FROM_HERE, 284 "Resolving conflict for remote sync: %s: REMOTE WIN", 285 url().DebugString().c_str()); 286 287 drive_metadata_.set_conflicted(false); 288 drive_metadata_.set_to_be_fetched(false); 289 drive_metadata_.set_type( 290 DriveFileSyncService::SyncFileTypeToDriveMetadataResourceType( 291 remote_file_type)); 292 metadata_store()->UpdateEntry( 293 url(), drive_metadata_, 294 base::Bind(&RemoteSyncDelegate::StartOver, AsWeakPtr(), callback)); 295 } 296 297 void RemoteSyncDelegate::HandleManualResolutionCase( 298 const SyncStatusCallback& callback) { 299 sync_action_ = SYNC_ACTION_NONE; 300 sync_service_->MarkConflict( 301 url(), &drive_metadata_, 302 base::Bind(&RemoteSyncDelegate::CompleteSync, AsWeakPtr(), callback)); 303 } 304 305 void RemoteSyncDelegate::DidGetEntryForConflictResolution( 306 const SyncStatusCallback& callback, 307 google_apis::GDataErrorCode error, 308 scoped_ptr<google_apis::ResourceEntry> entry) { 309 SyncStatusCode status = GDataErrorCodeToSyncStatusCodeWrapper(error); 310 if (status != SYNC_STATUS_OK || entry->updated_time().is_null()) { 311 HandleLocalWin(callback); 312 return; 313 } 314 315 SyncFileType file_type = SYNC_FILE_TYPE_UNKNOWN; 316 if (entry->is_file()) 317 file_type = SYNC_FILE_TYPE_FILE; 318 if (entry->is_folder()) 319 file_type = SYNC_FILE_TYPE_DIRECTORY; 320 321 remote_change_.updated_time = entry->updated_time(); 322 HandleConflict(callback, file_type); 323 } 324 325 void RemoteSyncDelegate::ResolveToLocal( 326 const SyncStatusCallback& callback) { 327 sync_action_ = SYNC_ACTION_NONE; 328 clear_local_changes_ = false; 329 330 // Re-add a fake local change to resolve it later in next LocalSync. 331 remote_change_processor()->RecordFakeLocalChange( 332 url(), 333 FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE, 334 local_metadata_.file_type), 335 base::Bind(&RemoteSyncDelegate::DidResolveToLocal, 336 AsWeakPtr(), callback)); 337 } 338 339 void RemoteSyncDelegate::DidResolveToLocal( 340 const SyncStatusCallback& callback, 341 SyncStatusCode status) { 342 if (status != SYNC_STATUS_OK) { 343 DCHECK_NE(SYNC_STATUS_HAS_CONFLICT, status); 344 AbortSync(callback, status); 345 return; 346 } 347 348 if (remote_file_change().IsDelete()) { 349 metadata_store()->DeleteEntry( 350 url(), 351 base::Bind(&RemoteSyncDelegate::CompleteSync, 352 AsWeakPtr(), callback)); 353 } else { 354 DCHECK(!remote_change_.resource_id.empty()); 355 drive_metadata_.set_resource_id(remote_change_.resource_id); 356 drive_metadata_.set_conflicted(false); 357 drive_metadata_.set_to_be_fetched(false); 358 drive_metadata_.set_md5_checksum(std::string()); 359 metadata_store()->UpdateEntry( 360 url(), drive_metadata_, 361 base::Bind(&RemoteSyncDelegate::CompleteSync, 362 AsWeakPtr(), callback)); 363 } 364 } 365 366 void RemoteSyncDelegate::ResolveToRemote( 367 const SyncStatusCallback& callback) { 368 drive_metadata_.set_conflicted(false); 369 drive_metadata_.set_to_be_fetched(true); 370 metadata_store()->UpdateEntry( 371 url(), drive_metadata_, 372 base::Bind(&RemoteSyncDelegate::DidResolveToRemote, 373 AsWeakPtr(), callback)); 374 } 375 376 void RemoteSyncDelegate::DidResolveToRemote( 377 const SyncStatusCallback& callback, 378 SyncStatusCode status) { 379 if (status != SYNC_STATUS_OK) { 380 AbortSync(callback, status); 381 return; 382 } 383 384 sync_action_ = SYNC_ACTION_ADDED; 385 if (remote_file_change().file_type() == SYNC_FILE_TYPE_FILE) { 386 DownloadFile(callback); 387 return; 388 } 389 390 // ApplyRemoteChange should replace any existing local file or 391 // directory with remote_change_. 392 ApplyRemoteChange(callback); 393 } 394 395 void RemoteSyncDelegate::StartOver( 396 const SyncStatusCallback& callback, 397 SyncStatusCode status) { 398 DidPrepareForProcessRemoteChange( 399 callback, status, local_metadata_, FileChangeList()); 400 } 401 402 void RemoteSyncDelegate::CompleteSync( 403 const SyncStatusCallback& callback, 404 SyncStatusCode status) { 405 if (status != SYNC_STATUS_OK) { 406 AbortSync(callback, status); 407 return; 408 } 409 410 sync_service_->RemoveRemoteChange(url()); 411 412 if (drive_metadata_.to_be_fetched()) { 413 // Clear |to_be_fetched| flag since we completed fetching the remote change 414 // and applying it to the local file. 415 DCHECK(!drive_metadata_.conflicted()); 416 drive_metadata_.set_conflicted(false); 417 drive_metadata_.set_to_be_fetched(false); 418 metadata_store()->UpdateEntry(url(), drive_metadata_, 419 base::Bind(&EmptyStatusCallback)); 420 } 421 422 if (remote_change_.changestamp > 0) { 423 DCHECK(metadata_store()->IsIncrementalSyncOrigin(url().origin())); 424 metadata_store()->SetLargestChangeStamp( 425 remote_change_.changestamp, 426 base::Bind(&RemoteSyncDelegate::DidFinish, AsWeakPtr(), callback)); 427 return; 428 } 429 430 if (drive_metadata_.conflicted()) 431 status = SYNC_STATUS_HAS_CONFLICT; 432 433 DidFinish(callback, status); 434 } 435 436 void RemoteSyncDelegate::AbortSync( 437 const SyncStatusCallback& callback, 438 SyncStatusCode status) { 439 clear_local_changes_ = false; 440 DidFinish(callback, status); 441 } 442 443 void RemoteSyncDelegate::DidFinish( 444 const SyncStatusCallback& callback, 445 SyncStatusCode status) { 446 remote_change_processor()->FinalizeRemoteSync( 447 url(), clear_local_changes_, 448 base::Bind(&RemoteSyncDelegate::DispatchCallbackAfterDidFinish, 449 AsWeakPtr(), callback, status)); 450 } 451 452 void RemoteSyncDelegate::DispatchCallbackAfterDidFinish( 453 const SyncStatusCallback& callback, 454 SyncStatusCode status) { 455 if (status == SYNC_STATUS_OK && sync_action_ != SYNC_ACTION_NONE) { 456 sync_service_->NotifyObserversFileStatusChanged( 457 url(), 458 SYNC_FILE_STATUS_SYNCED, 459 sync_action_, 460 SYNC_DIRECTION_REMOTE_TO_LOCAL); 461 } 462 463 callback.Run(status); 464 } 465 466 SyncStatusCode RemoteSyncDelegate::GDataErrorCodeToSyncStatusCodeWrapper( 467 google_apis::GDataErrorCode error) { 468 return sync_service_->GDataErrorCodeToSyncStatusCodeWrapper(error); 469 } 470 471 DriveMetadataStore* RemoteSyncDelegate::metadata_store() { 472 return sync_service_->metadata_store_.get(); 473 } 474 475 APIUtilInterface* RemoteSyncDelegate::api_util() { 476 return sync_service_->api_util_.get(); 477 } 478 479 RemoteChangeHandler* RemoteSyncDelegate::remote_change_handler() { 480 return &sync_service_->remote_change_handler_; 481 } 482 483 RemoteChangeProcessor* RemoteSyncDelegate::remote_change_processor() { 484 return sync_service_->remote_change_processor_; 485 } 486 487 ConflictResolutionResolver* RemoteSyncDelegate::conflict_resolution_resolver() { 488 return &sync_service_->conflict_resolution_resolver_; 489 } 490 491 } // namespace drive_backend 492 } // namespace sync_file_system 493