Check USB device path access when prompting users to select a device.
[chromium-blink-merge.git] / chrome / browser / sync_file_system / drive_backend / conflict_resolver.cc
blob59ebf2963ab795f583cdbcb51b8d8ff36f2050d1
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.
5 #include "chrome/browser/sync_file_system/drive_backend/conflict_resolver.h"
7 #include "base/callback.h"
8 #include "base/format_macros.h"
9 #include "base/location.h"
10 #include "base/logging.h"
11 #include "base/strings/stringprintf.h"
12 #include "chrome/browser/drive/drive_api_util.h"
13 #include "chrome/browser/drive/drive_service_interface.h"
14 #include "chrome/browser/drive/drive_uploader.h"
15 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_util.h"
16 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.h"
17 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.pb.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"
21 #include "chrome/browser/sync_file_system/logger.h"
22 #include "google_apis/drive/drive_api_parser.h"
24 namespace sync_file_system {
25 namespace drive_backend {
27 ConflictResolver::ConflictResolver(SyncEngineContext* sync_context)
28 : sync_context_(sync_context),
29 weak_ptr_factory_(this) {}
31 ConflictResolver::~ConflictResolver() {}
33 void ConflictResolver::RunPreflight(scoped_ptr<SyncTaskToken> token) {
34 token->InitializeTaskLog("Conflict Resolution");
36 scoped_ptr<TaskBlocker> task_blocker(new TaskBlocker);
37 task_blocker->exclusive = true;
38 SyncTaskManager::UpdateTaskBlocker(
39 token.Pass(), task_blocker.Pass(),
40 base::Bind(&ConflictResolver::RunExclusive,
41 weak_ptr_factory_.GetWeakPtr()));
44 void ConflictResolver::RunExclusive(scoped_ptr<SyncTaskToken> token) {
45 if (!IsContextReady()) {
46 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED);
47 return;
50 // Conflict resolution should be invoked on clean tree.
51 if (metadata_database()->HasDirtyTracker()) {
52 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_RETRY);
53 return;
56 TrackerIDSet trackers;
57 if (metadata_database()->GetMultiParentFileTrackers(
58 &target_file_id_, &trackers)) {
59 DCHECK_LT(1u, trackers.size());
60 if (!trackers.has_active()) {
61 NOTREACHED();
62 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED);
63 return;
66 token->RecordLog(base::StringPrintf(
67 "Detected multi-parent trackers (active tracker_id=%" PRId64 ")",
68 trackers.active_tracker()));
70 DCHECK(trackers.has_active());
71 for (TrackerIDSet::const_iterator itr = trackers.begin();
72 itr != trackers.end(); ++itr) {
73 FileTracker tracker;
74 if (!metadata_database()->FindTrackerByTrackerID(*itr, &tracker)) {
75 NOTREACHED();
76 continue;
79 if (tracker.active())
80 continue;
82 FileTracker parent_tracker;
83 bool should_success = metadata_database()->FindTrackerByTrackerID(
84 tracker.parent_tracker_id(), &parent_tracker);
85 if (!should_success) {
86 NOTREACHED();
87 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED);
88 return;
90 parents_to_remove_.push_back(parent_tracker.file_id());
92 DetachFromNonPrimaryParents(token.Pass());
93 return;
96 if (metadata_database()->GetConflictingTrackers(&trackers)) {
97 target_file_id_ = PickPrimaryFile(trackers);
98 DCHECK(!target_file_id_.empty());
99 int64 primary_tracker_id = -1;
100 for (TrackerIDSet::const_iterator itr = trackers.begin();
101 itr != trackers.end(); ++itr) {
102 FileTracker tracker;
103 if (!metadata_database()->FindTrackerByTrackerID(*itr, &tracker)) {
104 NOTREACHED();
105 continue;
107 if (tracker.file_id() != target_file_id_) {
108 non_primary_file_ids_.push_back(
109 std::make_pair(tracker.file_id(), tracker.synced_details().etag()));
110 } else {
111 primary_tracker_id = tracker.tracker_id();
115 token->RecordLog(base::StringPrintf(
116 "Detected %" PRIuS " conflicting trackers "
117 "(primary tracker_id=%" PRId64 ")",
118 non_primary_file_ids_.size(), primary_tracker_id));
120 RemoveNonPrimaryFiles(token.Pass());
121 return;
124 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_NO_CONFLICT);
127 void ConflictResolver::DetachFromNonPrimaryParents(
128 scoped_ptr<SyncTaskToken> token) {
129 DCHECK(!parents_to_remove_.empty());
131 // TODO(tzik): Check if ETag match is available for
132 // RemoteResourceFromDirectory.
133 std::string parent_folder_id = parents_to_remove_.back();
134 parents_to_remove_.pop_back();
136 token->RecordLog(base::StringPrintf(
137 "Detach %s from %s",
138 target_file_id_.c_str(), parent_folder_id.c_str()));
140 drive_service()->RemoveResourceFromDirectory(
141 parent_folder_id, target_file_id_,
142 base::Bind(&ConflictResolver::DidDetachFromParent,
143 weak_ptr_factory_.GetWeakPtr(),
144 base::Passed(&token)));
147 void ConflictResolver::DidDetachFromParent(
148 scoped_ptr<SyncTaskToken> token,
149 google_apis::DriveApiErrorCode error) {
150 SyncStatusCode status = DriveApiErrorCodeToSyncStatusCode(error);
151 if (status != SYNC_STATUS_OK) {
152 SyncTaskManager::NotifyTaskDone(token.Pass(), status);
153 return;
156 if (!parents_to_remove_.empty()) {
157 DetachFromNonPrimaryParents(token.Pass());
158 return;
161 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_OK);
164 std::string ConflictResolver::PickPrimaryFile(const TrackerIDSet& trackers) {
165 scoped_ptr<FileMetadata> primary;
166 for (TrackerIDSet::const_iterator itr = trackers.begin();
167 itr != trackers.end(); ++itr) {
168 FileTracker tracker;
169 if (!metadata_database()->FindTrackerByTrackerID(*itr, &tracker)) {
170 NOTREACHED();
171 continue;
174 scoped_ptr<FileMetadata> file_metadata(new FileMetadata);
175 if (!metadata_database()->FindFileByFileID(
176 tracker.file_id(), file_metadata.get())) {
177 NOTREACHED();
178 continue;
181 if (!primary) {
182 primary = file_metadata.Pass();
183 continue;
186 DCHECK(primary->details().file_kind() == FILE_KIND_FILE ||
187 primary->details().file_kind() == FILE_KIND_FOLDER);
188 DCHECK(file_metadata->details().file_kind() == FILE_KIND_FILE ||
189 file_metadata->details().file_kind() == FILE_KIND_FOLDER);
191 if (primary->details().file_kind() == FILE_KIND_FILE) {
192 if (file_metadata->details().file_kind() == FILE_KIND_FOLDER) {
193 // Prioritize folders over regular files.
194 primary = file_metadata.Pass();
195 continue;
198 DCHECK(file_metadata->details().file_kind() == FILE_KIND_FILE);
199 if (primary->details().modification_time() <
200 file_metadata->details().modification_time()) {
201 // Prioritize last write for regular files.
202 primary = file_metadata.Pass();
203 continue;
206 continue;
209 DCHECK(primary->details().file_kind() == FILE_KIND_FOLDER);
210 if (file_metadata->details().file_kind() == FILE_KIND_FILE) {
211 // Prioritize folders over regular files.
212 continue;
215 DCHECK(file_metadata->details().file_kind() == FILE_KIND_FOLDER);
216 if (primary->details().creation_time() >
217 file_metadata->details().creation_time()) {
218 // Prioritize first create for folders.
219 primary = file_metadata.Pass();
220 continue;
224 if (primary)
225 return primary->file_id();
226 return std::string();
229 void ConflictResolver::RemoveNonPrimaryFiles(scoped_ptr<SyncTaskToken> token) {
230 DCHECK(!non_primary_file_ids_.empty());
232 std::string file_id = non_primary_file_ids_.back().first;
233 std::string etag = non_primary_file_ids_.back().second;
234 non_primary_file_ids_.pop_back();
236 DCHECK_NE(target_file_id_, file_id);
238 token->RecordLog(base::StringPrintf(
239 "Remove non-primary file %s", file_id.c_str()));
241 // TODO(tzik): Check if the file is a folder, and merge its contents into
242 // the folder identified by |target_file_id_|.
243 drive_service()->DeleteResource(
244 file_id, etag,
245 base::Bind(&ConflictResolver::DidRemoveFile,
246 weak_ptr_factory_.GetWeakPtr(),
247 base::Passed(&token), file_id));
250 void ConflictResolver::DidRemoveFile(scoped_ptr<SyncTaskToken> token,
251 const std::string& file_id,
252 google_apis::DriveApiErrorCode error) {
253 if (error == google_apis::HTTP_PRECONDITION ||
254 error == google_apis::HTTP_CONFLICT) {
255 UpdateFileMetadata(file_id, token.Pass());
256 return;
259 SyncStatusCode status = DriveApiErrorCodeToSyncStatusCode(error);
260 if (status != SYNC_STATUS_OK && error != google_apis::HTTP_NOT_FOUND) {
261 SyncTaskManager::NotifyTaskDone(token.Pass(), status);
262 return;
265 deleted_file_ids_.push_back(file_id);
266 if (!non_primary_file_ids_.empty()) {
267 RemoveNonPrimaryFiles(token.Pass());
268 return;
271 status = metadata_database()->UpdateByDeletedRemoteFileList(
272 deleted_file_ids_);
273 SyncTaskManager::NotifyTaskDone(token.Pass(), status);
276 bool ConflictResolver::IsContextReady() {
277 return sync_context_->GetDriveService() &&
278 sync_context_->GetMetadataDatabase();
281 void ConflictResolver::UpdateFileMetadata(
282 const std::string& file_id,
283 scoped_ptr<SyncTaskToken> token) {
284 drive_service()->GetFileResource(
285 file_id,
286 base::Bind(&ConflictResolver::DidGetRemoteMetadata,
287 weak_ptr_factory_.GetWeakPtr(), file_id,
288 base::Passed(&token)));
291 void ConflictResolver::DidGetRemoteMetadata(
292 const std::string& file_id,
293 scoped_ptr<SyncTaskToken> token,
294 google_apis::DriveApiErrorCode error,
295 scoped_ptr<google_apis::FileResource> entry) {
296 SyncStatusCode status = DriveApiErrorCodeToSyncStatusCode(error);
297 if (status != SYNC_STATUS_OK && error != google_apis::HTTP_NOT_FOUND) {
298 SyncTaskManager::NotifyTaskDone(token.Pass(), status);
299 return;
302 if (error != google_apis::HTTP_NOT_FOUND) {
303 status = metadata_database()->UpdateByDeletedRemoteFile(file_id);
304 SyncTaskManager::NotifyTaskDone(token.Pass(), status);
305 return;
308 if (!entry) {
309 NOTREACHED();
310 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED);
311 return;
314 status = metadata_database()->UpdateByFileResource(*entry);
315 SyncTaskManager::NotifyTaskDone(token.Pass(), status);
318 drive::DriveServiceInterface* ConflictResolver::drive_service() {
319 set_used_network(true);
320 return sync_context_->GetDriveService();
323 MetadataDatabase* ConflictResolver::metadata_database() {
324 return sync_context_->GetMetadataDatabase();
327 } // namespace drive_backend
328 } // namespace sync_file_system