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/files/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 "storage/browser/blob/scoped_file.h"
23 #include "storage/browser/fileapi/file_system_context.h"
24 #include "storage/browser/fileapi/file_system_file_util.h"
25 #include "storage/browser/fileapi/file_system_operation_context.h"
26 #include "storage/browser/fileapi/file_system_operation_runner.h"
27 #include "storage/common/fileapi/file_system_util.h"
29 using storage::FileSystemContext
;
30 using storage::FileSystemFileUtil
;
31 using storage::FileSystemOperation
;
32 using storage::FileSystemOperationContext
;
33 using storage::FileSystemURL
;
35 namespace sync_file_system
{
39 const int kMaxConcurrentSyncableOperation
= 3;
40 const int kNotifyChangesDurationInSec
= 1;
41 const int kMaxURLsToFetchForLocalSync
= 5;
43 const base::FilePath::CharType kSnapshotDir
[] = FILE_PATH_LITERAL("snapshots");
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(base_path
.IsAbsolute());
60 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
63 void LocalFileSyncContext::MaybeInitializeFileSystemContext(
64 const GURL
& source_url
,
65 FileSystemContext
* file_system_context
,
66 const SyncStatusCallback
& callback
) {
67 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
68 if (ContainsKey(file_system_contexts_
, file_system_context
)) {
69 // The context has been already initialized. Just dispatch the callback
70 // with SYNC_STATUS_OK.
71 ui_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, SYNC_STATUS_OK
));
75 StatusCallbackQueue
& callback_queue
=
76 pending_initialize_callbacks_
[file_system_context
];
77 callback_queue
.push_back(callback
);
78 if (callback_queue
.size() > 1)
81 // The sync service always expects the origin (app) is initialized
82 // for writable way (even when MaybeInitializeFileSystemContext is called
83 // from read-only OpenFileSystem), so open the filesystem with
84 // CREATE_IF_NONEXISTENT here.
85 storage::FileSystemBackend::OpenFileSystemCallback open_filesystem_callback
=
86 base::Bind(&LocalFileSyncContext::InitializeFileSystemContextOnIOThread
,
89 make_scoped_refptr(file_system_context
));
90 io_task_runner_
->PostTask(
92 base::Bind(&storage::SandboxFileSystemBackendDelegate::OpenFileSystem
,
93 base::Unretained(file_system_context
->sandbox_delegate()),
95 storage::kFileSystemTypeSyncable
,
96 storage::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT
,
97 open_filesystem_callback
,
101 void LocalFileSyncContext::ShutdownOnUIThread() {
102 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
103 shutdown_on_ui_
= true;
104 io_task_runner_
->PostTask(
106 base::Bind(&LocalFileSyncContext::ShutdownOnIOThread
, this));
109 void LocalFileSyncContext::GetFileForLocalSync(
110 FileSystemContext
* file_system_context
,
111 const LocalFileSyncInfoCallback
& callback
) {
112 DCHECK(file_system_context
);
113 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
115 base::PostTaskAndReplyWithResult(
116 file_system_context
->default_file_task_runner(),
118 base::Bind(&LocalFileSyncContext::GetNextURLsForSyncOnFileThread
,
119 this, make_scoped_refptr(file_system_context
)),
120 base::Bind(&LocalFileSyncContext::TryPrepareForLocalSync
,
121 this, make_scoped_refptr(file_system_context
), callback
));
124 void LocalFileSyncContext::ClearChangesForURL(
125 FileSystemContext
* file_system_context
,
126 const FileSystemURL
& url
,
127 const base::Closure
& done_callback
) {
128 // This is initially called on UI thread and to be relayed to FILE thread.
129 DCHECK(file_system_context
);
130 if (!file_system_context
->default_file_task_runner()->
131 RunsTasksOnCurrentThread()) {
132 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
133 file_system_context
->default_file_task_runner()->PostTask(
135 base::Bind(&LocalFileSyncContext::ClearChangesForURL
,
136 this, make_scoped_refptr(file_system_context
),
137 url
, done_callback
));
141 SyncFileSystemBackend
* backend
=
142 SyncFileSystemBackend::GetBackend(file_system_context
);
144 DCHECK(backend
->change_tracker());
145 backend
->change_tracker()->ClearChangesForURL(url
);
147 // Call the completion callback on UI thread.
148 ui_task_runner_
->PostTask(FROM_HERE
, done_callback
);
151 void LocalFileSyncContext::FinalizeSnapshotSync(
152 storage::FileSystemContext
* file_system_context
,
153 const storage::FileSystemURL
& url
,
154 SyncStatusCode sync_finish_status
,
155 const base::Closure
& done_callback
) {
156 DCHECK(file_system_context
);
157 DCHECK(url
.is_valid());
158 if (!file_system_context
->default_file_task_runner()->
159 RunsTasksOnCurrentThread()) {
160 file_system_context
->default_file_task_runner()->PostTask(
162 base::Bind(&LocalFileSyncContext::FinalizeSnapshotSync
,
163 this, make_scoped_refptr(file_system_context
),
164 url
, sync_finish_status
, done_callback
));
168 SyncFileSystemBackend
* backend
=
169 SyncFileSystemBackend::GetBackend(file_system_context
);
171 DCHECK(backend
->change_tracker());
173 if (sync_finish_status
== SYNC_STATUS_OK
||
174 sync_finish_status
== SYNC_STATUS_HAS_CONFLICT
) {
175 // Commit the in-memory mirror change.
176 backend
->change_tracker()->ResetToMirrorAndCommitChangesForURL(url
);
178 // Abort in-memory mirror change.
179 backend
->change_tracker()->RemoveMirrorAndCommitChangesForURL(url
);
182 // We've been keeping it in writing mode, so clear the writing counter
183 // to unblock sync activities.
184 io_task_runner_
->PostTask(
185 FROM_HERE
, base::Bind(
186 &LocalFileSyncContext::FinalizeSnapshotSyncOnIOThread
, this, url
));
188 // Call the completion callback on UI thread.
189 ui_task_runner_
->PostTask(FROM_HERE
, done_callback
);
192 void LocalFileSyncContext::FinalizeExclusiveSync(
193 storage::FileSystemContext
* file_system_context
,
194 const storage::FileSystemURL
& url
,
195 bool clear_local_changes
,
196 const base::Closure
& done_callback
) {
197 DCHECK(file_system_context
);
198 if (!url
.is_valid()) {
203 if (clear_local_changes
) {
204 ClearChangesForURL(file_system_context
, url
,
205 base::Bind(&LocalFileSyncContext::FinalizeExclusiveSync
,
206 this, make_scoped_refptr(file_system_context
),
207 url
, false, done_callback
));
211 io_task_runner_
->PostTask(
213 base::Bind(&LocalFileSyncContext::ClearSyncFlagOnIOThread
,
214 this, url
, false /* for_snapshot_sync */));
219 void LocalFileSyncContext::PrepareForSync(
220 FileSystemContext
* file_system_context
,
221 const FileSystemURL
& url
,
223 const LocalFileSyncInfoCallback
& callback
) {
224 // This is initially called on UI thread and to be relayed to IO thread.
225 if (!io_task_runner_
->RunsTasksOnCurrentThread()) {
226 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
227 io_task_runner_
->PostTask(
229 base::Bind(&LocalFileSyncContext::PrepareForSync
, this,
230 make_scoped_refptr(file_system_context
), url
,
231 sync_mode
, callback
));
234 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
235 const bool syncable
= sync_status()->IsSyncable(url
);
236 // Disable writing if it's ready to be synced.
238 sync_status()->StartSyncing(url
);
239 ui_task_runner_
->PostTask(
241 base::Bind(&LocalFileSyncContext::DidGetWritingStatusForSync
,
242 this, make_scoped_refptr(file_system_context
),
243 syncable
? SYNC_STATUS_OK
:
244 SYNC_STATUS_FILE_BUSY
,
245 url
, sync_mode
, callback
));
248 void LocalFileSyncContext::RegisterURLForWaitingSync(
249 const FileSystemURL
& url
,
250 const base::Closure
& on_syncable_callback
) {
251 // This is initially called on UI thread and to be relayed to IO thread.
252 if (!io_task_runner_
->RunsTasksOnCurrentThread()) {
253 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
254 io_task_runner_
->PostTask(
256 base::Bind(&LocalFileSyncContext::RegisterURLForWaitingSync
,
257 this, url
, on_syncable_callback
));
260 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
263 if (sync_status()->IsSyncable(url
)) {
264 // No need to register; fire the callback now.
265 ui_task_runner_
->PostTask(FROM_HERE
, on_syncable_callback
);
268 url_waiting_sync_on_io_
= url
;
269 url_syncable_callback_
= on_syncable_callback
;
272 void LocalFileSyncContext::ApplyRemoteChange(
273 FileSystemContext
* file_system_context
,
274 const FileChange
& change
,
275 const base::FilePath
& local_path
,
276 const FileSystemURL
& url
,
277 const SyncStatusCallback
& callback
) {
278 if (!io_task_runner_
->RunsTasksOnCurrentThread()) {
279 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
280 io_task_runner_
->PostTask(
282 base::Bind(&LocalFileSyncContext::ApplyRemoteChange
, this,
283 make_scoped_refptr(file_system_context
),
284 change
, local_path
, url
, callback
));
287 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
288 DCHECK(!sync_status()->IsWritable(url
));
289 DCHECK(!sync_status()->IsWriting(url
));
291 FileSystemOperation::StatusCallback operation_callback
;
292 switch (change
.change()) {
293 case FileChange::FILE_CHANGE_DELETE
:
294 HandleRemoteDelete(file_system_context
, url
, callback
);
296 case FileChange::FILE_CHANGE_ADD_OR_UPDATE
:
297 HandleRemoteAddOrUpdate(
298 file_system_context
, change
, local_path
, url
, callback
);
302 callback
.Run(SYNC_STATUS_FAILED
);
305 void LocalFileSyncContext::HandleRemoteDelete(
306 FileSystemContext
* file_system_context
,
307 const FileSystemURL
& url
,
308 const SyncStatusCallback
& callback
) {
309 FileSystemURL url_for_sync
= CreateSyncableFileSystemURLForSync(
310 file_system_context
, url
);
312 // Handle root directory case differently.
313 if (storage::VirtualPath::IsRootPath(url
.path())) {
314 DCHECK(!root_delete_helper_
);
315 root_delete_helper_
.reset(new RootDeleteHelper(
316 file_system_context
, sync_status(), url
,
317 base::Bind(&LocalFileSyncContext::DidApplyRemoteChange
,
318 this, url
, callback
)));
319 root_delete_helper_
->Run();
323 file_system_context
->operation_runner()->Remove(
324 url_for_sync
, true /* recursive */,
325 base::Bind(&LocalFileSyncContext::DidApplyRemoteChange
,
326 this, url
, callback
));
329 void LocalFileSyncContext::HandleRemoteAddOrUpdate(
330 FileSystemContext
* file_system_context
,
331 const FileChange
& change
,
332 const base::FilePath
& local_path
,
333 const FileSystemURL
& url
,
334 const SyncStatusCallback
& callback
) {
335 FileSystemURL url_for_sync
= CreateSyncableFileSystemURLForSync(
336 file_system_context
, url
);
338 if (storage::VirtualPath::IsRootPath(url
.path())) {
339 DidApplyRemoteChange(url
, callback
, base::File::FILE_OK
);
343 file_system_context
->operation_runner()->Remove(
344 url_for_sync
, true /* recursive */,
346 &LocalFileSyncContext::DidRemoveExistingEntryForRemoteAddOrUpdate
,
348 make_scoped_refptr(file_system_context
),
355 void LocalFileSyncContext::DidRemoveExistingEntryForRemoteAddOrUpdate(
356 FileSystemContext
* file_system_context
,
357 const FileChange
& change
,
358 const base::FilePath
& local_path
,
359 const FileSystemURL
& url
,
360 const SyncStatusCallback
& callback
,
361 base::File::Error error
) {
362 // Remove() may fail if the target entry does not exist (which is ok),
363 // so we ignore |error| here.
365 if (shutdown_on_io_
) {
366 callback
.Run(SYNC_FILE_ERROR_ABORT
);
370 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
371 DCHECK(!sync_status()->IsWritable(url
));
372 DCHECK(!sync_status()->IsWriting(url
));
374 FileSystemURL url_for_sync
= CreateSyncableFileSystemURLForSync(
375 file_system_context
, url
);
376 FileSystemOperation::StatusCallback operation_callback
= base::Bind(
377 &LocalFileSyncContext::DidApplyRemoteChange
, this, url
, callback
);
379 DCHECK_EQ(FileChange::FILE_CHANGE_ADD_OR_UPDATE
, change
.change());
380 switch (change
.file_type()) {
381 case SYNC_FILE_TYPE_FILE
: {
382 DCHECK(!local_path
.empty());
383 base::FilePath dir_path
= storage::VirtualPath::DirName(url
.path());
384 if (dir_path
.empty() ||
385 storage::VirtualPath::DirName(dir_path
) == dir_path
) {
386 // Copying into the root directory.
387 file_system_context
->operation_runner()->CopyInForeignFile(
388 local_path
, url_for_sync
, operation_callback
);
390 FileSystemURL dir_url
= file_system_context
->CreateCrackedFileSystemURL(
391 url_for_sync
.origin(),
392 url_for_sync
.mount_type(),
393 storage::VirtualPath::DirName(url_for_sync
.virtual_path()));
394 file_system_context
->operation_runner()->CreateDirectory(
396 false /* exclusive */,
397 true /* recursive */,
398 base::Bind(&LocalFileSyncContext::DidCreateDirectoryForCopyIn
,
400 make_scoped_refptr(file_system_context
),
403 operation_callback
));
407 case SYNC_FILE_TYPE_DIRECTORY
:
408 file_system_context
->operation_runner()->CreateDirectory(
409 url_for_sync
, false /* exclusive */, true /* recursive */,
412 case SYNC_FILE_TYPE_UNKNOWN
:
413 NOTREACHED() << "File type unknown for ADD_OR_UPDATE change";
417 void LocalFileSyncContext::RecordFakeLocalChange(
418 FileSystemContext
* file_system_context
,
419 const FileSystemURL
& url
,
420 const FileChange
& change
,
421 const SyncStatusCallback
& callback
) {
422 // This is called on UI thread and to be relayed to FILE thread.
423 DCHECK(file_system_context
);
424 if (!file_system_context
->default_file_task_runner()->
425 RunsTasksOnCurrentThread()) {
426 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
427 file_system_context
->default_file_task_runner()->PostTask(
429 base::Bind(&LocalFileSyncContext::RecordFakeLocalChange
,
430 this, make_scoped_refptr(file_system_context
),
431 url
, change
, callback
));
435 SyncFileSystemBackend
* backend
=
436 SyncFileSystemBackend::GetBackend(file_system_context
);
438 DCHECK(backend
->change_tracker());
439 backend
->change_tracker()->MarkDirtyOnDatabase(url
);
440 backend
->change_tracker()->RecordChange(url
, change
);
442 // Fire the callback on UI thread.
443 ui_task_runner_
->PostTask(FROM_HERE
,
448 void LocalFileSyncContext::GetFileMetadata(
449 FileSystemContext
* file_system_context
,
450 const FileSystemURL
& url
,
451 const SyncFileMetadataCallback
& callback
) {
452 // This is initially called on UI thread and to be relayed to IO thread.
453 if (!io_task_runner_
->RunsTasksOnCurrentThread()) {
454 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
455 io_task_runner_
->PostTask(
457 base::Bind(&LocalFileSyncContext::GetFileMetadata
, this,
458 make_scoped_refptr(file_system_context
), url
, callback
));
461 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
463 FileSystemURL url_for_sync
= CreateSyncableFileSystemURLForSync(
464 file_system_context
, url
);
465 file_system_context
->operation_runner()->GetMetadata(
466 url_for_sync
, base::Bind(&LocalFileSyncContext::DidGetFileMetadata
,
470 void LocalFileSyncContext::HasPendingLocalChanges(
471 FileSystemContext
* file_system_context
,
472 const FileSystemURL
& url
,
473 const HasPendingLocalChangeCallback
& callback
) {
474 // This gets called on UI thread and relays the task on FILE thread.
475 DCHECK(file_system_context
);
476 if (!file_system_context
->default_file_task_runner()->
477 RunsTasksOnCurrentThread()) {
478 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
479 file_system_context
->default_file_task_runner()->PostTask(
481 base::Bind(&LocalFileSyncContext::HasPendingLocalChanges
,
482 this, make_scoped_refptr(file_system_context
),
487 SyncFileSystemBackend
* backend
=
488 SyncFileSystemBackend::GetBackend(file_system_context
);
490 DCHECK(backend
->change_tracker());
491 FileChangeList changes
;
492 backend
->change_tracker()->GetChangesForURL(url
, &changes
);
494 // Fire the callback on UI thread.
495 ui_task_runner_
->PostTask(FROM_HERE
,
501 void LocalFileSyncContext::PromoteDemotedChanges(
503 storage::FileSystemContext
* file_system_context
,
504 const base::Closure
& callback
) {
505 // This is initially called on UI thread and to be relayed to FILE thread.
506 DCHECK(file_system_context
);
507 if (!file_system_context
->default_file_task_runner()->
508 RunsTasksOnCurrentThread()) {
509 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
510 file_system_context
->default_file_task_runner()->PostTask(
512 base::Bind(&LocalFileSyncContext::PromoteDemotedChanges
,
513 this, origin
, make_scoped_refptr(file_system_context
),
518 SyncFileSystemBackend
* backend
=
519 SyncFileSystemBackend::GetBackend(file_system_context
);
521 DCHECK(backend
->change_tracker());
522 if (!backend
->change_tracker()->PromoteDemotedChanges()) {
523 ui_task_runner_
->PostTask(FROM_HERE
, callback
);
527 io_task_runner_
->PostTask(
529 base::Bind(&LocalFileSyncContext::UpdateChangesForOrigin
,
530 this, origin
, callback
));
533 void LocalFileSyncContext::UpdateChangesForOrigin(
535 const base::Closure
& callback
) {
536 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
539 origins_with_pending_changes_
.insert(origin
);
540 ScheduleNotifyChangesUpdatedOnIOThread(callback
);
543 void LocalFileSyncContext::AddOriginChangeObserver(
544 LocalOriginChangeObserver
* observer
) {
545 origin_change_observers_
.AddObserver(observer
);
548 void LocalFileSyncContext::RemoveOriginChangeObserver(
549 LocalOriginChangeObserver
* observer
) {
550 origin_change_observers_
.RemoveObserver(observer
);
553 base::WeakPtr
<SyncableFileOperationRunner
>
554 LocalFileSyncContext::operation_runner() const {
555 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
556 if (operation_runner_
)
557 return operation_runner_
->AsWeakPtr();
558 return base::WeakPtr
<SyncableFileOperationRunner
>();
561 LocalFileSyncStatus
* LocalFileSyncContext::sync_status() const {
562 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
563 return sync_status_
.get();
566 void LocalFileSyncContext::OnSyncEnabled(const FileSystemURL
& url
) {
567 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
570 UpdateChangesForOrigin(url
.origin(), NoopClosure());
571 if (url_syncable_callback_
.is_null() ||
572 sync_status()->IsWriting(url_waiting_sync_on_io_
)) {
575 // TODO(kinuko): may want to check how many pending tasks we have.
576 ui_task_runner_
->PostTask(FROM_HERE
, url_syncable_callback_
);
577 url_syncable_callback_
.Reset();
580 void LocalFileSyncContext::OnWriteEnabled(const FileSystemURL
& url
) {
581 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
582 // Nothing to do for now.
585 LocalFileSyncContext::~LocalFileSyncContext() {
588 void LocalFileSyncContext::ScheduleNotifyChangesUpdatedOnIOThread(
589 const base::Closure
& callback
) {
590 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
593 pending_completion_callbacks_
.push_back(callback
);
594 if (base::Time::Now() > last_notified_changes_
+ NotifyChangesDuration()) {
595 NotifyAvailableChangesOnIOThread();
596 } else if (!timer_on_io_
->IsRunning()) {
598 FROM_HERE
, NotifyChangesDuration(),
599 base::Bind(&LocalFileSyncContext::NotifyAvailableChangesOnIOThread
,
600 base::Unretained(this)));
604 void LocalFileSyncContext::NotifyAvailableChangesOnIOThread() {
605 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
609 std::vector
<base::Closure
> completion_callbacks
;
610 completion_callbacks
.swap(pending_completion_callbacks_
);
612 ui_task_runner_
->PostTask(
614 base::Bind(&LocalFileSyncContext::NotifyAvailableChanges
,
615 this, origins_with_pending_changes_
,
616 completion_callbacks
));
617 last_notified_changes_
= base::Time::Now();
618 origins_with_pending_changes_
.clear();
621 void LocalFileSyncContext::NotifyAvailableChanges(
622 const std::set
<GURL
>& origins
,
623 const std::vector
<base::Closure
>& callbacks
) {
624 FOR_EACH_OBSERVER(LocalOriginChangeObserver
, origin_change_observers_
,
625 OnChangesAvailableInOrigins(origins
));
626 for (const auto& callback
: callbacks
)
630 void LocalFileSyncContext::ShutdownOnIOThread() {
631 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
632 shutdown_on_io_
= true;
633 operation_runner_
.reset();
634 root_delete_helper_
.reset();
635 sync_status_
.reset();
636 timer_on_io_
.reset();
639 void LocalFileSyncContext::InitializeFileSystemContextOnIOThread(
640 const GURL
& source_url
,
641 FileSystemContext
* file_system_context
,
642 const GURL
& /* root */,
643 const std::string
& /* name */,
644 base::File::Error error
) {
645 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
647 error
= base::File::FILE_ERROR_ABORT
;
648 if (error
!= base::File::FILE_OK
) {
649 DidInitialize(source_url
, file_system_context
,
650 FileErrorToSyncStatusCode(error
));
653 DCHECK(file_system_context
);
654 SyncFileSystemBackend
* backend
=
655 SyncFileSystemBackend::GetBackend(file_system_context
);
657 if (!backend
->change_tracker()) {
658 // Create and initialize LocalFileChangeTracker and call back this method
660 std::set
<GURL
>* origins_with_changes
= new std::set
<GURL
>;
661 scoped_ptr
<LocalFileChangeTracker
>* tracker_ptr(
662 new scoped_ptr
<LocalFileChangeTracker
>);
663 base::PostTaskAndReplyWithResult(
664 file_system_context
->default_file_task_runner(),
666 base::Bind(&LocalFileSyncContext::InitializeChangeTrackerOnFileThread
,
668 make_scoped_refptr(file_system_context
),
669 origins_with_changes
),
670 base::Bind(&LocalFileSyncContext::DidInitializeChangeTrackerOnIOThread
,
671 this, base::Owned(tracker_ptr
),
673 make_scoped_refptr(file_system_context
),
674 base::Owned(origins_with_changes
)));
677 if (!operation_runner_
) {
678 DCHECK(!sync_status_
);
679 DCHECK(!timer_on_io_
);
680 sync_status_
.reset(new LocalFileSyncStatus
);
681 timer_on_io_
.reset(new base::OneShotTimer
<LocalFileSyncContext
>);
682 operation_runner_
.reset(new SyncableFileOperationRunner(
683 kMaxConcurrentSyncableOperation
,
684 sync_status_
.get()));
685 sync_status_
->AddObserver(this);
687 backend
->set_sync_context(this);
688 DidInitialize(source_url
, file_system_context
,
692 SyncStatusCode
LocalFileSyncContext::InitializeChangeTrackerOnFileThread(
693 scoped_ptr
<LocalFileChangeTracker
>* tracker_ptr
,
694 FileSystemContext
* file_system_context
,
695 std::set
<GURL
>* origins_with_changes
) {
696 DCHECK(file_system_context
);
698 DCHECK(origins_with_changes
);
699 tracker_ptr
->reset(new LocalFileChangeTracker(
700 file_system_context
->partition_path(),
702 file_system_context
->default_file_task_runner()));
703 const SyncStatusCode status
= (*tracker_ptr
)->Initialize(file_system_context
);
704 if (status
!= SYNC_STATUS_OK
)
707 // Get all origins that have pending changes.
708 FileSystemURLQueue urls
;
709 (*tracker_ptr
)->GetNextChangedURLs(&urls
, 0);
710 for (FileSystemURLQueue::iterator iter
= urls
.begin();
711 iter
!= urls
.end(); ++iter
) {
712 origins_with_changes
->insert(iter
->origin());
715 // Creates snapshot directory.
716 base::CreateDirectory(local_base_path_
.Append(kSnapshotDir
));
721 void LocalFileSyncContext::DidInitializeChangeTrackerOnIOThread(
722 scoped_ptr
<LocalFileChangeTracker
>* tracker_ptr
,
723 const GURL
& source_url
,
724 FileSystemContext
* file_system_context
,
725 std::set
<GURL
>* origins_with_changes
,
726 SyncStatusCode status
) {
727 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
728 DCHECK(file_system_context
);
729 DCHECK(origins_with_changes
);
731 status
= SYNC_STATUS_ABORT
;
732 if (status
!= SYNC_STATUS_OK
) {
733 DidInitialize(source_url
, file_system_context
, status
);
737 SyncFileSystemBackend
* backend
=
738 SyncFileSystemBackend::GetBackend(file_system_context
);
740 backend
->SetLocalFileChangeTracker(tracker_ptr
->Pass());
742 origins_with_pending_changes_
.insert(origins_with_changes
->begin(),
743 origins_with_changes
->end());
744 ScheduleNotifyChangesUpdatedOnIOThread(NoopClosure());
746 InitializeFileSystemContextOnIOThread(source_url
, file_system_context
,
747 GURL(), std::string(),
748 base::File::FILE_OK
);
751 void LocalFileSyncContext::DidInitialize(
752 const GURL
& source_url
,
753 FileSystemContext
* file_system_context
,
754 SyncStatusCode status
) {
755 if (!ui_task_runner_
->RunsTasksOnCurrentThread()) {
756 ui_task_runner_
->PostTask(
758 base::Bind(&LocalFileSyncContext::DidInitialize
,
760 make_scoped_refptr(file_system_context
), status
));
763 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
764 DCHECK(!ContainsKey(file_system_contexts_
, file_system_context
));
765 DCHECK(ContainsKey(pending_initialize_callbacks_
, file_system_context
));
767 SyncFileSystemBackend
* backend
=
768 SyncFileSystemBackend::GetBackend(file_system_context
);
770 DCHECK(backend
->change_tracker());
772 file_system_contexts_
.insert(file_system_context
);
774 StatusCallbackQueue
& callback_queue
=
775 pending_initialize_callbacks_
[file_system_context
];
776 for (StatusCallbackQueue::iterator iter
= callback_queue
.begin();
777 iter
!= callback_queue
.end(); ++iter
) {
778 ui_task_runner_
->PostTask(FROM_HERE
, base::Bind(*iter
, status
));
780 pending_initialize_callbacks_
.erase(file_system_context
);
783 scoped_ptr
<LocalFileSyncContext::FileSystemURLQueue
>
784 LocalFileSyncContext::GetNextURLsForSyncOnFileThread(
785 FileSystemContext
* file_system_context
) {
786 DCHECK(file_system_context
);
787 DCHECK(file_system_context
->default_file_task_runner()->
788 RunsTasksOnCurrentThread());
789 SyncFileSystemBackend
* backend
=
790 SyncFileSystemBackend::GetBackend(file_system_context
);
792 DCHECK(backend
->change_tracker());
793 scoped_ptr
<FileSystemURLQueue
> urls(new FileSystemURLQueue
);
794 backend
->change_tracker()->GetNextChangedURLs(
795 urls
.get(), kMaxURLsToFetchForLocalSync
);
796 for (FileSystemURLQueue::iterator iter
= urls
->begin();
797 iter
!= urls
->end(); ++iter
)
798 backend
->change_tracker()->DemoteChangesForURL(*iter
);
803 void LocalFileSyncContext::TryPrepareForLocalSync(
804 FileSystemContext
* file_system_context
,
805 const LocalFileSyncInfoCallback
& callback
,
806 scoped_ptr
<FileSystemURLQueue
> urls
) {
807 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
810 if (shutdown_on_ui_
) {
811 callback
.Run(SYNC_STATUS_ABORT
, LocalFileSyncInfo(), storage::ScopedFile());
816 callback
.Run(SYNC_STATUS_NO_CHANGE_TO_SYNC
,
818 storage::ScopedFile());
822 const FileSystemURL url
= urls
->front();
826 file_system_context
, url
, SYNC_SNAPSHOT
,
827 base::Bind(&LocalFileSyncContext::DidTryPrepareForLocalSync
,
828 this, make_scoped_refptr(file_system_context
),
829 base::Passed(&urls
), callback
));
832 void LocalFileSyncContext::DidTryPrepareForLocalSync(
833 FileSystemContext
* file_system_context
,
834 scoped_ptr
<FileSystemURLQueue
> remaining_urls
,
835 const LocalFileSyncInfoCallback
& callback
,
836 SyncStatusCode status
,
837 const LocalFileSyncInfo
& sync_file_info
,
838 storage::ScopedFile snapshot
) {
839 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
840 if (status
!= SYNC_STATUS_FILE_BUSY
) {
841 PromoteDemotedChangesForURLs(file_system_context
,
842 remaining_urls
.Pass());
843 callback
.Run(status
, sync_file_info
, snapshot
.Pass());
847 PromoteDemotedChangesForURL(file_system_context
, sync_file_info
.url
);
849 // Recursively call TryPrepareForLocalSync with remaining_urls.
850 TryPrepareForLocalSync(file_system_context
, callback
, remaining_urls
.Pass());
853 void LocalFileSyncContext::PromoteDemotedChangesForURL(
854 FileSystemContext
* file_system_context
,
855 const FileSystemURL
& url
) {
856 DCHECK(file_system_context
);
857 if (!file_system_context
->default_file_task_runner()->
858 RunsTasksOnCurrentThread()) {
859 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
862 file_system_context
->default_file_task_runner()->PostTask(
864 base::Bind(&LocalFileSyncContext::PromoteDemotedChangesForURL
,
865 this, make_scoped_refptr(file_system_context
), url
));
869 SyncFileSystemBackend
* backend
=
870 SyncFileSystemBackend::GetBackend(file_system_context
);
872 DCHECK(backend
->change_tracker());
873 backend
->change_tracker()->PromoteDemotedChangesForURL(url
);
876 void LocalFileSyncContext::PromoteDemotedChangesForURLs(
877 FileSystemContext
* file_system_context
,
878 scoped_ptr
<FileSystemURLQueue
> urls
) {
879 DCHECK(file_system_context
);
880 if (!file_system_context
->default_file_task_runner()->
881 RunsTasksOnCurrentThread()) {
882 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
885 file_system_context
->default_file_task_runner()->PostTask(
887 base::Bind(&LocalFileSyncContext::PromoteDemotedChangesForURLs
,
888 this, make_scoped_refptr(file_system_context
),
889 base::Passed(&urls
)));
893 for (FileSystemURLQueue::iterator iter
= urls
->begin();
894 iter
!= urls
->end(); ++iter
)
895 PromoteDemotedChangesForURL(file_system_context
, *iter
);
898 void LocalFileSyncContext::DidGetWritingStatusForSync(
899 FileSystemContext
* file_system_context
,
900 SyncStatusCode status
,
901 const FileSystemURL
& url
,
903 const LocalFileSyncInfoCallback
& callback
) {
904 // This gets called on UI thread and relays the task on FILE thread.
905 DCHECK(file_system_context
);
906 if (!file_system_context
->default_file_task_runner()->
907 RunsTasksOnCurrentThread()) {
908 DCHECK(ui_task_runner_
->RunsTasksOnCurrentThread());
909 if (shutdown_on_ui_
) {
911 SYNC_STATUS_ABORT
, LocalFileSyncInfo(), storage::ScopedFile());
914 file_system_context
->default_file_task_runner()->PostTask(
916 base::Bind(&LocalFileSyncContext::DidGetWritingStatusForSync
,
917 this, make_scoped_refptr(file_system_context
),
918 status
, url
, sync_mode
, callback
));
922 SyncFileSystemBackend
* backend
=
923 SyncFileSystemBackend::GetBackend(file_system_context
);
925 DCHECK(backend
->change_tracker());
926 FileChangeList changes
;
927 backend
->change_tracker()->GetChangesForURL(url
, &changes
);
929 base::FilePath platform_path
;
930 base::File::Info file_info
;
931 FileSystemFileUtil
* file_util
=
932 file_system_context
->sandbox_delegate()->sync_file_util();
935 base::File::Error file_error
= file_util
->GetFileInfo(
937 new FileSystemOperationContext(file_system_context
)).get(),
942 storage::ScopedFile snapshot
;
943 if (file_error
== base::File::FILE_OK
&& sync_mode
== SYNC_SNAPSHOT
) {
944 base::FilePath snapshot_path
;
945 base::CreateTemporaryFileInDir(local_base_path_
.Append(kSnapshotDir
),
947 if (base::CopyFile(platform_path
, snapshot_path
)) {
948 platform_path
= snapshot_path
;
950 storage::ScopedFile(snapshot_path
,
951 storage::ScopedFile::DELETE_ON_SCOPE_OUT
,
952 file_system_context
->default_file_task_runner());
956 if (status
== SYNC_STATUS_OK
&&
957 file_error
!= base::File::FILE_OK
&&
958 file_error
!= base::File::FILE_ERROR_NOT_FOUND
) {
959 status
= FileErrorToSyncStatusCode(file_error
);
962 DCHECK(!file_info
.is_symbolic_link
);
964 SyncFileType file_type
= SYNC_FILE_TYPE_FILE
;
965 if (file_error
== base::File::FILE_ERROR_NOT_FOUND
)
966 file_type
= SYNC_FILE_TYPE_UNKNOWN
;
967 else if (file_info
.is_directory
)
968 file_type
= SYNC_FILE_TYPE_DIRECTORY
;
970 LocalFileSyncInfo sync_file_info
;
971 sync_file_info
.url
= url
;
972 sync_file_info
.local_file_path
= platform_path
;
973 sync_file_info
.metadata
.file_type
= file_type
;
974 sync_file_info
.metadata
.size
= file_info
.size
;
975 sync_file_info
.metadata
.last_modified
= file_info
.last_modified
;
976 sync_file_info
.changes
= changes
;
978 if (status
== SYNC_STATUS_OK
&& sync_mode
== SYNC_SNAPSHOT
) {
979 if (!changes
.empty()) {
980 // Now we create an empty mirror change record for URL (and we record
981 // changes to both mirror and original records during sync), so that
982 // we can reset to the mirror when the sync succeeds.
983 backend
->change_tracker()->CreateFreshMirrorForURL(url
);
986 // 'Unlock' the file for snapshot sync.
987 // (But keep it in writing status so that no other sync starts on
989 io_task_runner_
->PostTask(
991 base::Bind(&LocalFileSyncContext::ClearSyncFlagOnIOThread
,
992 this, url
, true /* for_snapshot_sync */));
995 ui_task_runner_
->PostTask(FROM_HERE
,
996 base::Bind(callback
, status
, sync_file_info
,
997 base::Passed(&snapshot
)));
1000 void LocalFileSyncContext::ClearSyncFlagOnIOThread(
1001 const FileSystemURL
& url
,
1002 bool for_snapshot_sync
) {
1003 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
1004 if (shutdown_on_io_
)
1006 sync_status()->EndSyncing(url
);
1008 if (for_snapshot_sync
) {
1009 // The caller will hold shared lock on this one.
1010 sync_status()->StartWriting(url
);
1014 // Since a sync has finished the number of changes must have been updated.
1015 UpdateChangesForOrigin(url
.origin(), NoopClosure());
1018 void LocalFileSyncContext::FinalizeSnapshotSyncOnIOThread(
1019 const FileSystemURL
& url
) {
1020 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
1021 if (shutdown_on_io_
)
1023 sync_status()->EndWriting(url
);
1025 // Since a sync has finished the number of changes must have been updated.
1026 UpdateChangesForOrigin(url
.origin(), NoopClosure());
1029 void LocalFileSyncContext::DidApplyRemoteChange(
1030 const FileSystemURL
& url
,
1031 const SyncStatusCallback
& callback_on_ui
,
1032 base::File::Error file_error
) {
1033 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
1034 root_delete_helper_
.reset();
1035 ui_task_runner_
->PostTask(
1037 base::Bind(callback_on_ui
, FileErrorToSyncStatusCode(file_error
)));
1040 void LocalFileSyncContext::DidGetFileMetadata(
1041 const SyncFileMetadataCallback
& callback
,
1042 base::File::Error file_error
,
1043 const base::File::Info
& file_info
) {
1044 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
1045 SyncFileMetadata metadata
;
1046 if (file_error
== base::File::FILE_OK
) {
1047 metadata
.file_type
= file_info
.is_directory
?
1048 SYNC_FILE_TYPE_DIRECTORY
: SYNC_FILE_TYPE_FILE
;
1049 metadata
.size
= file_info
.size
;
1050 metadata
.last_modified
= file_info
.last_modified
;
1052 ui_task_runner_
->PostTask(
1054 base::Bind(callback
, FileErrorToSyncStatusCode(file_error
), metadata
));
1057 base::TimeDelta
LocalFileSyncContext::NotifyChangesDuration() {
1058 if (mock_notify_changes_duration_in_sec_
>= 0)
1059 return base::TimeDelta::FromSeconds(mock_notify_changes_duration_in_sec_
);
1060 return base::TimeDelta::FromSeconds(kNotifyChangesDurationInSec
);
1063 void LocalFileSyncContext::DidCreateDirectoryForCopyIn(
1064 FileSystemContext
* file_system_context
,
1065 const base::FilePath
& local_path
,
1066 const FileSystemURL
& dest_url
,
1067 const StatusCallback
& callback
,
1068 base::File::Error error
) {
1069 if (error
!= base::File::FILE_OK
) {
1070 callback
.Run(error
);
1074 FileSystemURL url_for_sync
= CreateSyncableFileSystemURLForSync(
1075 file_system_context
, dest_url
);
1076 file_system_context
->operation_runner()->CopyInForeignFile(
1077 local_path
, url_for_sync
, callback
);
1080 } // namespace sync_file_system