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