| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/sync_file_system/drive_backend/conflict_resolver.h" | 5 #include "chrome/browser/sync_file_system/drive_backend/conflict_resolver.h" |
| 6 | 6 |
| 7 #include "base/callback.h" | 7 #include "base/callback.h" |
| 8 #include "base/format_macros.h" | 8 #include "base/format_macros.h" |
| 9 #include "base/location.h" | 9 #include "base/location.h" |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| 11 #include "base/strings/stringprintf.h" |
| 11 #include "chrome/browser/drive/drive_api_util.h" | 12 #include "chrome/browser/drive/drive_api_util.h" |
| 12 #include "chrome/browser/drive/drive_service_interface.h" | 13 #include "chrome/browser/drive/drive_service_interface.h" |
| 13 #include "chrome/browser/drive/drive_uploader.h" | 14 #include "chrome/browser/drive/drive_uploader.h" |
| 14 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_util.h" | 15 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_util.h" |
| 15 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.h" | 16 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.h" |
| 16 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.pb.h" | 17 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.pb.h" |
| 17 #include "chrome/browser/sync_file_system/drive_backend/sync_engine_context.h" | 18 #include "chrome/browser/sync_file_system/drive_backend/sync_engine_context.h" |
| 19 #include "chrome/browser/sync_file_system/drive_backend/sync_task_manager.h" |
| 20 #include "chrome/browser/sync_file_system/drive_backend/sync_task_token.h" |
| 18 #include "chrome/browser/sync_file_system/logger.h" | 21 #include "chrome/browser/sync_file_system/logger.h" |
| 19 #include "google_apis/drive/drive_api_parser.h" | 22 #include "google_apis/drive/drive_api_parser.h" |
| 20 | 23 |
| 21 namespace sync_file_system { | 24 namespace sync_file_system { |
| 22 namespace drive_backend { | 25 namespace drive_backend { |
| 23 | 26 |
| 24 ConflictResolver::ConflictResolver(SyncEngineContext* sync_context) | 27 ConflictResolver::ConflictResolver(SyncEngineContext* sync_context) |
| 25 : sync_context_(sync_context), | 28 : sync_context_(sync_context), |
| 26 weak_ptr_factory_(this) {} | 29 weak_ptr_factory_(this) {} |
| 27 | 30 |
| 28 ConflictResolver::~ConflictResolver() {} | 31 ConflictResolver::~ConflictResolver() {} |
| 29 | 32 |
| 30 void ConflictResolver::RunExclusive(const SyncStatusCallback& callback) { | 33 void ConflictResolver::RunPreflight(scoped_ptr<SyncTaskToken> token) { |
| 34 token->InitializeTaskLog("Conflict Resolution"); |
| 35 |
| 36 scoped_ptr<BlockingFactor> blocking_factor(new BlockingFactor); |
| 37 blocking_factor->exclusive = true; |
| 38 SyncTaskManager::UpdateBlockingFactor( |
| 39 token.Pass(), blocking_factor.Pass(), |
| 40 base::Bind(&ConflictResolver::RunExclusive, |
| 41 weak_ptr_factory_.GetWeakPtr())); |
| 42 } |
| 43 |
| 44 void ConflictResolver::RunExclusive(scoped_ptr<SyncTaskToken> token) { |
| 31 if (!IsContextReady()) { | 45 if (!IsContextReady()) { |
| 32 NOTREACHED(); | 46 NOTREACHED(); |
| 33 callback.Run(SYNC_STATUS_FAILED); | 47 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED); |
| 34 return; | 48 return; |
| 35 } | 49 } |
| 36 | 50 |
| 37 // Conflict resolution should be invoked on clean tree. | 51 // Conflict resolution should be invoked on clean tree. |
| 38 if (metadata_database()->HasDirtyTracker()) { | 52 if (metadata_database()->HasDirtyTracker()) { |
| 39 NOTREACHED(); | 53 NOTREACHED(); |
| 40 callback.Run(SYNC_STATUS_FAILED); | 54 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED); |
| 41 return; | 55 return; |
| 42 } | 56 } |
| 43 | 57 |
| 44 TrackerIDSet trackers; | 58 TrackerIDSet trackers; |
| 45 if (metadata_database()->GetMultiParentFileTrackers( | 59 if (metadata_database()->GetMultiParentFileTrackers( |
| 46 &target_file_id_, &trackers)) { | 60 &target_file_id_, &trackers)) { |
| 47 DCHECK_LT(1u, trackers.size()); | 61 DCHECK_LT(1u, trackers.size()); |
| 48 if (!trackers.has_active()) { | 62 if (!trackers.has_active()) { |
| 49 NOTREACHED(); | 63 NOTREACHED(); |
| 50 callback.Run(SYNC_STATUS_FAILED); | 64 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED); |
| 51 return; | 65 return; |
| 52 } | 66 } |
| 53 | 67 |
| 54 util::Log(logging::LOG_VERBOSE, FROM_HERE, | 68 token->RecordLog(base::StringPrintf( |
| 55 "[ConflictResolver] Detected multi-parent trackers " | 69 "Detected multi-parent trackers (active tracker_id=%" PRId64 ")", |
| 56 "(active tracker_id=%" PRId64 ")", | 70 trackers.active_tracker())); |
| 57 trackers.active_tracker()); | |
| 58 | 71 |
| 59 DCHECK(trackers.has_active()); | 72 DCHECK(trackers.has_active()); |
| 60 for (TrackerIDSet::const_iterator itr = trackers.begin(); | 73 for (TrackerIDSet::const_iterator itr = trackers.begin(); |
| 61 itr != trackers.end(); ++itr) { | 74 itr != trackers.end(); ++itr) { |
| 62 FileTracker tracker; | 75 FileTracker tracker; |
| 63 if (!metadata_database()->FindTrackerByTrackerID(*itr, &tracker)) { | 76 if (!metadata_database()->FindTrackerByTrackerID(*itr, &tracker)) { |
| 64 NOTREACHED(); | 77 NOTREACHED(); |
| 65 continue; | 78 continue; |
| 66 } | 79 } |
| 67 | 80 |
| 68 if (tracker.active()) | 81 if (tracker.active()) |
| 69 continue; | 82 continue; |
| 70 | 83 |
| 71 FileTracker parent_tracker; | 84 FileTracker parent_tracker; |
| 72 bool should_success = metadata_database()->FindTrackerByTrackerID( | 85 bool should_success = metadata_database()->FindTrackerByTrackerID( |
| 73 tracker.parent_tracker_id(), &parent_tracker); | 86 tracker.parent_tracker_id(), &parent_tracker); |
| 74 if (!should_success) { | 87 if (!should_success) { |
| 75 NOTREACHED(); | 88 NOTREACHED(); |
| 76 callback.Run(SYNC_STATUS_FAILED); | 89 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED); |
| 77 return; | 90 return; |
| 78 } | 91 } |
| 79 parents_to_remove_.push_back(parent_tracker.file_id()); | 92 parents_to_remove_.push_back(parent_tracker.file_id()); |
| 80 } | 93 } |
| 81 DetachFromNonPrimaryParents(callback); | 94 DetachFromNonPrimaryParents(token.Pass()); |
| 82 return; | 95 return; |
| 83 } | 96 } |
| 84 | 97 |
| 85 if (metadata_database()->GetConflictingTrackers(&trackers)) { | 98 if (metadata_database()->GetConflictingTrackers(&trackers)) { |
| 86 target_file_id_ = PickPrimaryFile(trackers); | 99 target_file_id_ = PickPrimaryFile(trackers); |
| 87 DCHECK(!target_file_id_.empty()); | 100 DCHECK(!target_file_id_.empty()); |
| 88 int64 primary_tracker_id = -1; | 101 int64 primary_tracker_id = -1; |
| 89 for (TrackerIDSet::const_iterator itr = trackers.begin(); | 102 for (TrackerIDSet::const_iterator itr = trackers.begin(); |
| 90 itr != trackers.end(); ++itr) { | 103 itr != trackers.end(); ++itr) { |
| 91 FileTracker tracker; | 104 FileTracker tracker; |
| 92 if (!metadata_database()->FindTrackerByTrackerID(*itr, &tracker)) { | 105 if (!metadata_database()->FindTrackerByTrackerID(*itr, &tracker)) { |
| 93 NOTREACHED(); | 106 NOTREACHED(); |
| 94 continue; | 107 continue; |
| 95 } | 108 } |
| 96 if (tracker.file_id() != target_file_id_) { | 109 if (tracker.file_id() != target_file_id_) { |
| 97 non_primary_file_ids_.push_back( | 110 non_primary_file_ids_.push_back( |
| 98 std::make_pair(tracker.file_id(), tracker.synced_details().etag())); | 111 std::make_pair(tracker.file_id(), tracker.synced_details().etag())); |
| 99 } else { | 112 } else { |
| 100 primary_tracker_id = tracker.tracker_id(); | 113 primary_tracker_id = tracker.tracker_id(); |
| 101 } | 114 } |
| 102 } | 115 } |
| 103 | 116 |
| 104 util::Log(logging::LOG_VERBOSE, FROM_HERE, | 117 token->RecordLog(base::StringPrintf( |
| 105 "[ConflictResolver] Detected %" PRIuS " conflicting trackers " | 118 "Detected %" PRIuS " conflicting trackers " |
| 106 "(primary tracker_id=%" PRId64 ")", | 119 "(primary tracker_id=%" PRId64 ")", |
| 107 non_primary_file_ids_.size(), primary_tracker_id); | 120 non_primary_file_ids_.size(), primary_tracker_id)); |
| 108 | 121 |
| 109 RemoveNonPrimaryFiles(callback); | 122 RemoveNonPrimaryFiles(token.Pass()); |
| 110 return; | 123 return; |
| 111 } | 124 } |
| 112 | 125 |
| 113 callback.Run(SYNC_STATUS_NO_CONFLICT); | 126 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_NO_CONFLICT); |
| 114 } | 127 } |
| 115 | 128 |
| 116 void ConflictResolver::DetachFromNonPrimaryParents( | 129 void ConflictResolver::DetachFromNonPrimaryParents( |
| 117 const SyncStatusCallback& callback) { | 130 scoped_ptr<SyncTaskToken> token) { |
| 118 DCHECK(!parents_to_remove_.empty()); | 131 DCHECK(!parents_to_remove_.empty()); |
| 119 | 132 |
| 120 // TODO(tzik): Check if ETag match is available for | 133 // TODO(tzik): Check if ETag match is available for |
| 121 // RemoteResourceFromDirectory. | 134 // RemoteResourceFromDirectory. |
| 122 std::string parent_folder_id = parents_to_remove_.back(); | 135 std::string parent_folder_id = parents_to_remove_.back(); |
| 123 parents_to_remove_.pop_back(); | 136 parents_to_remove_.pop_back(); |
| 137 |
| 138 token->RecordLog(base::StringPrintf( |
| 139 "Detach %s from %s", |
| 140 target_file_id_.c_str(), parent_folder_id.c_str())); |
| 141 |
| 124 drive_service()->RemoveResourceFromDirectory( | 142 drive_service()->RemoveResourceFromDirectory( |
| 125 parent_folder_id, target_file_id_, | 143 parent_folder_id, target_file_id_, |
| 126 base::Bind(&ConflictResolver::DidDetachFromParent, | 144 base::Bind(&ConflictResolver::DidDetachFromParent, |
| 127 weak_ptr_factory_.GetWeakPtr(), | 145 weak_ptr_factory_.GetWeakPtr(), |
| 128 callback)); | 146 base::Passed(&token))); |
| 129 util::Log(logging::LOG_VERBOSE, FROM_HERE, | |
| 130 "[ConflictResolver] Detach %s from %s", | |
| 131 target_file_id_.c_str(), parent_folder_id.c_str()); | |
| 132 } | 147 } |
| 133 | 148 |
| 134 void ConflictResolver::DidDetachFromParent(const SyncStatusCallback& callback, | 149 void ConflictResolver::DidDetachFromParent(scoped_ptr<SyncTaskToken> token, |
| 135 google_apis::GDataErrorCode error) { | 150 google_apis::GDataErrorCode error) { |
| 136 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error); | 151 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error); |
| 137 if (status != SYNC_STATUS_OK) { | 152 if (status != SYNC_STATUS_OK) { |
| 138 callback.Run(status); | 153 SyncTaskManager::NotifyTaskDone(token.Pass(), status); |
| 139 return; | 154 return; |
| 140 } | 155 } |
| 141 | 156 |
| 142 if (!parents_to_remove_.empty()) { | 157 if (!parents_to_remove_.empty()) { |
| 143 DetachFromNonPrimaryParents(callback); | 158 DetachFromNonPrimaryParents(token.Pass()); |
| 144 return; | 159 return; |
| 145 } | 160 } |
| 146 | 161 |
| 147 callback.Run(SYNC_STATUS_OK); | 162 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_OK); |
| 148 } | 163 } |
| 149 | 164 |
| 150 std::string ConflictResolver::PickPrimaryFile(const TrackerIDSet& trackers) { | 165 std::string ConflictResolver::PickPrimaryFile(const TrackerIDSet& trackers) { |
| 151 scoped_ptr<FileMetadata> primary; | 166 scoped_ptr<FileMetadata> primary; |
| 152 for (TrackerIDSet::const_iterator itr = trackers.begin(); | 167 for (TrackerIDSet::const_iterator itr = trackers.begin(); |
| 153 itr != trackers.end(); ++itr) { | 168 itr != trackers.end(); ++itr) { |
| 154 FileTracker tracker; | 169 FileTracker tracker; |
| 155 if (!metadata_database()->FindTrackerByTrackerID(*itr, &tracker)) { | 170 if (!metadata_database()->FindTrackerByTrackerID(*itr, &tracker)) { |
| 156 NOTREACHED(); | 171 NOTREACHED(); |
| 157 continue; | 172 continue; |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 205 primary = file_metadata.Pass(); | 220 primary = file_metadata.Pass(); |
| 206 continue; | 221 continue; |
| 207 } | 222 } |
| 208 } | 223 } |
| 209 | 224 |
| 210 if (primary) | 225 if (primary) |
| 211 return primary->file_id(); | 226 return primary->file_id(); |
| 212 return std::string(); | 227 return std::string(); |
| 213 } | 228 } |
| 214 | 229 |
| 215 void ConflictResolver::RemoveNonPrimaryFiles( | 230 void ConflictResolver::RemoveNonPrimaryFiles(scoped_ptr<SyncTaskToken> token) { |
| 216 const SyncStatusCallback& callback) { | |
| 217 DCHECK(!non_primary_file_ids_.empty()); | 231 DCHECK(!non_primary_file_ids_.empty()); |
| 218 | 232 |
| 219 std::string file_id = non_primary_file_ids_.back().first; | 233 std::string file_id = non_primary_file_ids_.back().first; |
| 220 std::string etag = non_primary_file_ids_.back().second; | 234 std::string etag = non_primary_file_ids_.back().second; |
| 221 non_primary_file_ids_.pop_back(); | 235 non_primary_file_ids_.pop_back(); |
| 222 | 236 |
| 223 DCHECK_NE(target_file_id_, file_id); | 237 DCHECK_NE(target_file_id_, file_id); |
| 224 | 238 |
| 225 util::Log(logging::LOG_VERBOSE, FROM_HERE, | 239 token->RecordLog(base::StringPrintf( |
| 226 "[ConflictResolver] Remove non-primary file %s", file_id.c_str()); | 240 "Remove non-primary file %s", file_id.c_str())); |
| 227 | 241 |
| 228 // TODO(tzik): Check if the file is a folder, and merge its contents into | 242 // TODO(tzik): Check if the file is a folder, and merge its contents into |
| 229 // the folder identified by |target_file_id_|. | 243 // the folder identified by |target_file_id_|. |
| 230 drive_service()->DeleteResource( | 244 drive_service()->DeleteResource( |
| 231 file_id, etag, | 245 file_id, etag, |
| 232 base::Bind(&ConflictResolver::DidRemoveFile, | 246 base::Bind(&ConflictResolver::DidRemoveFile, |
| 233 weak_ptr_factory_.GetWeakPtr(), | 247 weak_ptr_factory_.GetWeakPtr(), |
| 234 callback, file_id)); | 248 base::Passed(&token), file_id)); |
| 235 } | 249 } |
| 236 | 250 |
| 237 void ConflictResolver::DidRemoveFile(const SyncStatusCallback& callback, | 251 void ConflictResolver::DidRemoveFile(scoped_ptr<SyncTaskToken> token, |
| 238 const std::string& file_id, | 252 const std::string& file_id, |
| 239 google_apis::GDataErrorCode error) { | 253 google_apis::GDataErrorCode error) { |
| 240 if (error == google_apis::HTTP_PRECONDITION || | 254 if (error == google_apis::HTTP_PRECONDITION || |
| 241 error == google_apis::HTTP_CONFLICT) { | 255 error == google_apis::HTTP_CONFLICT) { |
| 242 UpdateFileMetadata(file_id, callback); | 256 UpdateFileMetadata(file_id, token.Pass()); |
| 243 return; | 257 return; |
| 244 } | 258 } |
| 245 | 259 |
| 246 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error); | 260 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error); |
| 247 if (status != SYNC_STATUS_OK && error != google_apis::HTTP_NOT_FOUND) { | 261 if (status != SYNC_STATUS_OK && error != google_apis::HTTP_NOT_FOUND) { |
| 248 callback.Run(status); | 262 SyncTaskManager::NotifyTaskDone(token.Pass(), status); |
| 249 return; | 263 return; |
| 250 } | 264 } |
| 251 | 265 |
| 252 deleted_file_ids_.push_back(file_id); | 266 deleted_file_ids_.push_back(file_id); |
| 253 if (!non_primary_file_ids_.empty()) { | 267 if (!non_primary_file_ids_.empty()) { |
| 254 RemoveNonPrimaryFiles(callback); | 268 RemoveNonPrimaryFiles(token.Pass()); |
| 255 return; | 269 return; |
| 256 } | 270 } |
| 257 | 271 |
| 258 metadata_database()->UpdateByDeletedRemoteFileList( | 272 metadata_database()->UpdateByDeletedRemoteFileList( |
| 259 deleted_file_ids_, callback); | 273 deleted_file_ids_, SyncTaskToken::WrapToCallback(token.Pass())); |
| 260 } | 274 } |
| 261 | 275 |
| 262 bool ConflictResolver::IsContextReady() { | 276 bool ConflictResolver::IsContextReady() { |
| 263 return sync_context_->GetDriveService() && | 277 return sync_context_->GetDriveService() && |
| 264 sync_context_->GetMetadataDatabase(); | 278 sync_context_->GetMetadataDatabase(); |
| 265 } | 279 } |
| 266 | 280 |
| 267 void ConflictResolver::UpdateFileMetadata( | 281 void ConflictResolver::UpdateFileMetadata( |
| 268 const std::string& file_id, | 282 const std::string& file_id, |
| 269 const SyncStatusCallback& callback) { | 283 scoped_ptr<SyncTaskToken> token) { |
| 270 drive_service()->GetFileResource( | 284 drive_service()->GetFileResource( |
| 271 file_id, | 285 file_id, |
| 272 base::Bind(&ConflictResolver::DidGetRemoteMetadata, | 286 base::Bind(&ConflictResolver::DidGetRemoteMetadata, |
| 273 weak_ptr_factory_.GetWeakPtr(), file_id, callback)); | 287 weak_ptr_factory_.GetWeakPtr(), file_id, |
| 288 base::Passed(&token))); |
| 274 } | 289 } |
| 275 | 290 |
| 276 void ConflictResolver::DidGetRemoteMetadata( | 291 void ConflictResolver::DidGetRemoteMetadata( |
| 277 const std::string& file_id, | 292 const std::string& file_id, |
| 278 const SyncStatusCallback& callback, | 293 scoped_ptr<SyncTaskToken> token, |
| 279 google_apis::GDataErrorCode error, | 294 google_apis::GDataErrorCode error, |
| 280 scoped_ptr<google_apis::FileResource> entry) { | 295 scoped_ptr<google_apis::FileResource> entry) { |
| 281 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error); | 296 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error); |
| 282 if (status != SYNC_STATUS_OK && error != google_apis::HTTP_NOT_FOUND) { | 297 if (status != SYNC_STATUS_OK && error != google_apis::HTTP_NOT_FOUND) { |
| 283 callback.Run(status); | 298 SyncTaskManager::NotifyTaskDone(token.Pass(), status); |
| 284 return; | 299 return; |
| 285 } | 300 } |
| 286 | 301 |
| 287 if (error != google_apis::HTTP_NOT_FOUND) { | 302 if (error != google_apis::HTTP_NOT_FOUND) { |
| 288 metadata_database()->UpdateByDeletedRemoteFile(file_id, callback); | 303 metadata_database()->UpdateByDeletedRemoteFile( |
| 304 file_id, SyncTaskToken::WrapToCallback(token.Pass())); |
| 289 return; | 305 return; |
| 290 } | 306 } |
| 291 | 307 |
| 292 if (!entry) { | 308 if (!entry) { |
| 293 NOTREACHED(); | 309 NOTREACHED(); |
| 294 callback.Run(SYNC_STATUS_FAILED); | 310 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED); |
| 295 return; | 311 return; |
| 296 } | 312 } |
| 297 | 313 |
| 298 metadata_database()->UpdateByFileResource(*entry, callback); | 314 metadata_database()->UpdateByFileResource( |
| 315 *entry, SyncTaskToken::WrapToCallback(token.Pass())); |
| 299 } | 316 } |
| 300 | 317 |
| 301 drive::DriveServiceInterface* ConflictResolver::drive_service() { | 318 drive::DriveServiceInterface* ConflictResolver::drive_service() { |
| 302 set_used_network(true); | 319 set_used_network(true); |
| 303 return sync_context_->GetDriveService(); | 320 return sync_context_->GetDriveService(); |
| 304 } | 321 } |
| 305 | 322 |
| 306 MetadataDatabase* ConflictResolver::metadata_database() { | 323 MetadataDatabase* ConflictResolver::metadata_database() { |
| 307 return sync_context_->GetMetadataDatabase(); | 324 return sync_context_->GetMetadataDatabase(); |
| 308 } | 325 } |
| 309 | 326 |
| 310 } // namespace drive_backend | 327 } // namespace drive_backend |
| 311 } // namespace sync_file_system | 328 } // namespace sync_file_system |
| OLD | NEW |