• 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/local/local_file_sync_context.h"
6 
7 #include "base/bind.h"
8 #include "base/file_util.h"
9 #include "base/location.h"
10 #include "base/single_thread_task_runner.h"
11 #include "base/stl_util.h"
12 #include "base/task_runner_util.h"
13 #include "chrome/browser/sync_file_system/file_change.h"
14 #include "chrome/browser/sync_file_system/local/local_file_change_tracker.h"
15 #include "chrome/browser/sync_file_system/local/local_origin_change_observer.h"
16 #include "chrome/browser/sync_file_system/local/root_delete_helper.h"
17 #include "chrome/browser/sync_file_system/local/sync_file_system_backend.h"
18 #include "chrome/browser/sync_file_system/local/syncable_file_operation_runner.h"
19 #include "chrome/browser/sync_file_system/logger.h"
20 #include "chrome/browser/sync_file_system/sync_file_metadata.h"
21 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
22 #include "webkit/browser/fileapi/file_system_context.h"
23 #include "webkit/browser/fileapi/file_system_file_util.h"
24 #include "webkit/browser/fileapi/file_system_operation_context.h"
25 #include "webkit/browser/fileapi/file_system_operation_runner.h"
26 #include "webkit/common/blob/scoped_file.h"
27 #include "webkit/common/fileapi/file_system_util.h"
28 
29 using fileapi::FileSystemContext;
30 using fileapi::FileSystemFileUtil;
31 using fileapi::FileSystemOperation;
32 using fileapi::FileSystemOperationContext;
33 using fileapi::FileSystemURL;
34 
35 namespace sync_file_system {
36 
37 namespace {
38 
39 const int kMaxConcurrentSyncableOperation = 3;
40 const int kNotifyChangesDurationInSec = 1;
41 const int kMaxURLsToFetchForLocalSync = 5;
42 
43 const base::FilePath::CharType kSnapshotDir[] = FILE_PATH_LITERAL("snapshots");
44 
45 }  // namespace
46 
LocalFileSyncContext(const base::FilePath & base_path,leveldb::Env * env_override,base::SingleThreadTaskRunner * ui_task_runner,base::SingleThreadTaskRunner * io_task_runner)47 LocalFileSyncContext::LocalFileSyncContext(
48     const base::FilePath& base_path,
49     leveldb::Env* env_override,
50     base::SingleThreadTaskRunner* ui_task_runner,
51     base::SingleThreadTaskRunner* io_task_runner)
52     : local_base_path_(base_path.Append(FILE_PATH_LITERAL("local"))),
53       env_override_(env_override),
54       ui_task_runner_(ui_task_runner),
55       io_task_runner_(io_task_runner),
56       shutdown_on_ui_(false),
57       shutdown_on_io_(false),
58       mock_notify_changes_duration_in_sec_(-1) {
59   DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
60 }
61 
MaybeInitializeFileSystemContext(const GURL & source_url,FileSystemContext * file_system_context,const SyncStatusCallback & callback)62 void LocalFileSyncContext::MaybeInitializeFileSystemContext(
63     const GURL& source_url,
64     FileSystemContext* file_system_context,
65     const SyncStatusCallback& callback) {
66   DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
67   if (ContainsKey(file_system_contexts_, file_system_context)) {
68     // The context has been already initialized. Just dispatch the callback
69     // with SYNC_STATUS_OK.
70     ui_task_runner_->PostTask(FROM_HERE, base::Bind(callback, SYNC_STATUS_OK));
71     return;
72   }
73 
74   StatusCallbackQueue& callback_queue =
75       pending_initialize_callbacks_[file_system_context];
76   callback_queue.push_back(callback);
77   if (callback_queue.size() > 1)
78     return;
79 
80   // The sync service always expects the origin (app) is initialized
81   // for writable way (even when MaybeInitializeFileSystemContext is called
82   // from read-only OpenFileSystem), so open the filesystem with
83   // CREATE_IF_NONEXISTENT here.
84   fileapi::FileSystemBackend::OpenFileSystemCallback open_filesystem_callback =
85       base::Bind(&LocalFileSyncContext::InitializeFileSystemContextOnIOThread,
86                  this, source_url, make_scoped_refptr(file_system_context));
87   io_task_runner_->PostTask(
88       FROM_HERE,
89       base::Bind(&fileapi::SandboxFileSystemBackendDelegate::OpenFileSystem,
90                  base::Unretained(file_system_context->sandbox_delegate()),
91                  source_url, fileapi::kFileSystemTypeSyncable,
92                  fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
93                  open_filesystem_callback, GURL()));
94 }
95 
ShutdownOnUIThread()96 void LocalFileSyncContext::ShutdownOnUIThread() {
97   DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
98   shutdown_on_ui_ = true;
99   io_task_runner_->PostTask(
100       FROM_HERE,
101       base::Bind(&LocalFileSyncContext::ShutdownOnIOThread, this));
102 }
103 
GetFileForLocalSync(FileSystemContext * file_system_context,const LocalFileSyncInfoCallback & callback)104 void LocalFileSyncContext::GetFileForLocalSync(
105     FileSystemContext* file_system_context,
106     const LocalFileSyncInfoCallback& callback) {
107   DCHECK(file_system_context);
108   DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
109 
110   std::deque<FileSystemURL>* urls = new std::deque<FileSystemURL>;
111   file_system_context->default_file_task_runner()->PostTaskAndReply(
112       FROM_HERE,
113       base::Bind(&LocalFileSyncContext::GetNextURLsForSyncOnFileThread,
114                  this, make_scoped_refptr(file_system_context),
115                  base::Unretained(urls)),
116       base::Bind(&LocalFileSyncContext::TryPrepareForLocalSync,
117                  this, make_scoped_refptr(file_system_context),
118                  base::Owned(urls), callback));
119 }
120 
ClearChangesForURL(FileSystemContext * file_system_context,const FileSystemURL & url,const base::Closure & done_callback)121 void LocalFileSyncContext::ClearChangesForURL(
122     FileSystemContext* file_system_context,
123     const FileSystemURL& url,
124     const base::Closure& done_callback) {
125   // This is initially called on UI thread and to be relayed to FILE thread.
126   DCHECK(file_system_context);
127   if (!file_system_context->default_file_task_runner()->
128           RunsTasksOnCurrentThread()) {
129     DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
130     file_system_context->default_file_task_runner()->PostTask(
131         FROM_HERE,
132         base::Bind(&LocalFileSyncContext::ClearChangesForURL,
133                    this, make_scoped_refptr(file_system_context),
134                    url, done_callback));
135     return;
136   }
137 
138   SyncFileSystemBackend* backend =
139       SyncFileSystemBackend::GetBackend(file_system_context);
140   DCHECK(backend);
141   DCHECK(backend->change_tracker());
142   backend->change_tracker()->ClearChangesForURL(url);
143 
144   // Call the completion callback on UI thread.
145   ui_task_runner_->PostTask(FROM_HERE, done_callback);
146 }
147 
FinalizeSnapshotSync(fileapi::FileSystemContext * file_system_context,const fileapi::FileSystemURL & url,SyncStatusCode sync_finish_status,const base::Closure & done_callback)148 void LocalFileSyncContext::FinalizeSnapshotSync(
149     fileapi::FileSystemContext* file_system_context,
150     const fileapi::FileSystemURL& url,
151     SyncStatusCode sync_finish_status,
152     const base::Closure& done_callback) {
153   DCHECK(file_system_context);
154   DCHECK(url.is_valid());
155   if (!file_system_context->default_file_task_runner()->
156           RunsTasksOnCurrentThread()) {
157     file_system_context->default_file_task_runner()->PostTask(
158         FROM_HERE,
159         base::Bind(&LocalFileSyncContext::FinalizeSnapshotSync,
160                    this, make_scoped_refptr(file_system_context),
161                    url, sync_finish_status, done_callback));
162     return;
163   }
164 
165   SyncFileSystemBackend* backend =
166       SyncFileSystemBackend::GetBackend(file_system_context);
167   DCHECK(backend);
168   DCHECK(backend->change_tracker());
169 
170   if (sync_finish_status == SYNC_STATUS_OK ||
171       sync_finish_status == SYNC_STATUS_HAS_CONFLICT) {
172     // Commit the in-memory mirror change.
173     backend->change_tracker()->ResetToMirrorAndCommitChangesForURL(url);
174   } else {
175     // Abort in-memory mirror change.
176     backend->change_tracker()->RemoveMirrorAndCommitChangesForURL(url);
177     if (sync_finish_status == SYNC_STATUS_FILE_BUSY)
178       backend->change_tracker()->DemoteChangesForURL(url);
179   }
180 
181   // We've been keeping it in writing mode, so clear the writing counter
182   // to unblock sync activities.
183   io_task_runner_->PostTask(
184       FROM_HERE, base::Bind(
185           &LocalFileSyncContext::FinalizeSnapshotSyncOnIOThread, this, url));
186 
187   // Call the completion callback on UI thread.
188   ui_task_runner_->PostTask(FROM_HERE, done_callback);
189 }
190 
FinalizeExclusiveSync(fileapi::FileSystemContext * file_system_context,const fileapi::FileSystemURL & url,bool clear_local_changes,const base::Closure & done_callback)191 void LocalFileSyncContext::FinalizeExclusiveSync(
192     fileapi::FileSystemContext* file_system_context,
193     const fileapi::FileSystemURL& url,
194     bool clear_local_changes,
195     const base::Closure& done_callback) {
196   DCHECK(file_system_context);
197   if (!url.is_valid()) {
198     done_callback.Run();
199     return;
200   }
201 
202   if (clear_local_changes) {
203     ClearChangesForURL(file_system_context, url,
204                        base::Bind(&LocalFileSyncContext::FinalizeExclusiveSync,
205                                   this, make_scoped_refptr(file_system_context),
206                                   url, false, done_callback));
207     return;
208   }
209 
210   io_task_runner_->PostTask(
211       FROM_HERE,
212       base::Bind(&LocalFileSyncContext::ClearSyncFlagOnIOThread,
213                  this, url, false /* for_snapshot_sync */));
214 
215   done_callback.Run();
216 }
217 
PrepareForSync(FileSystemContext * file_system_context,const FileSystemURL & url,SyncMode sync_mode,const LocalFileSyncInfoCallback & callback)218 void LocalFileSyncContext::PrepareForSync(
219     FileSystemContext* file_system_context,
220     const FileSystemURL& url,
221     SyncMode sync_mode,
222     const LocalFileSyncInfoCallback& callback) {
223   // This is initially called on UI thread and to be relayed to IO thread.
224   if (!io_task_runner_->RunsTasksOnCurrentThread()) {
225     DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
226     io_task_runner_->PostTask(
227         FROM_HERE,
228         base::Bind(&LocalFileSyncContext::PrepareForSync, this,
229                    make_scoped_refptr(file_system_context), url,
230                    sync_mode, callback));
231     return;
232   }
233   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
234   const bool syncable = sync_status()->IsSyncable(url);
235   // Disable writing if it's ready to be synced.
236   if (syncable)
237     sync_status()->StartSyncing(url);
238   ui_task_runner_->PostTask(
239       FROM_HERE,
240       base::Bind(&LocalFileSyncContext::DidGetWritingStatusForSync,
241                  this, make_scoped_refptr(file_system_context),
242                  syncable ? SYNC_STATUS_OK :
243                             SYNC_STATUS_FILE_BUSY,
244                  url, sync_mode, callback));
245 }
246 
RegisterURLForWaitingSync(const FileSystemURL & url,const base::Closure & on_syncable_callback)247 void LocalFileSyncContext::RegisterURLForWaitingSync(
248     const FileSystemURL& url,
249     const base::Closure& on_syncable_callback) {
250   // This is initially called on UI thread and to be relayed to IO thread.
251   if (!io_task_runner_->RunsTasksOnCurrentThread()) {
252     DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
253     io_task_runner_->PostTask(
254         FROM_HERE,
255         base::Bind(&LocalFileSyncContext::RegisterURLForWaitingSync,
256                    this, url, on_syncable_callback));
257     return;
258   }
259   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
260   if (shutdown_on_io_)
261     return;
262   if (sync_status()->IsSyncable(url)) {
263     // No need to register; fire the callback now.
264     ui_task_runner_->PostTask(FROM_HERE, on_syncable_callback);
265     return;
266   }
267   url_waiting_sync_on_io_ = url;
268   url_syncable_callback_ = on_syncable_callback;
269 }
270 
ApplyRemoteChange(FileSystemContext * file_system_context,const FileChange & change,const base::FilePath & local_path,const FileSystemURL & url,const SyncStatusCallback & callback)271 void LocalFileSyncContext::ApplyRemoteChange(
272     FileSystemContext* file_system_context,
273     const FileChange& change,
274     const base::FilePath& local_path,
275     const FileSystemURL& url,
276     const SyncStatusCallback& callback) {
277   if (!io_task_runner_->RunsTasksOnCurrentThread()) {
278     DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
279     io_task_runner_->PostTask(
280         FROM_HERE,
281         base::Bind(&LocalFileSyncContext::ApplyRemoteChange, this,
282                    make_scoped_refptr(file_system_context),
283                    change, local_path, url, callback));
284     return;
285   }
286   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
287   DCHECK(!sync_status()->IsWritable(url));
288   DCHECK(!sync_status()->IsWriting(url));
289 
290   FileSystemOperation::StatusCallback operation_callback;
291   switch (change.change()) {
292     case FileChange::FILE_CHANGE_DELETE:
293       HandleRemoteDelete(file_system_context, url, callback);
294       return;
295     case FileChange::FILE_CHANGE_ADD_OR_UPDATE:
296       HandleRemoteAddOrUpdate(
297           file_system_context, change, local_path, url, callback);
298       return;
299   }
300   NOTREACHED();
301   callback.Run(SYNC_STATUS_FAILED);
302 }
303 
HandleRemoteDelete(FileSystemContext * file_system_context,const FileSystemURL & url,const SyncStatusCallback & callback)304 void LocalFileSyncContext::HandleRemoteDelete(
305     FileSystemContext* file_system_context,
306     const FileSystemURL& url,
307     const SyncStatusCallback& callback) {
308   FileSystemURL url_for_sync = CreateSyncableFileSystemURLForSync(
309       file_system_context, url);
310 
311   // Handle root directory case differently.
312   if (fileapi::VirtualPath::IsRootPath(url.path())) {
313     DCHECK(!root_delete_helper_);
314     root_delete_helper_.reset(new RootDeleteHelper(
315         file_system_context, sync_status(), url,
316         base::Bind(&LocalFileSyncContext::DidApplyRemoteChange,
317                    this, url, callback)));
318     root_delete_helper_->Run();
319     return;
320   }
321 
322   file_system_context->operation_runner()->Remove(
323       url_for_sync, true /* recursive */,
324       base::Bind(&LocalFileSyncContext::DidApplyRemoteChange,
325                  this, url, callback));
326 }
327 
HandleRemoteAddOrUpdate(FileSystemContext * file_system_context,const FileChange & change,const base::FilePath & local_path,const FileSystemURL & url,const SyncStatusCallback & callback)328 void LocalFileSyncContext::HandleRemoteAddOrUpdate(
329     FileSystemContext* file_system_context,
330     const FileChange& change,
331     const base::FilePath& local_path,
332     const FileSystemURL& url,
333     const SyncStatusCallback& callback) {
334   FileSystemURL url_for_sync = CreateSyncableFileSystemURLForSync(
335       file_system_context, url);
336 
337   if (fileapi::VirtualPath::IsRootPath(url.path())) {
338     DidApplyRemoteChange(url, callback, base::File::FILE_OK);
339     return;
340   }
341 
342   file_system_context->operation_runner()->Remove(
343       url_for_sync, true /* recursive */,
344       base::Bind(
345           &LocalFileSyncContext::DidRemoveExistingEntryForRemoteAddOrUpdate,
346           this,
347           make_scoped_refptr(file_system_context),
348           change,
349           local_path,
350           url,
351           callback));
352 }
353 
DidRemoveExistingEntryForRemoteAddOrUpdate(FileSystemContext * file_system_context,const FileChange & change,const base::FilePath & local_path,const FileSystemURL & url,const SyncStatusCallback & callback,base::File::Error error)354 void LocalFileSyncContext::DidRemoveExistingEntryForRemoteAddOrUpdate(
355     FileSystemContext* file_system_context,
356     const FileChange& change,
357     const base::FilePath& local_path,
358     const FileSystemURL& url,
359     const SyncStatusCallback& callback,
360     base::File::Error error) {
361   // Remove() may fail if the target entry does not exist (which is ok),
362   // so we ignore |error| here.
363 
364   if (shutdown_on_io_) {
365     callback.Run(SYNC_FILE_ERROR_ABORT);
366     return;
367   }
368 
369   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
370   DCHECK(!sync_status()->IsWritable(url));
371   DCHECK(!sync_status()->IsWriting(url));
372 
373   FileSystemURL url_for_sync = CreateSyncableFileSystemURLForSync(
374       file_system_context, url);
375   FileSystemOperation::StatusCallback operation_callback = base::Bind(
376       &LocalFileSyncContext::DidApplyRemoteChange, this, url, callback);
377 
378   DCHECK_EQ(FileChange::FILE_CHANGE_ADD_OR_UPDATE, change.change());
379   switch (change.file_type()) {
380     case SYNC_FILE_TYPE_FILE: {
381       DCHECK(!local_path.empty());
382       base::FilePath dir_path = fileapi::VirtualPath::DirName(url.path());
383       if (dir_path.empty() ||
384           fileapi::VirtualPath::DirName(dir_path) == dir_path) {
385         // Copying into the root directory.
386         file_system_context->operation_runner()->CopyInForeignFile(
387             local_path, url_for_sync, operation_callback);
388       } else {
389         FileSystemURL dir_url = file_system_context->CreateCrackedFileSystemURL(
390             url_for_sync.origin(),
391             url_for_sync.mount_type(),
392             fileapi::VirtualPath::DirName(url_for_sync.virtual_path()));
393         file_system_context->operation_runner()->CreateDirectory(
394             dir_url,
395             false /* exclusive */,
396             true /* recursive */,
397             base::Bind(&LocalFileSyncContext::DidCreateDirectoryForCopyIn,
398                        this,
399                        make_scoped_refptr(file_system_context),
400                        local_path,
401                        url,
402                        operation_callback));
403       }
404       break;
405     }
406     case SYNC_FILE_TYPE_DIRECTORY:
407       file_system_context->operation_runner()->CreateDirectory(
408           url_for_sync, false /* exclusive */, true /* recursive */,
409           operation_callback);
410       break;
411     case SYNC_FILE_TYPE_UNKNOWN:
412       NOTREACHED() << "File type unknown for ADD_OR_UPDATE change";
413   }
414 }
415 
RecordFakeLocalChange(FileSystemContext * file_system_context,const FileSystemURL & url,const FileChange & change,const SyncStatusCallback & callback)416 void LocalFileSyncContext::RecordFakeLocalChange(
417     FileSystemContext* file_system_context,
418     const FileSystemURL& url,
419     const FileChange& change,
420     const SyncStatusCallback& callback) {
421   // This is called on UI thread and to be relayed to FILE thread.
422   DCHECK(file_system_context);
423   if (!file_system_context->default_file_task_runner()->
424           RunsTasksOnCurrentThread()) {
425     DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
426     file_system_context->default_file_task_runner()->PostTask(
427         FROM_HERE,
428         base::Bind(&LocalFileSyncContext::RecordFakeLocalChange,
429                    this, make_scoped_refptr(file_system_context),
430                    url, change, callback));
431     return;
432   }
433 
434   SyncFileSystemBackend* backend =
435       SyncFileSystemBackend::GetBackend(file_system_context);
436   DCHECK(backend);
437   DCHECK(backend->change_tracker());
438   backend->change_tracker()->MarkDirtyOnDatabase(url);
439   backend->change_tracker()->RecordChange(url, change);
440 
441   // Fire the callback on UI thread.
442   ui_task_runner_->PostTask(FROM_HERE,
443                             base::Bind(callback,
444                                        SYNC_STATUS_OK));
445 }
446 
GetFileMetadata(FileSystemContext * file_system_context,const FileSystemURL & url,const SyncFileMetadataCallback & callback)447 void LocalFileSyncContext::GetFileMetadata(
448     FileSystemContext* file_system_context,
449     const FileSystemURL& url,
450     const SyncFileMetadataCallback& callback) {
451   // This is initially called on UI thread and to be relayed to IO thread.
452   if (!io_task_runner_->RunsTasksOnCurrentThread()) {
453     DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
454     io_task_runner_->PostTask(
455         FROM_HERE,
456         base::Bind(&LocalFileSyncContext::GetFileMetadata, this,
457                    make_scoped_refptr(file_system_context), url, callback));
458     return;
459   }
460   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
461 
462   FileSystemURL url_for_sync = CreateSyncableFileSystemURLForSync(
463       file_system_context, url);
464   file_system_context->operation_runner()->GetMetadata(
465       url_for_sync, base::Bind(&LocalFileSyncContext::DidGetFileMetadata,
466                       this, callback));
467 }
468 
HasPendingLocalChanges(FileSystemContext * file_system_context,const FileSystemURL & url,const HasPendingLocalChangeCallback & callback)469 void LocalFileSyncContext::HasPendingLocalChanges(
470     FileSystemContext* file_system_context,
471     const FileSystemURL& url,
472     const HasPendingLocalChangeCallback& callback) {
473   // This gets called on UI thread and relays the task on FILE thread.
474   DCHECK(file_system_context);
475   if (!file_system_context->default_file_task_runner()->
476           RunsTasksOnCurrentThread()) {
477     DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
478     file_system_context->default_file_task_runner()->PostTask(
479         FROM_HERE,
480         base::Bind(&LocalFileSyncContext::HasPendingLocalChanges,
481                    this, make_scoped_refptr(file_system_context),
482                    url, callback));
483     return;
484   }
485 
486   SyncFileSystemBackend* backend =
487       SyncFileSystemBackend::GetBackend(file_system_context);
488   DCHECK(backend);
489   DCHECK(backend->change_tracker());
490   FileChangeList changes;
491   backend->change_tracker()->GetChangesForURL(url, &changes);
492 
493   // Fire the callback on UI thread.
494   ui_task_runner_->PostTask(FROM_HERE,
495                             base::Bind(callback,
496                                        SYNC_STATUS_OK,
497                                        !changes.empty()));
498 }
499 
PromoteDemotedChanges(const GURL & origin,fileapi::FileSystemContext * file_system_context)500 void LocalFileSyncContext::PromoteDemotedChanges(
501     const GURL& origin,
502     fileapi::FileSystemContext* file_system_context) {
503   // This is initially called on UI thread and to be relayed to FILE thread.
504   DCHECK(file_system_context);
505   if (!file_system_context->default_file_task_runner()->
506           RunsTasksOnCurrentThread()) {
507     DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
508     file_system_context->default_file_task_runner()->PostTask(
509         FROM_HERE,
510         base::Bind(&LocalFileSyncContext::PromoteDemotedChanges,
511                    this, origin, make_scoped_refptr(file_system_context)));
512     return;
513   }
514 
515   SyncFileSystemBackend* backend =
516       SyncFileSystemBackend::GetBackend(file_system_context);
517   DCHECK(backend);
518   DCHECK(backend->change_tracker());
519   if (!backend->change_tracker()->PromoteDemotedChanges())
520     return;
521 
522   io_task_runner_->PostTask(
523       FROM_HERE,
524       base::Bind(&LocalFileSyncContext::UpdateChangesForOrigin,
525                  this, origin));
526 }
527 
UpdateChangesForOrigin(const GURL & origin)528 void LocalFileSyncContext::UpdateChangesForOrigin(const GURL& origin) {
529   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
530   if (shutdown_on_io_)
531     return;
532   origins_with_pending_changes_.insert(origin);
533   ScheduleNotifyChangesUpdatedOnIOThread();
534 }
535 
AddOriginChangeObserver(LocalOriginChangeObserver * observer)536 void LocalFileSyncContext::AddOriginChangeObserver(
537     LocalOriginChangeObserver* observer) {
538   origin_change_observers_.AddObserver(observer);
539 }
540 
RemoveOriginChangeObserver(LocalOriginChangeObserver * observer)541 void LocalFileSyncContext::RemoveOriginChangeObserver(
542     LocalOriginChangeObserver* observer) {
543   origin_change_observers_.RemoveObserver(observer);
544 }
545 
546 base::WeakPtr<SyncableFileOperationRunner>
operation_runner() const547 LocalFileSyncContext::operation_runner() const {
548   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
549   if (operation_runner_)
550     return operation_runner_->AsWeakPtr();
551   return base::WeakPtr<SyncableFileOperationRunner>();
552 }
553 
sync_status() const554 LocalFileSyncStatus* LocalFileSyncContext::sync_status() const {
555   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
556   return sync_status_.get();
557 }
558 
OnSyncEnabled(const FileSystemURL & url)559 void LocalFileSyncContext::OnSyncEnabled(const FileSystemURL& url) {
560   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
561   if (shutdown_on_io_)
562     return;
563   UpdateChangesForOrigin(url.origin());
564   if (url_syncable_callback_.is_null() ||
565       sync_status()->IsWriting(url_waiting_sync_on_io_)) {
566     return;
567   }
568   // TODO(kinuko): may want to check how many pending tasks we have.
569   ui_task_runner_->PostTask(FROM_HERE, url_syncable_callback_);
570   url_syncable_callback_.Reset();
571 }
572 
OnWriteEnabled(const FileSystemURL & url)573 void LocalFileSyncContext::OnWriteEnabled(const FileSystemURL& url) {
574   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
575   // Nothing to do for now.
576 }
577 
~LocalFileSyncContext()578 LocalFileSyncContext::~LocalFileSyncContext() {
579 }
580 
ScheduleNotifyChangesUpdatedOnIOThread()581 void LocalFileSyncContext::ScheduleNotifyChangesUpdatedOnIOThread() {
582   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
583   if (shutdown_on_io_)
584     return;
585   if (base::Time::Now() > last_notified_changes_ + NotifyChangesDuration()) {
586     NotifyAvailableChangesOnIOThread();
587   } else if (!timer_on_io_->IsRunning()) {
588     timer_on_io_->Start(
589         FROM_HERE, NotifyChangesDuration(), this,
590         &LocalFileSyncContext::NotifyAvailableChangesOnIOThread);
591   }
592 }
593 
NotifyAvailableChangesOnIOThread()594 void LocalFileSyncContext::NotifyAvailableChangesOnIOThread() {
595   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
596   if (shutdown_on_io_)
597     return;
598   ui_task_runner_->PostTask(
599       FROM_HERE,
600       base::Bind(&LocalFileSyncContext::NotifyAvailableChanges,
601                  this, origins_with_pending_changes_));
602   last_notified_changes_ = base::Time::Now();
603   origins_with_pending_changes_.clear();
604 }
605 
NotifyAvailableChanges(const std::set<GURL> & origins)606 void LocalFileSyncContext::NotifyAvailableChanges(
607     const std::set<GURL>& origins) {
608   FOR_EACH_OBSERVER(LocalOriginChangeObserver, origin_change_observers_,
609                     OnChangesAvailableInOrigins(origins));
610 }
611 
ShutdownOnIOThread()612 void LocalFileSyncContext::ShutdownOnIOThread() {
613   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
614   shutdown_on_io_ = true;
615   operation_runner_.reset();
616   root_delete_helper_.reset();
617   sync_status_.reset();
618   timer_on_io_.reset();
619 }
620 
InitializeFileSystemContextOnIOThread(const GURL & source_url,FileSystemContext * file_system_context,const GURL &,const std::string &,base::File::Error error)621 void LocalFileSyncContext::InitializeFileSystemContextOnIOThread(
622     const GURL& source_url,
623     FileSystemContext* file_system_context,
624     const GURL& /* root */,
625     const std::string& /* name */,
626     base::File::Error error) {
627   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
628   if (shutdown_on_io_)
629     error = base::File::FILE_ERROR_ABORT;
630   if (error != base::File::FILE_OK) {
631     DidInitialize(source_url, file_system_context,
632                   FileErrorToSyncStatusCode(error));
633     return;
634   }
635   DCHECK(file_system_context);
636   SyncFileSystemBackend* backend =
637       SyncFileSystemBackend::GetBackend(file_system_context);
638   DCHECK(backend);
639   if (!backend->change_tracker()) {
640     // Create and initialize LocalFileChangeTracker and call back this method
641     // later again.
642     std::set<GURL>* origins_with_changes = new std::set<GURL>;
643     scoped_ptr<LocalFileChangeTracker>* tracker_ptr(
644         new scoped_ptr<LocalFileChangeTracker>);
645     base::PostTaskAndReplyWithResult(
646         file_system_context->default_file_task_runner(),
647         FROM_HERE,
648         base::Bind(&LocalFileSyncContext::InitializeChangeTrackerOnFileThread,
649                    this, tracker_ptr,
650                    make_scoped_refptr(file_system_context),
651                    origins_with_changes),
652         base::Bind(&LocalFileSyncContext::DidInitializeChangeTrackerOnIOThread,
653                    this, base::Owned(tracker_ptr),
654                    source_url,
655                    make_scoped_refptr(file_system_context),
656                    base::Owned(origins_with_changes)));
657     return;
658   }
659   if (!operation_runner_) {
660     DCHECK(!sync_status_);
661     DCHECK(!timer_on_io_);
662     sync_status_.reset(new LocalFileSyncStatus);
663     timer_on_io_.reset(new base::OneShotTimer<LocalFileSyncContext>);
664     operation_runner_.reset(new SyncableFileOperationRunner(
665             kMaxConcurrentSyncableOperation,
666             sync_status_.get()));
667     sync_status_->AddObserver(this);
668   }
669   backend->set_sync_context(this);
670   DidInitialize(source_url, file_system_context,
671                 SYNC_STATUS_OK);
672 }
673 
InitializeChangeTrackerOnFileThread(scoped_ptr<LocalFileChangeTracker> * tracker_ptr,FileSystemContext * file_system_context,std::set<GURL> * origins_with_changes)674 SyncStatusCode LocalFileSyncContext::InitializeChangeTrackerOnFileThread(
675     scoped_ptr<LocalFileChangeTracker>* tracker_ptr,
676     FileSystemContext* file_system_context,
677     std::set<GURL>* origins_with_changes) {
678   DCHECK(file_system_context);
679   DCHECK(tracker_ptr);
680   DCHECK(origins_with_changes);
681   tracker_ptr->reset(new LocalFileChangeTracker(
682           file_system_context->partition_path(),
683           env_override_,
684           file_system_context->default_file_task_runner()));
685   const SyncStatusCode status = (*tracker_ptr)->Initialize(file_system_context);
686   if (status != SYNC_STATUS_OK)
687     return status;
688 
689   // Get all origins that have pending changes.
690   std::deque<FileSystemURL> urls;
691   (*tracker_ptr)->GetNextChangedURLs(&urls, 0);
692   for (std::deque<FileSystemURL>::iterator iter = urls.begin();
693        iter != urls.end(); ++iter) {
694     origins_with_changes->insert(iter->origin());
695   }
696 
697   // Creates snapshot directory.
698   base::CreateDirectory(local_base_path_.Append(kSnapshotDir));
699 
700   return status;
701 }
702 
DidInitializeChangeTrackerOnIOThread(scoped_ptr<LocalFileChangeTracker> * tracker_ptr,const GURL & source_url,FileSystemContext * file_system_context,std::set<GURL> * origins_with_changes,SyncStatusCode status)703 void LocalFileSyncContext::DidInitializeChangeTrackerOnIOThread(
704     scoped_ptr<LocalFileChangeTracker>* tracker_ptr,
705     const GURL& source_url,
706     FileSystemContext* file_system_context,
707     std::set<GURL>* origins_with_changes,
708     SyncStatusCode status) {
709   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
710   DCHECK(file_system_context);
711   DCHECK(origins_with_changes);
712   if (shutdown_on_io_)
713     status = SYNC_STATUS_ABORT;
714   if (status != SYNC_STATUS_OK) {
715     DidInitialize(source_url, file_system_context, status);
716     return;
717   }
718 
719   SyncFileSystemBackend* backend =
720       SyncFileSystemBackend::GetBackend(file_system_context);
721   DCHECK(backend);
722   backend->SetLocalFileChangeTracker(tracker_ptr->Pass());
723 
724   origins_with_pending_changes_.insert(origins_with_changes->begin(),
725                                        origins_with_changes->end());
726   ScheduleNotifyChangesUpdatedOnIOThread();
727 
728   InitializeFileSystemContextOnIOThread(source_url, file_system_context,
729                                         GURL(), std::string(),
730                                         base::File::FILE_OK);
731 }
732 
DidInitialize(const GURL & source_url,FileSystemContext * file_system_context,SyncStatusCode status)733 void LocalFileSyncContext::DidInitialize(
734     const GURL& source_url,
735     FileSystemContext* file_system_context,
736     SyncStatusCode status) {
737   if (!ui_task_runner_->RunsTasksOnCurrentThread()) {
738     ui_task_runner_->PostTask(
739         FROM_HERE,
740         base::Bind(&LocalFileSyncContext::DidInitialize,
741                    this, source_url,
742                    make_scoped_refptr(file_system_context), status));
743     return;
744   }
745   DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
746   DCHECK(!ContainsKey(file_system_contexts_, file_system_context));
747   DCHECK(ContainsKey(pending_initialize_callbacks_, file_system_context));
748 
749   SyncFileSystemBackend* backend =
750       SyncFileSystemBackend::GetBackend(file_system_context);
751   DCHECK(backend);
752   DCHECK(backend->change_tracker());
753 
754   file_system_contexts_.insert(file_system_context);
755 
756   StatusCallbackQueue& callback_queue =
757       pending_initialize_callbacks_[file_system_context];
758   for (StatusCallbackQueue::iterator iter = callback_queue.begin();
759        iter != callback_queue.end(); ++iter) {
760     ui_task_runner_->PostTask(FROM_HERE, base::Bind(*iter, status));
761   }
762   pending_initialize_callbacks_.erase(file_system_context);
763 }
764 
GetNextURLsForSyncOnFileThread(FileSystemContext * file_system_context,std::deque<FileSystemURL> * urls)765 void LocalFileSyncContext::GetNextURLsForSyncOnFileThread(
766     FileSystemContext* file_system_context,
767     std::deque<FileSystemURL>* urls) {
768   DCHECK(file_system_context);
769   DCHECK(file_system_context->default_file_task_runner()->
770              RunsTasksOnCurrentThread());
771   SyncFileSystemBackend* backend =
772       SyncFileSystemBackend::GetBackend(file_system_context);
773   DCHECK(backend);
774   DCHECK(backend->change_tracker());
775   backend->change_tracker()->GetNextChangedURLs(
776       urls, kMaxURLsToFetchForLocalSync);
777 }
778 
TryPrepareForLocalSync(FileSystemContext * file_system_context,std::deque<FileSystemURL> * urls,const LocalFileSyncInfoCallback & callback)779 void LocalFileSyncContext::TryPrepareForLocalSync(
780     FileSystemContext* file_system_context,
781     std::deque<FileSystemURL>* urls,
782     const LocalFileSyncInfoCallback& callback) {
783   DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
784   DCHECK(urls);
785 
786   if (shutdown_on_ui_) {
787     callback.Run(SYNC_STATUS_ABORT, LocalFileSyncInfo(),
788                  webkit_blob::ScopedFile());
789     return;
790   }
791 
792   if (urls->empty()) {
793     callback.Run(SYNC_STATUS_NO_CHANGE_TO_SYNC, LocalFileSyncInfo(),
794                  webkit_blob::ScopedFile());
795     return;
796   }
797 
798   const FileSystemURL url = urls->front();
799   urls->pop_front();
800   std::deque<FileSystemURL>* remaining = new std::deque<FileSystemURL>;
801   remaining->swap(*urls);
802 
803   PrepareForSync(
804       file_system_context, url, SYNC_SNAPSHOT,
805       base::Bind(&LocalFileSyncContext::DidTryPrepareForLocalSync,
806                  this, make_scoped_refptr(file_system_context),
807                  base::Owned(remaining), callback));
808 }
809 
DidTryPrepareForLocalSync(FileSystemContext * file_system_context,std::deque<FileSystemURL> * remaining_urls,const LocalFileSyncInfoCallback & callback,SyncStatusCode status,const LocalFileSyncInfo & sync_file_info,webkit_blob::ScopedFile snapshot)810 void LocalFileSyncContext::DidTryPrepareForLocalSync(
811     FileSystemContext* file_system_context,
812     std::deque<FileSystemURL>* remaining_urls,
813     const LocalFileSyncInfoCallback& callback,
814     SyncStatusCode status,
815     const LocalFileSyncInfo& sync_file_info,
816     webkit_blob::ScopedFile snapshot) {
817   DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
818   if (status != SYNC_STATUS_FILE_BUSY) {
819     callback.Run(status, sync_file_info, snapshot.Pass());
820     return;
821   }
822   // Recursively call TryPrepareForLocalSync with remaining_urls.
823   TryPrepareForLocalSync(file_system_context, remaining_urls, callback);
824 }
825 
DidGetWritingStatusForSync(FileSystemContext * file_system_context,SyncStatusCode status,const FileSystemURL & url,SyncMode sync_mode,const LocalFileSyncInfoCallback & callback)826 void LocalFileSyncContext::DidGetWritingStatusForSync(
827     FileSystemContext* file_system_context,
828     SyncStatusCode status,
829     const FileSystemURL& url,
830     SyncMode sync_mode,
831     const LocalFileSyncInfoCallback& callback) {
832   // This gets called on UI thread and relays the task on FILE thread.
833   DCHECK(file_system_context);
834   if (!file_system_context->default_file_task_runner()->
835           RunsTasksOnCurrentThread()) {
836     DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
837     if (shutdown_on_ui_) {
838       callback.Run(SYNC_STATUS_ABORT, LocalFileSyncInfo(),
839                    webkit_blob::ScopedFile());
840       return;
841     }
842     file_system_context->default_file_task_runner()->PostTask(
843         FROM_HERE,
844         base::Bind(&LocalFileSyncContext::DidGetWritingStatusForSync,
845                    this, make_scoped_refptr(file_system_context),
846                    status, url, sync_mode, callback));
847     return;
848   }
849 
850   SyncFileSystemBackend* backend =
851       SyncFileSystemBackend::GetBackend(file_system_context);
852   DCHECK(backend);
853   DCHECK(backend->change_tracker());
854   FileChangeList changes;
855   backend->change_tracker()->GetChangesForURL(url, &changes);
856 
857   base::FilePath platform_path;
858   base::File::Info file_info;
859   FileSystemFileUtil* file_util =
860       file_system_context->sandbox_delegate()->sync_file_util();
861   DCHECK(file_util);
862 
863   base::File::Error file_error = file_util->GetFileInfo(
864       make_scoped_ptr(
865           new FileSystemOperationContext(file_system_context)).get(),
866       url,
867       &file_info,
868       &platform_path);
869 
870   webkit_blob::ScopedFile snapshot;
871   if (file_error == base::File::FILE_OK && sync_mode == SYNC_SNAPSHOT) {
872     base::FilePath snapshot_path;
873     base::CreateTemporaryFileInDir(local_base_path_.Append(kSnapshotDir),
874                                    &snapshot_path);
875     if (base::CopyFile(platform_path, snapshot_path)) {
876       platform_path = snapshot_path;
877       snapshot = webkit_blob::ScopedFile(
878           snapshot_path,
879           webkit_blob::ScopedFile::DELETE_ON_SCOPE_OUT,
880           file_system_context->default_file_task_runner());
881     }
882   }
883 
884   if (status == SYNC_STATUS_OK &&
885       file_error != base::File::FILE_OK &&
886       file_error != base::File::FILE_ERROR_NOT_FOUND) {
887     status = FileErrorToSyncStatusCode(file_error);
888 }
889 
890   DCHECK(!file_info.is_symbolic_link);
891 
892   SyncFileType file_type = SYNC_FILE_TYPE_FILE;
893   if (file_error == base::File::FILE_ERROR_NOT_FOUND)
894     file_type = SYNC_FILE_TYPE_UNKNOWN;
895   else if (file_info.is_directory)
896     file_type = SYNC_FILE_TYPE_DIRECTORY;
897 
898   LocalFileSyncInfo sync_file_info;
899   sync_file_info.url = url;
900   sync_file_info.local_file_path = platform_path;
901   sync_file_info.metadata.file_type = file_type;
902   sync_file_info.metadata.size = file_info.size;
903   sync_file_info.metadata.last_modified = file_info.last_modified;
904   sync_file_info.changes = changes;
905 
906   if (status == SYNC_STATUS_OK && sync_mode == SYNC_SNAPSHOT) {
907     if (!changes.empty()) {
908       // Now we create an empty mirror change record for URL (and we record
909       // changes to both mirror and original records during sync), so that
910       // we can reset to the mirror when the sync succeeds.
911       backend->change_tracker()->CreateFreshMirrorForURL(url);
912     }
913 
914     // 'Unlock' the file for snapshot sync.
915     // (But keep it in writing status so that no other sync starts on
916     // the same URL)
917     io_task_runner_->PostTask(
918         FROM_HERE,
919         base::Bind(&LocalFileSyncContext::ClearSyncFlagOnIOThread,
920                    this, url, true /* for_snapshot_sync */));
921   }
922 
923   ui_task_runner_->PostTask(FROM_HERE,
924                             base::Bind(callback, status, sync_file_info,
925                                        base::Passed(&snapshot)));
926 }
927 
ClearSyncFlagOnIOThread(const FileSystemURL & url,bool for_snapshot_sync)928 void LocalFileSyncContext::ClearSyncFlagOnIOThread(
929     const FileSystemURL& url,
930     bool for_snapshot_sync) {
931   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
932   if (shutdown_on_io_)
933     return;
934   sync_status()->EndSyncing(url);
935 
936   if (for_snapshot_sync) {
937     // The caller will hold shared lock on this one.
938     sync_status()->StartWriting(url);
939     return;
940   }
941 
942   // Since a sync has finished the number of changes must have been updated.
943   UpdateChangesForOrigin(url.origin());
944 }
945 
FinalizeSnapshotSyncOnIOThread(const FileSystemURL & url)946 void LocalFileSyncContext::FinalizeSnapshotSyncOnIOThread(
947     const FileSystemURL& url) {
948   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
949   if (shutdown_on_io_)
950     return;
951   sync_status()->EndWriting(url);
952 
953   // Since a sync has finished the number of changes must have been updated.
954   UpdateChangesForOrigin(url.origin());
955 }
956 
DidApplyRemoteChange(const FileSystemURL & url,const SyncStatusCallback & callback_on_ui,base::File::Error file_error)957 void LocalFileSyncContext::DidApplyRemoteChange(
958     const FileSystemURL& url,
959     const SyncStatusCallback& callback_on_ui,
960     base::File::Error file_error) {
961   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
962   root_delete_helper_.reset();
963   ui_task_runner_->PostTask(
964       FROM_HERE,
965       base::Bind(callback_on_ui, FileErrorToSyncStatusCode(file_error)));
966 }
967 
DidGetFileMetadata(const SyncFileMetadataCallback & callback,base::File::Error file_error,const base::File::Info & file_info)968 void LocalFileSyncContext::DidGetFileMetadata(
969     const SyncFileMetadataCallback& callback,
970     base::File::Error file_error,
971     const base::File::Info& file_info) {
972   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
973   SyncFileMetadata metadata;
974   if (file_error == base::File::FILE_OK) {
975     metadata.file_type = file_info.is_directory ?
976         SYNC_FILE_TYPE_DIRECTORY : SYNC_FILE_TYPE_FILE;
977     metadata.size = file_info.size;
978     metadata.last_modified = file_info.last_modified;
979   }
980   ui_task_runner_->PostTask(
981       FROM_HERE,
982       base::Bind(callback, FileErrorToSyncStatusCode(file_error), metadata));
983 }
984 
NotifyChangesDuration()985 base::TimeDelta LocalFileSyncContext::NotifyChangesDuration() {
986   if (mock_notify_changes_duration_in_sec_ >= 0)
987     return base::TimeDelta::FromSeconds(mock_notify_changes_duration_in_sec_);
988   return base::TimeDelta::FromSeconds(kNotifyChangesDurationInSec);
989 }
990 
DidCreateDirectoryForCopyIn(FileSystemContext * file_system_context,const base::FilePath & local_path,const FileSystemURL & dest_url,const StatusCallback & callback,base::File::Error error)991 void LocalFileSyncContext::DidCreateDirectoryForCopyIn(
992     FileSystemContext* file_system_context,
993     const base::FilePath& local_path,
994     const FileSystemURL& dest_url,
995     const StatusCallback& callback,
996     base::File::Error error) {
997   if (error != base::File::FILE_OK) {
998     callback.Run(error);
999     return;
1000   }
1001 
1002   FileSystemURL url_for_sync = CreateSyncableFileSystemURLForSync(
1003       file_system_context, dest_url);
1004   file_system_context->operation_runner()->CopyInForeignFile(
1005       local_path, url_for_sync, callback);
1006 }
1007 
1008 }  // namespace sync_file_system
1009