• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
EmptyStatusCallback(sync_file_system::SyncStatusCode status)17 void EmptyStatusCallback(sync_file_system::SyncStatusCode status) {}
18 
19 }  // namespace
20 
21 namespace sync_file_system {
22 namespace drive_backend {
23 
RemoteSyncDelegate(DriveFileSyncService * sync_service,const RemoteChange & remote_change)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 
~RemoteSyncDelegate()34 RemoteSyncDelegate::~RemoteSyncDelegate() {}
35 
Run(const SyncStatusCallback & callback)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 
DidPrepareForProcessRemoteChange(const SyncStatusCallback & callback,SyncStatusCode status,const SyncFileMetadata & metadata,const FileChangeList & local_changes)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 
ApplyRemoteChange(const SyncStatusCallback & callback)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 
DidApplyRemoteChange(const SyncStatusCallback & callback,SyncStatusCode status)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 
DeleteMetadata(const SyncStatusCallback & callback)182 void RemoteSyncDelegate::DeleteMetadata(const SyncStatusCallback& callback) {
183   metadata_store()->DeleteEntry(
184       url(),
185       base::Bind(&RemoteSyncDelegate::CompleteSync, AsWeakPtr(), callback));
186 }
187 
DownloadFile(const SyncStatusCallback & callback)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 
DidDownloadFile(const SyncStatusCallback & callback,google_apis::GDataErrorCode error,const std::string & md5_checksum,int64 file_size,const base::Time & updated_time,webkit_blob::ScopedFile downloaded_file)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 
HandleConflict(const SyncStatusCallback & callback,SyncFileType remote_file_type)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 
HandleLocalWin(const SyncStatusCallback & callback)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 
HandleRemoteWin(const SyncStatusCallback & callback,SyncFileType remote_file_type)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 
HandleManualResolutionCase(const SyncStatusCallback & callback)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 
DidGetEntryForConflictResolution(const SyncStatusCallback & callback,google_apis::GDataErrorCode error,scoped_ptr<google_apis::ResourceEntry> entry)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 
ResolveToLocal(const SyncStatusCallback & callback)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 
DidResolveToLocal(const SyncStatusCallback & callback,SyncStatusCode status)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 
ResolveToRemote(const SyncStatusCallback & callback)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 
DidResolveToRemote(const SyncStatusCallback & callback,SyncStatusCode status)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 
StartOver(const SyncStatusCallback & callback,SyncStatusCode status)395 void RemoteSyncDelegate::StartOver(
396     const SyncStatusCallback& callback,
397     SyncStatusCode status) {
398   DidPrepareForProcessRemoteChange(
399       callback, status, local_metadata_, FileChangeList());
400 }
401 
CompleteSync(const SyncStatusCallback & callback,SyncStatusCode status)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 
AbortSync(const SyncStatusCallback & callback,SyncStatusCode status)436 void RemoteSyncDelegate::AbortSync(
437     const SyncStatusCallback& callback,
438     SyncStatusCode status) {
439   clear_local_changes_ = false;
440   DidFinish(callback, status);
441 }
442 
DidFinish(const SyncStatusCallback & callback,SyncStatusCode status)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 
DispatchCallbackAfterDidFinish(const SyncStatusCallback & callback,SyncStatusCode status)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 
GDataErrorCodeToSyncStatusCodeWrapper(google_apis::GDataErrorCode error)466 SyncStatusCode RemoteSyncDelegate::GDataErrorCodeToSyncStatusCodeWrapper(
467     google_apis::GDataErrorCode error) {
468   return sync_service_->GDataErrorCodeToSyncStatusCodeWrapper(error);
469 }
470 
metadata_store()471 DriveMetadataStore* RemoteSyncDelegate::metadata_store() {
472   return sync_service_->metadata_store_.get();
473 }
474 
api_util()475 APIUtilInterface* RemoteSyncDelegate::api_util() {
476   return sync_service_->api_util_.get();
477 }
478 
remote_change_handler()479 RemoteChangeHandler* RemoteSyncDelegate::remote_change_handler() {
480   return &sync_service_->remote_change_handler_;
481 }
482 
remote_change_processor()483 RemoteChangeProcessor* RemoteSyncDelegate::remote_change_processor() {
484   return sync_service_->remote_change_processor_;
485 }
486 
conflict_resolution_resolver()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