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/local/local_file_sync_context.h"
8 #include "base/file_util.h"
9 #include "base/location.h"
10 #include "base/platform_file.h"
11 #include "base/single_thread_task_runner.h"
12 #include "base/stl_util.h"
13 #include "base/task_runner_util.h"
14 #include "chrome/browser/sync_file_system/file_change.h"
15 #include "chrome/browser/sync_file_system/local/local_file_change_tracker.h"
16 #include "chrome/browser/sync_file_system/local/local_origin_change_observer.h"
17 #include "chrome/browser/sync_file_system/local/root_delete_helper.h"
18 #include "chrome/browser/sync_file_system/local/sync_file_system_backend.h"
19 #include "chrome/browser/sync_file_system/local/syncable_file_operation_runner.h"
20 #include "chrome/browser/sync_file_system/logger.h"
21 #include "chrome/browser/sync_file_system/sync_file_metadata.h"
22 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
23 #include "webkit/browser/fileapi/file_system_context.h"
24 #include "webkit/browser/fileapi/file_system_file_util.h"
25 #include "webkit/browser/fileapi/file_system_operation_context.h"
26 #include "webkit/browser/fileapi/file_system_operation_runner.h"
27 #include "webkit/common/blob/scoped_file.h"
28 #include "webkit/common/fileapi/file_system_util.h"
30 using fileapi::FileSystemContext
;
31 using fileapi::FileSystemFileUtil
;
32 using fileapi::FileSystemOperation
;
33 using fileapi::FileSystemOperationContext
;
34 using fileapi::FileSystemURL
;
36 namespace sync_file_system
{
40 const int kMaxConcurrentSyncableOperation
= 3;
41 const int kNotifyChangesDurationInSec
= 1;
42 const int kMaxURLsToFetchForLocalSync
= 5;
44 const base::FilePath::CharType kSnapshotDir
[] = FILE_PATH_LITERAL("snapshots");
48 LocalFileSyncContext::LocalFileSyncContext(
49 const base::FilePath
& base_path
,
50 base::SingleThreadTaskRunner
* ui_task_runner
,
51 base::SingleThreadTaskRunner
* io_task_runner
)
52 : local_base_path_(base_path
.Append(FILE_PATH_LITERAL("local"))),
53 ui_task_runner_(ui_task_runner
),
54 io_task_runner_(io_task_runner
),
55 shutdown_on_ui_(false),
56 shutdown_on_io_(false),
57 mock_notify_changes_duration_in_sec_(-1) {
58 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
61 void LocalFileSyncContext::MaybeInitializeFileSystemContext(
62 const GURL
& source_url
,
63 FileSystemContext
* file_system_context
,
64 const SyncStatusCallback
& callback
) {
65 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
66 if (ContainsKey(file_system_contexts_
, file_system_context
)) {
67 // The context has been already initialized. Just dispatch the callback
68 // with SYNC_STATUS_OK.
69 ui_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, SYNC_STATUS_OK
));
73 StatusCallbackQueue
& callback_queue
=
74 pending_initialize_callbacks_
[file_system_context
];
75 callback_queue
.push_back(callback
);
76 if (callback_queue
.size() > 1)
79 // The sync service always expects the origin (app) is initialized
80 // for writable way (even when MaybeInitializeFileSystemContext is called
81 // from read-only OpenFileSystem), so open the filesystem with
82 // CREATE_IF_NONEXISTENT here.
83 fileapi::FileSystemBackend::OpenFileSystemCallback open_filesystem_callback
=
84 base::Bind(&LocalFileSyncContext::InitializeFileSystemContextOnIOThread
,
85 this, source_url
, make_scoped_refptr(file_system_context
));
86 io_task_runner_
->PostTask(
88 base::Bind(&fileapi::SandboxFileSystemBackendDelegate::OpenFileSystem
,
89 base::Unretained(file_system_context
->sandbox_delegate()),
90 source_url
, fileapi::kFileSystemTypeSyncable
,
91 fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT
,
92 open_filesystem_callback
, GURL()));
95 void LocalFileSyncContext::ShutdownOnUIThread() {
96 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
97 shutdown_on_ui_
= true;
98 io_task_runner_
->PostTask(
100 base::Bind(&LocalFileSyncContext::ShutdownOnIOThread
, this));
103 void LocalFileSyncContext::GetFileForLocalSync(
104 FileSystemContext
* file_system_context
,
105 const LocalFileSyncInfoCallback
& callback
) {
106 DCHECK(file_system_context
);
107 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
109 std::deque
<FileSystemURL
>* urls
= new std::deque
<FileSystemURL
>;
110 file_system_context
->default_file_task_runner()->PostTaskAndReply(
112 base::Bind(&LocalFileSyncContext::GetNextURLsForSyncOnFileThread
,
113 this, make_scoped_refptr(file_system_context
),
114 base::Unretained(urls
)),
115 base::Bind(&LocalFileSyncContext::TryPrepareForLocalSync
,
116 this, make_scoped_refptr(file_system_context
),
117 base::Owned(urls
), callback
));
120 void LocalFileSyncContext::ClearChangesForURL(
121 FileSystemContext
* file_system_context
,
122 const FileSystemURL
& url
,
123 const base::Closure
& done_callback
) {
124 // This is initially called on UI thread and to be relayed to FILE thread.
125 DCHECK(file_system_context
);
126 if (!file_system_context
->default_file_task_runner()->
127 RunsTasksOnCurrentThread()) {
128 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
129 file_system_context
->default_file_task_runner()->PostTask(
131 base::Bind(&LocalFileSyncContext::ClearChangesForURL
,
132 this, make_scoped_refptr(file_system_context
),
133 url
, done_callback
));
137 SyncFileSystemBackend
* backend
=
138 SyncFileSystemBackend::GetBackend(file_system_context
);
140 DCHECK(backend
->change_tracker());
141 backend
->change_tracker()->ClearChangesForURL(url
);
143 // Call the completion callback on UI thread.
144 ui_task_runner_
->PostTask(FROM_HERE
, done_callback
);
147 void LocalFileSyncContext::FinalizeSnapshotSync(
148 fileapi::FileSystemContext
* file_system_context
,
149 const fileapi::FileSystemURL
& url
,
150 SyncStatusCode sync_finish_status
,
151 const base::Closure
& done_callback
) {
152 DCHECK(file_system_context
);
153 DCHECK(url
.is_valid());
154 if (!file_system_context
->default_file_task_runner()->
155 RunsTasksOnCurrentThread()) {
156 file_system_context
->default_file_task_runner()->PostTask(
158 base::Bind(&LocalFileSyncContext::FinalizeSnapshotSync
,
159 this, make_scoped_refptr(file_system_context
),
160 url
, sync_finish_status
, done_callback
));
164 SyncFileSystemBackend
* backend
=
165 SyncFileSystemBackend::GetBackend(file_system_context
);
167 DCHECK(backend
->change_tracker());
169 if (sync_finish_status
== SYNC_STATUS_OK
||
170 sync_finish_status
== SYNC_STATUS_HAS_CONFLICT
) {
171 // Commit the in-memory mirror change.
172 backend
->change_tracker()->ResetToMirrorAndCommitChangesForURL(url
);
174 // Abort in-memory mirror change.
175 backend
->change_tracker()->RemoveMirrorAndCommitChangesForURL(url
);
176 if (sync_finish_status
== SYNC_STATUS_FILE_BUSY
)
177 backend
->change_tracker()->DemoteChangesForURL(url
);
180 // We've been keeping it in writing mode, so clear the writing counter
181 // to unblock sync activities.
182 io_task_runner_
->PostTask(
183 FROM_HERE
, base::Bind(
184 &LocalFileSyncContext::FinalizeSnapshotSyncOnIOThread
, this, url
));
186 // Call the completion callback on UI thread.
187 ui_task_runner_
->PostTask(FROM_HERE
, done_callback
);
190 void LocalFileSyncContext::FinalizeExclusiveSync(
191 fileapi::FileSystemContext
* file_system_context
,
192 const fileapi::FileSystemURL
& url
,
193 bool clear_local_changes
,
194 const base::Closure
& done_callback
) {
195 DCHECK(file_system_context
);
196 if (!url
.is_valid()) {
201 if (clear_local_changes
) {
202 ClearChangesForURL(file_system_context
, url
,
203 base::Bind(&LocalFileSyncContext::FinalizeExclusiveSync
,
204 this, make_scoped_refptr(file_system_context
),
205 url
, false, done_callback
));
209 io_task_runner_
->PostTask(
211 base::Bind(&LocalFileSyncContext::ClearSyncFlagOnIOThread
,
212 this, url
, false /* for_snapshot_sync */));
217 void LocalFileSyncContext::PrepareForSync(
218 FileSystemContext
* file_system_context
,
219 const FileSystemURL
& url
,
221 const LocalFileSyncInfoCallback
& callback
) {
222 // This is initially called on UI thread and to be relayed to IO thread.
223 if (!io_task_runner_
->RunsTasksOnCurrentThread()) {
224 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
225 io_task_runner_
->PostTask(
227 base::Bind(&LocalFileSyncContext::PrepareForSync
, this,
228 make_scoped_refptr(file_system_context
), url
,
229 sync_mode
, callback
));
232 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
233 const bool syncable
= sync_status()->IsSyncable(url
);
234 // Disable writing if it's ready to be synced.
236 sync_status()->StartSyncing(url
);
237 ui_task_runner_
->PostTask(
239 base::Bind(&LocalFileSyncContext::DidGetWritingStatusForSync
,
240 this, make_scoped_refptr(file_system_context
),
241 syncable
? SYNC_STATUS_OK
:
242 SYNC_STATUS_FILE_BUSY
,
243 url
, sync_mode
, callback
));
246 void LocalFileSyncContext::RegisterURLForWaitingSync(
247 const FileSystemURL
& url
,
248 const base::Closure
& on_syncable_callback
) {
249 // This is initially called on UI thread and to be relayed to IO thread.
250 if (!io_task_runner_
->RunsTasksOnCurrentThread()) {
251 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
252 io_task_runner_
->PostTask(
254 base::Bind(&LocalFileSyncContext::RegisterURLForWaitingSync
,
255 this, url
, on_syncable_callback
));
258 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
261 if (sync_status()->IsSyncable(url
)) {
262 // No need to register; fire the callback now.
263 ui_task_runner_
->PostTask(FROM_HERE
, on_syncable_callback
);
266 url_waiting_sync_on_io_
= url
;
267 url_syncable_callback_
= on_syncable_callback
;
270 void LocalFileSyncContext::ApplyRemoteChange(
271 FileSystemContext
* file_system_context
,
272 const FileChange
& change
,
273 const base::FilePath
& local_path
,
274 const FileSystemURL
& url
,
275 const SyncStatusCallback
& callback
) {
276 if (!io_task_runner_
->RunsTasksOnCurrentThread()) {
277 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
278 io_task_runner_
->PostTask(
280 base::Bind(&LocalFileSyncContext::ApplyRemoteChange
, this,
281 make_scoped_refptr(file_system_context
),
282 change
, local_path
, url
, callback
));
285 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
286 DCHECK(!sync_status()->IsWritable(url
));
287 DCHECK(!sync_status()->IsWriting(url
));
289 FileSystemOperation::StatusCallback operation_callback
;
290 switch (change
.change()) {
291 case FileChange::FILE_CHANGE_DELETE
:
292 HandleRemoteDelete(file_system_context
, url
, callback
);
294 case FileChange::FILE_CHANGE_ADD_OR_UPDATE
:
295 HandleRemoteAddOrUpdate(
296 file_system_context
, change
, local_path
, url
, callback
);
300 callback
.Run(SYNC_STATUS_FAILED
);
303 void LocalFileSyncContext::HandleRemoteDelete(
304 FileSystemContext
* file_system_context
,
305 const FileSystemURL
& url
,
306 const SyncStatusCallback
& callback
) {
307 FileSystemURL url_for_sync
= CreateSyncableFileSystemURLForSync(
308 file_system_context
, url
);
310 // Handle root directory case differently.
311 if (fileapi::VirtualPath::IsRootPath(url
.path())) {
312 DCHECK(!root_delete_helper_
);
313 root_delete_helper_
.reset(new RootDeleteHelper(
314 file_system_context
, sync_status(), url
,
315 base::Bind(&LocalFileSyncContext::DidApplyRemoteChange
,
316 this, url
, callback
)));
317 root_delete_helper_
->Run();
321 file_system_context
->operation_runner()->Remove(
322 url_for_sync
, true /* recursive */,
323 base::Bind(&LocalFileSyncContext::DidApplyRemoteChange
,
324 this, url
, callback
));
327 void LocalFileSyncContext::HandleRemoteAddOrUpdate(
328 FileSystemContext
* file_system_context
,
329 const FileChange
& change
,
330 const base::FilePath
& local_path
,
331 const FileSystemURL
& url
,
332 const SyncStatusCallback
& callback
) {
333 FileSystemURL url_for_sync
= CreateSyncableFileSystemURLForSync(
334 file_system_context
, url
);
336 if (fileapi::VirtualPath::IsRootPath(url
.path())) {
337 DidApplyRemoteChange(url
, callback
, base::PLATFORM_FILE_OK
);
341 file_system_context
->operation_runner()->Remove(
342 url_for_sync
, true /* recursive */,
344 &LocalFileSyncContext::DidRemoveExistingEntryForRemoteAddOrUpdate
,
346 make_scoped_refptr(file_system_context
),
353 void LocalFileSyncContext::DidRemoveExistingEntryForRemoteAddOrUpdate(
354 FileSystemContext
* file_system_context
,
355 const FileChange
& change
,
356 const base::FilePath
& local_path
,
357 const FileSystemURL
& url
,
358 const SyncStatusCallback
& callback
,
359 base::PlatformFileError error
) {
360 // Remove() may fail if the target entry does not exist (which is ok),
361 // so we ignore |error| here.
363 if (shutdown_on_io_
) {
364 callback
.Run(SYNC_FILE_ERROR_ABORT
);
368 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
369 DCHECK(!sync_status()->IsWritable(url
));
370 DCHECK(!sync_status()->IsWriting(url
));
372 FileSystemURL url_for_sync
= CreateSyncableFileSystemURLForSync(
373 file_system_context
, url
);
374 FileSystemOperation::StatusCallback operation_callback
= base::Bind(
375 &LocalFileSyncContext::DidApplyRemoteChange
, this, url
, callback
);
377 DCHECK_EQ(FileChange::FILE_CHANGE_ADD_OR_UPDATE
, change
.change());
378 switch (change
.file_type()) {
379 case SYNC_FILE_TYPE_FILE
: {
380 DCHECK(!local_path
.empty());
381 base::FilePath dir_path
= fileapi::VirtualPath::DirName(url
.path());
382 if (dir_path
.empty() ||
383 fileapi::VirtualPath::DirName(dir_path
) == dir_path
) {
384 // Copying into the root directory.
385 file_system_context
->operation_runner()->CopyInForeignFile(
386 local_path
, url_for_sync
, operation_callback
);
388 FileSystemURL dir_url
= file_system_context
->CreateCrackedFileSystemURL(
389 url_for_sync
.origin(),
390 url_for_sync
.mount_type(),
391 fileapi::VirtualPath::DirName(url_for_sync
.virtual_path()));
392 file_system_context
->operation_runner()->CreateDirectory(
394 false /* exclusive */,
395 true /* recursive */,
396 base::Bind(&LocalFileSyncContext::DidCreateDirectoryForCopyIn
,
398 make_scoped_refptr(file_system_context
),
401 operation_callback
));
405 case SYNC_FILE_TYPE_DIRECTORY
:
406 file_system_context
->operation_runner()->CreateDirectory(
407 url_for_sync
, false /* exclusive */, true /* recursive */,
410 case SYNC_FILE_TYPE_UNKNOWN
:
411 NOTREACHED() << "File type unknown for ADD_OR_UPDATE change";
415 void LocalFileSyncContext::RecordFakeLocalChange(
416 FileSystemContext
* file_system_context
,
417 const FileSystemURL
& url
,
418 const FileChange
& change
,
419 const SyncStatusCallback
& callback
) {
420 // This is called on UI thread and to be relayed to FILE thread.
421 DCHECK(file_system_context
);
422 if (!file_system_context
->default_file_task_runner()->
423 RunsTasksOnCurrentThread()) {
424 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
425 file_system_context
->default_file_task_runner()->PostTask(
427 base::Bind(&LocalFileSyncContext::RecordFakeLocalChange
,
428 this, make_scoped_refptr(file_system_context
),
429 url
, change
, callback
));
433 SyncFileSystemBackend
* backend
=
434 SyncFileSystemBackend::GetBackend(file_system_context
);
436 DCHECK(backend
->change_tracker());
437 backend
->change_tracker()->MarkDirtyOnDatabase(url
);
438 backend
->change_tracker()->RecordChange(url
, change
);
440 // Fire the callback on UI thread.
441 ui_task_runner_
->PostTask(FROM_HERE
,
446 void LocalFileSyncContext::GetFileMetadata(
447 FileSystemContext
* file_system_context
,
448 const FileSystemURL
& url
,
449 const SyncFileMetadataCallback
& callback
) {
450 // This is initially called on UI thread and to be relayed to IO thread.
451 if (!io_task_runner_
->RunsTasksOnCurrentThread()) {
452 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
453 io_task_runner_
->PostTask(
455 base::Bind(&LocalFileSyncContext::GetFileMetadata
, this,
456 make_scoped_refptr(file_system_context
), url
, callback
));
459 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
461 FileSystemURL url_for_sync
= CreateSyncableFileSystemURLForSync(
462 file_system_context
, url
);
463 file_system_context
->operation_runner()->GetMetadata(
464 url_for_sync
, base::Bind(&LocalFileSyncContext::DidGetFileMetadata
,
468 void LocalFileSyncContext::HasPendingLocalChanges(
469 FileSystemContext
* file_system_context
,
470 const FileSystemURL
& url
,
471 const HasPendingLocalChangeCallback
& callback
) {
472 // This gets called on UI thread and relays the task on FILE thread.
473 DCHECK(file_system_context
);
474 if (!file_system_context
->default_file_task_runner()->
475 RunsTasksOnCurrentThread()) {
476 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
477 file_system_context
->default_file_task_runner()->PostTask(
479 base::Bind(&LocalFileSyncContext::HasPendingLocalChanges
,
480 this, make_scoped_refptr(file_system_context
),
485 SyncFileSystemBackend
* backend
=
486 SyncFileSystemBackend::GetBackend(file_system_context
);
488 DCHECK(backend
->change_tracker());
489 FileChangeList changes
;
490 backend
->change_tracker()->GetChangesForURL(url
, &changes
);
492 // Fire the callback on UI thread.
493 ui_task_runner_
->PostTask(FROM_HERE
,
499 void LocalFileSyncContext::PromoteDemotedChanges(
501 fileapi::FileSystemContext
* file_system_context
) {
502 // This is initially called on UI thread and to be relayed to FILE thread.
503 DCHECK(file_system_context
);
504 if (!file_system_context
->default_file_task_runner()->
505 RunsTasksOnCurrentThread()) {
506 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
507 file_system_context
->default_file_task_runner()->PostTask(
509 base::Bind(&LocalFileSyncContext::PromoteDemotedChanges
,
510 this, origin
, make_scoped_refptr(file_system_context
)));
514 SyncFileSystemBackend
* backend
=
515 SyncFileSystemBackend::GetBackend(file_system_context
);
517 DCHECK(backend
->change_tracker());
518 if (!backend
->change_tracker()->PromoteDemotedChanges())
521 io_task_runner_
->PostTask(
523 base::Bind(&LocalFileSyncContext::UpdateChangesForOrigin
,
527 void LocalFileSyncContext::UpdateChangesForOrigin(const GURL
& origin
) {
528 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
531 origins_with_pending_changes_
.insert(origin
);
532 ScheduleNotifyChangesUpdatedOnIOThread();
535 void LocalFileSyncContext::AddOriginChangeObserver(
536 LocalOriginChangeObserver
* observer
) {
537 origin_change_observers_
.AddObserver(observer
);
540 void LocalFileSyncContext::RemoveOriginChangeObserver(
541 LocalOriginChangeObserver
* observer
) {
542 origin_change_observers_
.RemoveObserver(observer
);
545 base::WeakPtr
<SyncableFileOperationRunner
>
546 LocalFileSyncContext::operation_runner() const {
547 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
548 if (operation_runner_
)
549 return operation_runner_
->AsWeakPtr();
550 return base::WeakPtr
<SyncableFileOperationRunner
>();
553 LocalFileSyncStatus
* LocalFileSyncContext::sync_status() const {
554 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
555 return sync_status_
.get();
558 void LocalFileSyncContext::OnSyncEnabled(const FileSystemURL
& url
) {
559 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
562 UpdateChangesForOrigin(url
.origin());
563 if (url_syncable_callback_
.is_null() ||
564 sync_status()->IsWriting(url_waiting_sync_on_io_
)) {
567 // TODO(kinuko): may want to check how many pending tasks we have.
568 ui_task_runner_
->PostTask(FROM_HERE
, url_syncable_callback_
);
569 url_syncable_callback_
.Reset();
572 void LocalFileSyncContext::OnWriteEnabled(const FileSystemURL
& url
) {
573 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
574 // Nothing to do for now.
577 LocalFileSyncContext::~LocalFileSyncContext() {
580 void LocalFileSyncContext::ScheduleNotifyChangesUpdatedOnIOThread() {
581 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
584 if (base::Time::Now() > last_notified_changes_
+ NotifyChangesDuration()) {
585 NotifyAvailableChangesOnIOThread();
586 } else if (!timer_on_io_
->IsRunning()) {
588 FROM_HERE
, NotifyChangesDuration(), this,
589 &LocalFileSyncContext::NotifyAvailableChangesOnIOThread
);
593 void LocalFileSyncContext::NotifyAvailableChangesOnIOThread() {
594 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
597 ui_task_runner_
->PostTask(
599 base::Bind(&LocalFileSyncContext::NotifyAvailableChanges
,
600 this, origins_with_pending_changes_
));
601 last_notified_changes_
= base::Time::Now();
602 origins_with_pending_changes_
.clear();
605 void LocalFileSyncContext::NotifyAvailableChanges(
606 const std::set
<GURL
>& origins
) {
607 FOR_EACH_OBSERVER(LocalOriginChangeObserver
, origin_change_observers_
,
608 OnChangesAvailableInOrigins(origins
));
611 void LocalFileSyncContext::ShutdownOnIOThread() {
612 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
613 shutdown_on_io_
= true;
614 operation_runner_
.reset();
615 root_delete_helper_
.reset();
616 sync_status_
.reset();
617 timer_on_io_
.reset();
620 void LocalFileSyncContext::InitializeFileSystemContextOnIOThread(
621 const GURL
& source_url
,
622 FileSystemContext
* file_system_context
,
623 const GURL
& /* root */,
624 const std::string
& /* name */,
625 base::PlatformFileError error
) {
626 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
628 error
= base::PLATFORM_FILE_ERROR_ABORT
;
629 if (error
!= base::PLATFORM_FILE_OK
) {
630 DidInitialize(source_url
, file_system_context
,
631 PlatformFileErrorToSyncStatusCode(error
));
634 DCHECK(file_system_context
);
635 SyncFileSystemBackend
* backend
=
636 SyncFileSystemBackend::GetBackend(file_system_context
);
638 if (!backend
->change_tracker()) {
639 // Create and initialize LocalFileChangeTracker and call back this method
641 std::set
<GURL
>* origins_with_changes
= new std::set
<GURL
>;
642 scoped_ptr
<LocalFileChangeTracker
>* tracker_ptr(
643 new scoped_ptr
<LocalFileChangeTracker
>);
644 base::PostTaskAndReplyWithResult(
645 file_system_context
->default_file_task_runner(),
647 base::Bind(&LocalFileSyncContext::InitializeChangeTrackerOnFileThread
,
649 make_scoped_refptr(file_system_context
),
650 origins_with_changes
),
651 base::Bind(&LocalFileSyncContext::DidInitializeChangeTrackerOnIOThread
,
652 this, base::Owned(tracker_ptr
),
654 make_scoped_refptr(file_system_context
),
655 base::Owned(origins_with_changes
)));
658 if (!operation_runner_
) {
659 DCHECK(!sync_status_
);
660 DCHECK(!timer_on_io_
);
661 sync_status_
.reset(new LocalFileSyncStatus
);
662 timer_on_io_
.reset(new base::OneShotTimer
<LocalFileSyncContext
>);
663 operation_runner_
.reset(new SyncableFileOperationRunner(
664 kMaxConcurrentSyncableOperation
,
665 sync_status_
.get()));
666 sync_status_
->AddObserver(this);
668 backend
->set_sync_context(this);
669 DidInitialize(source_url
, file_system_context
,
673 SyncStatusCode
LocalFileSyncContext::InitializeChangeTrackerOnFileThread(
674 scoped_ptr
<LocalFileChangeTracker
>* tracker_ptr
,
675 FileSystemContext
* file_system_context
,
676 std::set
<GURL
>* origins_with_changes
) {
677 DCHECK(file_system_context
);
679 DCHECK(origins_with_changes
);
680 tracker_ptr
->reset(new LocalFileChangeTracker(
681 file_system_context
->partition_path(),
682 file_system_context
->default_file_task_runner()));
683 const SyncStatusCode status
= (*tracker_ptr
)->Initialize(file_system_context
);
684 if (status
!= SYNC_STATUS_OK
)
687 // Get all origins that have pending changes.
688 std::deque
<FileSystemURL
> urls
;
689 (*tracker_ptr
)->GetNextChangedURLs(&urls
, 0);
690 for (std::deque
<FileSystemURL
>::iterator iter
= urls
.begin();
691 iter
!= urls
.end(); ++iter
) {
692 origins_with_changes
->insert(iter
->origin());
695 // Creates snapshot directory.
696 base::CreateDirectory(local_base_path_
.Append(kSnapshotDir
));
701 void LocalFileSyncContext::DidInitializeChangeTrackerOnIOThread(
702 scoped_ptr
<LocalFileChangeTracker
>* tracker_ptr
,
703 const GURL
& source_url
,
704 FileSystemContext
* file_system_context
,
705 std::set
<GURL
>* origins_with_changes
,
706 SyncStatusCode status
) {
707 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
708 DCHECK(file_system_context
);
709 DCHECK(origins_with_changes
);
711 status
= SYNC_STATUS_ABORT
;
712 if (status
!= SYNC_STATUS_OK
) {
713 DidInitialize(source_url
, file_system_context
, status
);
717 SyncFileSystemBackend
* backend
=
718 SyncFileSystemBackend::GetBackend(file_system_context
);
720 backend
->SetLocalFileChangeTracker(tracker_ptr
->Pass());
722 origins_with_pending_changes_
.insert(origins_with_changes
->begin(),
723 origins_with_changes
->end());
724 ScheduleNotifyChangesUpdatedOnIOThread();
726 InitializeFileSystemContextOnIOThread(source_url
, file_system_context
,
727 GURL(), std::string(),
728 base::PLATFORM_FILE_OK
);
731 void LocalFileSyncContext::DidInitialize(
732 const GURL
& source_url
,
733 FileSystemContext
* file_system_context
,
734 SyncStatusCode status
) {
735 if (!ui_task_runner_
->RunsTasksOnCurrentThread()) {
736 ui_task_runner_
->PostTask(
738 base::Bind(&LocalFileSyncContext::DidInitialize
,
740 make_scoped_refptr(file_system_context
), status
));
743 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
744 DCHECK(!ContainsKey(file_system_contexts_
, file_system_context
));
745 DCHECK(ContainsKey(pending_initialize_callbacks_
, file_system_context
));
747 SyncFileSystemBackend
* backend
=
748 SyncFileSystemBackend::GetBackend(file_system_context
);
750 DCHECK(backend
->change_tracker());
752 file_system_contexts_
.insert(file_system_context
);
754 StatusCallbackQueue
& callback_queue
=
755 pending_initialize_callbacks_
[file_system_context
];
756 for (StatusCallbackQueue::iterator iter
= callback_queue
.begin();
757 iter
!= callback_queue
.end(); ++iter
) {
758 ui_task_runner_
->PostTask(FROM_HERE
, base::Bind(*iter
, status
));
760 pending_initialize_callbacks_
.erase(file_system_context
);
763 void LocalFileSyncContext::GetNextURLsForSyncOnFileThread(
764 FileSystemContext
* file_system_context
,
765 std::deque
<FileSystemURL
>* urls
) {
766 DCHECK(file_system_context
);
767 DCHECK(file_system_context
->default_file_task_runner()->
768 RunsTasksOnCurrentThread());
769 SyncFileSystemBackend
* backend
=
770 SyncFileSystemBackend::GetBackend(file_system_context
);
772 DCHECK(backend
->change_tracker());
773 backend
->change_tracker()->GetNextChangedURLs(
774 urls
, kMaxURLsToFetchForLocalSync
);
777 void LocalFileSyncContext::TryPrepareForLocalSync(
778 FileSystemContext
* file_system_context
,
779 std::deque
<FileSystemURL
>* urls
,
780 const LocalFileSyncInfoCallback
& callback
) {
781 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
784 if (shutdown_on_ui_
) {
785 callback
.Run(SYNC_STATUS_ABORT
, LocalFileSyncInfo(),
786 webkit_blob::ScopedFile());
791 callback
.Run(SYNC_STATUS_NO_CHANGE_TO_SYNC
, LocalFileSyncInfo(),
792 webkit_blob::ScopedFile());
796 const FileSystemURL url
= urls
->front();
798 std::deque
<FileSystemURL
>* remaining
= new std::deque
<FileSystemURL
>;
799 remaining
->swap(*urls
);
802 file_system_context
, url
, SYNC_SNAPSHOT
,
803 base::Bind(&LocalFileSyncContext::DidTryPrepareForLocalSync
,
804 this, make_scoped_refptr(file_system_context
),
805 base::Owned(remaining
), callback
));
808 void LocalFileSyncContext::DidTryPrepareForLocalSync(
809 FileSystemContext
* file_system_context
,
810 std::deque
<FileSystemURL
>* remaining_urls
,
811 const LocalFileSyncInfoCallback
& callback
,
812 SyncStatusCode status
,
813 const LocalFileSyncInfo
& sync_file_info
,
814 webkit_blob::ScopedFile snapshot
) {
815 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
816 if (status
!= SYNC_STATUS_FILE_BUSY
) {
817 callback
.Run(status
, sync_file_info
, snapshot
.Pass());
820 // Recursively call TryPrepareForLocalSync with remaining_urls.
821 TryPrepareForLocalSync(file_system_context
, remaining_urls
, callback
);
824 void LocalFileSyncContext::DidGetWritingStatusForSync(
825 FileSystemContext
* file_system_context
,
826 SyncStatusCode status
,
827 const FileSystemURL
& url
,
829 const LocalFileSyncInfoCallback
& callback
) {
830 // This gets called on UI thread and relays the task on FILE thread.
831 DCHECK(file_system_context
);
832 if (!file_system_context
->default_file_task_runner()->
833 RunsTasksOnCurrentThread()) {
834 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
835 if (shutdown_on_ui_
) {
836 callback
.Run(SYNC_STATUS_ABORT
, LocalFileSyncInfo(),
837 webkit_blob::ScopedFile());
840 file_system_context
->default_file_task_runner()->PostTask(
842 base::Bind(&LocalFileSyncContext::DidGetWritingStatusForSync
,
843 this, make_scoped_refptr(file_system_context
),
844 status
, url
, sync_mode
, callback
));
848 SyncFileSystemBackend
* backend
=
849 SyncFileSystemBackend::GetBackend(file_system_context
);
851 DCHECK(backend
->change_tracker());
852 FileChangeList changes
;
853 backend
->change_tracker()->GetChangesForURL(url
, &changes
);
855 base::FilePath platform_path
;
856 base::PlatformFileInfo file_info
;
857 FileSystemFileUtil
* file_util
=
858 file_system_context
->sandbox_delegate()->sync_file_util();
861 base::PlatformFileError file_error
= file_util
->GetFileInfo(
863 new FileSystemOperationContext(file_system_context
)).get(),
868 webkit_blob::ScopedFile snapshot
;
869 if (file_error
== base::PLATFORM_FILE_OK
&& sync_mode
== SYNC_SNAPSHOT
) {
870 base::FilePath snapshot_path
;
871 base::CreateTemporaryFileInDir(local_base_path_
.Append(kSnapshotDir
),
873 if (base::CopyFile(platform_path
, snapshot_path
)) {
874 platform_path
= snapshot_path
;
875 snapshot
= webkit_blob::ScopedFile(
877 webkit_blob::ScopedFile::DELETE_ON_SCOPE_OUT
,
878 file_system_context
->default_file_task_runner());
882 if (status
== SYNC_STATUS_OK
&&
883 file_error
!= base::PLATFORM_FILE_OK
&&
884 file_error
!= base::PLATFORM_FILE_ERROR_NOT_FOUND
)
885 status
= PlatformFileErrorToSyncStatusCode(file_error
);
887 DCHECK(!file_info
.is_symbolic_link
);
889 SyncFileType file_type
= SYNC_FILE_TYPE_FILE
;
890 if (file_error
== base::PLATFORM_FILE_ERROR_NOT_FOUND
)
891 file_type
= SYNC_FILE_TYPE_UNKNOWN
;
892 else if (file_info
.is_directory
)
893 file_type
= SYNC_FILE_TYPE_DIRECTORY
;
895 LocalFileSyncInfo sync_file_info
;
896 sync_file_info
.url
= url
;
897 sync_file_info
.local_file_path
= platform_path
;
898 sync_file_info
.metadata
.file_type
= file_type
;
899 sync_file_info
.metadata
.size
= file_info
.size
;
900 sync_file_info
.metadata
.last_modified
= file_info
.last_modified
;
901 sync_file_info
.changes
= changes
;
903 if (status
== SYNC_STATUS_OK
&& sync_mode
== SYNC_SNAPSHOT
) {
904 if (!changes
.empty()) {
905 // Now we create an empty mirror change record for URL (and we record
906 // changes to both mirror and original records during sync), so that
907 // we can reset to the mirror when the sync succeeds.
908 backend
->change_tracker()->CreateFreshMirrorForURL(url
);
911 // 'Unlock' the file for snapshot sync.
912 // (But keep it in writing status so that no other sync starts on
914 io_task_runner_
->PostTask(
916 base::Bind(&LocalFileSyncContext::ClearSyncFlagOnIOThread
,
917 this, url
, true /* for_snapshot_sync */));
920 ui_task_runner_
->PostTask(FROM_HERE
,
921 base::Bind(callback
, status
, sync_file_info
,
922 base::Passed(&snapshot
)));
925 void LocalFileSyncContext::ClearSyncFlagOnIOThread(
926 const FileSystemURL
& url
,
927 bool for_snapshot_sync
) {
928 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
931 sync_status()->EndSyncing(url
);
933 if (for_snapshot_sync
) {
934 // The caller will hold shared lock on this one.
935 sync_status()->StartWriting(url
);
939 // Since a sync has finished the number of changes must have been updated.
940 UpdateChangesForOrigin(url
.origin());
943 void LocalFileSyncContext::FinalizeSnapshotSyncOnIOThread(
944 const FileSystemURL
& url
) {
945 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
948 sync_status()->EndWriting(url
);
950 // Since a sync has finished the number of changes must have been updated.
951 UpdateChangesForOrigin(url
.origin());
954 void LocalFileSyncContext::DidApplyRemoteChange(
955 const FileSystemURL
& url
,
956 const SyncStatusCallback
& callback_on_ui
,
957 base::PlatformFileError file_error
) {
958 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
959 root_delete_helper_
.reset();
960 ui_task_runner_
->PostTask(
962 base::Bind(callback_on_ui
,
963 PlatformFileErrorToSyncStatusCode(file_error
)));
966 void LocalFileSyncContext::DidGetFileMetadata(
967 const SyncFileMetadataCallback
& callback
,
968 base::PlatformFileError file_error
,
969 const base::PlatformFileInfo
& file_info
) {
970 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
971 SyncFileMetadata metadata
;
972 if (file_error
== base::PLATFORM_FILE_OK
) {
973 metadata
.file_type
= file_info
.is_directory
?
974 SYNC_FILE_TYPE_DIRECTORY
: SYNC_FILE_TYPE_FILE
;
975 metadata
.size
= file_info
.size
;
976 metadata
.last_modified
= file_info
.last_modified
;
978 ui_task_runner_
->PostTask(
981 PlatformFileErrorToSyncStatusCode(file_error
),
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
);
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::PlatformFileError error
) {
997 if (error
!= base::PLATFORM_FILE_OK
) {
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
);
1008 } // namespace sync_file_system