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 leveldb::Env* env_override,
51 base::SingleThreadTaskRunner* ui_task_runner,
52 base::SingleThreadTaskRunner* io_task_runner)
53 : local_base_path_(base_path.Append(FILE_PATH_LITERAL("local"))),
54 env_override_(env_override),
55 ui_task_runner_(ui_task_runner),
56 io_task_runner_(io_task_runner),
57 shutdown_on_ui_(false),
58 shutdown_on_io_(false),
59 mock_notify_changes_duration_in_sec_(-1) {
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 fileapi::FileSystemBackend::OpenFileSystemCallback open_filesystem_callback =
86 base::Bind(&LocalFileSyncContext::InitializeFileSystemContextOnIOThread,
87 this, source_url, make_scoped_refptr(file_system_context));
88 io_task_runner_->PostTask(
90 base::Bind(&fileapi::SandboxFileSystemBackendDelegate::OpenFileSystem,
91 base::Unretained(file_system_context->sandbox_delegate()),
92 source_url, fileapi::kFileSystemTypeSyncable,
93 fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
94 open_filesystem_callback, GURL()));
97 void LocalFileSyncContext::ShutdownOnUIThread() {
98 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
99 shutdown_on_ui_ = true;
100 io_task_runner_->PostTask(
102 base::Bind(&LocalFileSyncContext::ShutdownOnIOThread, this));
105 void LocalFileSyncContext::GetFileForLocalSync(
106 FileSystemContext* file_system_context,
107 const LocalFileSyncInfoCallback& callback) {
108 DCHECK(file_system_context);
109 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
111 std::deque<FileSystemURL>* urls = new std::deque<FileSystemURL>;
112 file_system_context->default_file_task_runner()->PostTaskAndReply(
114 base::Bind(&LocalFileSyncContext::GetNextURLsForSyncOnFileThread,
115 this, make_scoped_refptr(file_system_context),
116 base::Unretained(urls)),
117 base::Bind(&LocalFileSyncContext::TryPrepareForLocalSync,
118 this, make_scoped_refptr(file_system_context),
119 base::Owned(urls), callback));
122 void LocalFileSyncContext::ClearChangesForURL(
123 FileSystemContext* file_system_context,
124 const FileSystemURL& url,
125 const base::Closure& done_callback) {
126 // This is initially called on UI thread and to be relayed to FILE thread.
127 DCHECK(file_system_context);
128 if (!file_system_context->default_file_task_runner()->
129 RunsTasksOnCurrentThread()) {
130 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
131 file_system_context->default_file_task_runner()->PostTask(
133 base::Bind(&LocalFileSyncContext::ClearChangesForURL,
134 this, make_scoped_refptr(file_system_context),
135 url, done_callback));
139 SyncFileSystemBackend* backend =
140 SyncFileSystemBackend::GetBackend(file_system_context);
142 DCHECK(backend->change_tracker());
143 backend->change_tracker()->ClearChangesForURL(url);
145 // Call the completion callback on UI thread.
146 ui_task_runner_->PostTask(FROM_HERE, done_callback);
149 void LocalFileSyncContext::FinalizeSnapshotSync(
150 fileapi::FileSystemContext* file_system_context,
151 const fileapi::FileSystemURL& url,
152 SyncStatusCode sync_finish_status,
153 const base::Closure& done_callback) {
154 DCHECK(file_system_context);
155 DCHECK(url.is_valid());
156 if (!file_system_context->default_file_task_runner()->
157 RunsTasksOnCurrentThread()) {
158 file_system_context->default_file_task_runner()->PostTask(
160 base::Bind(&LocalFileSyncContext::FinalizeSnapshotSync,
161 this, make_scoped_refptr(file_system_context),
162 url, sync_finish_status, done_callback));
166 SyncFileSystemBackend* backend =
167 SyncFileSystemBackend::GetBackend(file_system_context);
169 DCHECK(backend->change_tracker());
171 if (sync_finish_status == SYNC_STATUS_OK ||
172 sync_finish_status == SYNC_STATUS_HAS_CONFLICT) {
173 // Commit the in-memory mirror change.
174 backend->change_tracker()->ResetToMirrorAndCommitChangesForURL(url);
176 // Abort in-memory mirror change.
177 backend->change_tracker()->RemoveMirrorAndCommitChangesForURL(url);
178 if (sync_finish_status == SYNC_STATUS_FILE_BUSY)
179 backend->change_tracker()->DemoteChangesForURL(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 fileapi::FileSystemContext* file_system_context,
194 const fileapi::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 (fileapi::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 (fileapi::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 = fileapi::VirtualPath::DirName(url.path());
384 if (dir_path.empty() ||
385 fileapi::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 fileapi::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 fileapi::FileSystemContext* file_system_context) {
504 // This is initially called on UI thread and to be relayed to FILE thread.
505 DCHECK(file_system_context);
506 if (!file_system_context->default_file_task_runner()->
507 RunsTasksOnCurrentThread()) {
508 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
509 file_system_context->default_file_task_runner()->PostTask(
511 base::Bind(&LocalFileSyncContext::PromoteDemotedChanges,
512 this, origin, make_scoped_refptr(file_system_context)));
516 SyncFileSystemBackend* backend =
517 SyncFileSystemBackend::GetBackend(file_system_context);
519 DCHECK(backend->change_tracker());
520 if (!backend->change_tracker()->PromoteDemotedChanges())
523 io_task_runner_->PostTask(
525 base::Bind(&LocalFileSyncContext::UpdateChangesForOrigin,
529 void LocalFileSyncContext::UpdateChangesForOrigin(const GURL& origin) {
530 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
533 origins_with_pending_changes_.insert(origin);
534 ScheduleNotifyChangesUpdatedOnIOThread();
537 void LocalFileSyncContext::AddOriginChangeObserver(
538 LocalOriginChangeObserver* observer) {
539 origin_change_observers_.AddObserver(observer);
542 void LocalFileSyncContext::RemoveOriginChangeObserver(
543 LocalOriginChangeObserver* observer) {
544 origin_change_observers_.RemoveObserver(observer);
547 base::WeakPtr<SyncableFileOperationRunner>
548 LocalFileSyncContext::operation_runner() const {
549 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
550 if (operation_runner_)
551 return operation_runner_->AsWeakPtr();
552 return base::WeakPtr<SyncableFileOperationRunner>();
555 LocalFileSyncStatus* LocalFileSyncContext::sync_status() const {
556 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
557 return sync_status_.get();
560 void LocalFileSyncContext::OnSyncEnabled(const FileSystemURL& url) {
561 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
564 UpdateChangesForOrigin(url.origin());
565 if (url_syncable_callback_.is_null() ||
566 sync_status()->IsWriting(url_waiting_sync_on_io_)) {
569 // TODO(kinuko): may want to check how many pending tasks we have.
570 ui_task_runner_->PostTask(FROM_HERE, url_syncable_callback_);
571 url_syncable_callback_.Reset();
574 void LocalFileSyncContext::OnWriteEnabled(const FileSystemURL& url) {
575 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
576 // Nothing to do for now.
579 LocalFileSyncContext::~LocalFileSyncContext() {
582 void LocalFileSyncContext::ScheduleNotifyChangesUpdatedOnIOThread() {
583 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
586 if (base::Time::Now() > last_notified_changes_ + NotifyChangesDuration()) {
587 NotifyAvailableChangesOnIOThread();
588 } else if (!timer_on_io_->IsRunning()) {
590 FROM_HERE, NotifyChangesDuration(), this,
591 &LocalFileSyncContext::NotifyAvailableChangesOnIOThread);
595 void LocalFileSyncContext::NotifyAvailableChangesOnIOThread() {
596 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
599 ui_task_runner_->PostTask(
601 base::Bind(&LocalFileSyncContext::NotifyAvailableChanges,
602 this, origins_with_pending_changes_));
603 last_notified_changes_ = base::Time::Now();
604 origins_with_pending_changes_.clear();
607 void LocalFileSyncContext::NotifyAvailableChanges(
608 const std::set<GURL>& origins) {
609 FOR_EACH_OBSERVER(LocalOriginChangeObserver, origin_change_observers_,
610 OnChangesAvailableInOrigins(origins));
613 void LocalFileSyncContext::ShutdownOnIOThread() {
614 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
615 shutdown_on_io_ = true;
616 operation_runner_.reset();
617 root_delete_helper_.reset();
618 sync_status_.reset();
619 timer_on_io_.reset();
622 void LocalFileSyncContext::InitializeFileSystemContextOnIOThread(
623 const GURL& source_url,
624 FileSystemContext* file_system_context,
625 const GURL& /* root */,
626 const std::string& /* name */,
627 base::File::Error error) {
628 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
630 error = base::File::FILE_ERROR_ABORT;
631 if (error != base::File::FILE_OK) {
632 DidInitialize(source_url, file_system_context,
633 FileErrorToSyncStatusCode(error));
636 DCHECK(file_system_context);
637 SyncFileSystemBackend* backend =
638 SyncFileSystemBackend::GetBackend(file_system_context);
640 if (!backend->change_tracker()) {
641 // Create and initialize LocalFileChangeTracker and call back this method
643 std::set<GURL>* origins_with_changes = new std::set<GURL>;
644 scoped_ptr<LocalFileChangeTracker>* tracker_ptr(
645 new scoped_ptr<LocalFileChangeTracker>);
646 base::PostTaskAndReplyWithResult(
647 file_system_context->default_file_task_runner(),
649 base::Bind(&LocalFileSyncContext::InitializeChangeTrackerOnFileThread,
651 make_scoped_refptr(file_system_context),
652 origins_with_changes),
653 base::Bind(&LocalFileSyncContext::DidInitializeChangeTrackerOnIOThread,
654 this, base::Owned(tracker_ptr),
656 make_scoped_refptr(file_system_context),
657 base::Owned(origins_with_changes)));
660 if (!operation_runner_) {
661 DCHECK(!sync_status_);
662 DCHECK(!timer_on_io_);
663 sync_status_.reset(new LocalFileSyncStatus);
664 timer_on_io_.reset(new base::OneShotTimer<LocalFileSyncContext>);
665 operation_runner_.reset(new SyncableFileOperationRunner(
666 kMaxConcurrentSyncableOperation,
667 sync_status_.get()));
668 sync_status_->AddObserver(this);
670 backend->set_sync_context(this);
671 DidInitialize(source_url, file_system_context,
675 SyncStatusCode LocalFileSyncContext::InitializeChangeTrackerOnFileThread(
676 scoped_ptr<LocalFileChangeTracker>* tracker_ptr,
677 FileSystemContext* file_system_context,
678 std::set<GURL>* origins_with_changes) {
679 DCHECK(file_system_context);
681 DCHECK(origins_with_changes);
682 tracker_ptr->reset(new LocalFileChangeTracker(
683 file_system_context->partition_path(),
685 file_system_context->default_file_task_runner()));
686 const SyncStatusCode status = (*tracker_ptr)->Initialize(file_system_context);
687 if (status != SYNC_STATUS_OK)
690 // Get all origins that have pending changes.
691 std::deque<FileSystemURL> urls;
692 (*tracker_ptr)->GetNextChangedURLs(&urls, 0);
693 for (std::deque<FileSystemURL>::iterator iter = urls.begin();
694 iter != urls.end(); ++iter) {
695 origins_with_changes->insert(iter->origin());
698 // Creates snapshot directory.
699 base::CreateDirectory(local_base_path_.Append(kSnapshotDir));
704 void LocalFileSyncContext::DidInitializeChangeTrackerOnIOThread(
705 scoped_ptr<LocalFileChangeTracker>* tracker_ptr,
706 const GURL& source_url,
707 FileSystemContext* file_system_context,
708 std::set<GURL>* origins_with_changes,
709 SyncStatusCode status) {
710 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
711 DCHECK(file_system_context);
712 DCHECK(origins_with_changes);
714 status = SYNC_STATUS_ABORT;
715 if (status != SYNC_STATUS_OK) {
716 DidInitialize(source_url, file_system_context, status);
720 SyncFileSystemBackend* backend =
721 SyncFileSystemBackend::GetBackend(file_system_context);
723 backend->SetLocalFileChangeTracker(tracker_ptr->Pass());
725 origins_with_pending_changes_.insert(origins_with_changes->begin(),
726 origins_with_changes->end());
727 ScheduleNotifyChangesUpdatedOnIOThread();
729 InitializeFileSystemContextOnIOThread(source_url, file_system_context,
730 GURL(), std::string(),
731 base::File::FILE_OK);
734 void LocalFileSyncContext::DidInitialize(
735 const GURL& source_url,
736 FileSystemContext* file_system_context,
737 SyncStatusCode status) {
738 if (!ui_task_runner_->RunsTasksOnCurrentThread()) {
739 ui_task_runner_->PostTask(
741 base::Bind(&LocalFileSyncContext::DidInitialize,
743 make_scoped_refptr(file_system_context), status));
746 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
747 DCHECK(!ContainsKey(file_system_contexts_, file_system_context));
748 DCHECK(ContainsKey(pending_initialize_callbacks_, file_system_context));
750 SyncFileSystemBackend* backend =
751 SyncFileSystemBackend::GetBackend(file_system_context);
753 DCHECK(backend->change_tracker());
755 file_system_contexts_.insert(file_system_context);
757 StatusCallbackQueue& callback_queue =
758 pending_initialize_callbacks_[file_system_context];
759 for (StatusCallbackQueue::iterator iter = callback_queue.begin();
760 iter != callback_queue.end(); ++iter) {
761 ui_task_runner_->PostTask(FROM_HERE, base::Bind(*iter, status));
763 pending_initialize_callbacks_.erase(file_system_context);
766 void LocalFileSyncContext::GetNextURLsForSyncOnFileThread(
767 FileSystemContext* file_system_context,
768 std::deque<FileSystemURL>* urls) {
769 DCHECK(file_system_context);
770 DCHECK(file_system_context->default_file_task_runner()->
771 RunsTasksOnCurrentThread());
772 SyncFileSystemBackend* backend =
773 SyncFileSystemBackend::GetBackend(file_system_context);
775 DCHECK(backend->change_tracker());
776 backend->change_tracker()->GetNextChangedURLs(
777 urls, kMaxURLsToFetchForLocalSync);
780 void LocalFileSyncContext::TryPrepareForLocalSync(
781 FileSystemContext* file_system_context,
782 std::deque<FileSystemURL>* urls,
783 const LocalFileSyncInfoCallback& callback) {
784 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
787 if (shutdown_on_ui_) {
788 callback.Run(SYNC_STATUS_ABORT, LocalFileSyncInfo(),
789 webkit_blob::ScopedFile());
794 callback.Run(SYNC_STATUS_NO_CHANGE_TO_SYNC, LocalFileSyncInfo(),
795 webkit_blob::ScopedFile());
799 const FileSystemURL url = urls->front();
801 std::deque<FileSystemURL>* remaining = new std::deque<FileSystemURL>;
802 remaining->swap(*urls);
805 file_system_context, url, SYNC_SNAPSHOT,
806 base::Bind(&LocalFileSyncContext::DidTryPrepareForLocalSync,
807 this, make_scoped_refptr(file_system_context),
808 base::Owned(remaining), callback));
811 void LocalFileSyncContext::DidTryPrepareForLocalSync(
812 FileSystemContext* file_system_context,
813 std::deque<FileSystemURL>* remaining_urls,
814 const LocalFileSyncInfoCallback& callback,
815 SyncStatusCode status,
816 const LocalFileSyncInfo& sync_file_info,
817 webkit_blob::ScopedFile snapshot) {
818 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
819 if (status != SYNC_STATUS_FILE_BUSY) {
820 callback.Run(status, sync_file_info, snapshot.Pass());
823 // Recursively call TryPrepareForLocalSync with remaining_urls.
824 TryPrepareForLocalSync(file_system_context, remaining_urls, callback);
827 void LocalFileSyncContext::DidGetWritingStatusForSync(
828 FileSystemContext* file_system_context,
829 SyncStatusCode status,
830 const FileSystemURL& url,
832 const LocalFileSyncInfoCallback& callback) {
833 // This gets called on UI thread and relays the task on FILE thread.
834 DCHECK(file_system_context);
835 if (!file_system_context->default_file_task_runner()->
836 RunsTasksOnCurrentThread()) {
837 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
838 if (shutdown_on_ui_) {
839 callback.Run(SYNC_STATUS_ABORT, LocalFileSyncInfo(),
840 webkit_blob::ScopedFile());
843 file_system_context->default_file_task_runner()->PostTask(
845 base::Bind(&LocalFileSyncContext::DidGetWritingStatusForSync,
846 this, make_scoped_refptr(file_system_context),
847 status, url, sync_mode, callback));
851 SyncFileSystemBackend* backend =
852 SyncFileSystemBackend::GetBackend(file_system_context);
854 DCHECK(backend->change_tracker());
855 FileChangeList changes;
856 backend->change_tracker()->GetChangesForURL(url, &changes);
858 base::FilePath platform_path;
859 base::File::Info file_info;
860 FileSystemFileUtil* file_util =
861 file_system_context->sandbox_delegate()->sync_file_util();
864 base::File::Error file_error = file_util->GetFileInfo(
866 new FileSystemOperationContext(file_system_context)).get(),
871 webkit_blob::ScopedFile snapshot;
872 if (file_error == base::File::FILE_OK && sync_mode == SYNC_SNAPSHOT) {
873 base::FilePath snapshot_path;
874 base::CreateTemporaryFileInDir(local_base_path_.Append(kSnapshotDir),
876 if (base::CopyFile(platform_path, snapshot_path)) {
877 platform_path = snapshot_path;
878 snapshot = webkit_blob::ScopedFile(
880 webkit_blob::ScopedFile::DELETE_ON_SCOPE_OUT,
881 file_system_context->default_file_task_runner());
885 if (status == SYNC_STATUS_OK &&
886 file_error != base::File::FILE_OK &&
887 file_error != base::File::FILE_ERROR_NOT_FOUND) {
888 status = FileErrorToSyncStatusCode(file_error);
891 DCHECK(!file_info.is_symbolic_link);
893 SyncFileType file_type = SYNC_FILE_TYPE_FILE;
894 if (file_error == base::File::FILE_ERROR_NOT_FOUND)
895 file_type = SYNC_FILE_TYPE_UNKNOWN;
896 else if (file_info.is_directory)
897 file_type = SYNC_FILE_TYPE_DIRECTORY;
899 LocalFileSyncInfo sync_file_info;
900 sync_file_info.url = url;
901 sync_file_info.local_file_path = platform_path;
902 sync_file_info.metadata.file_type = file_type;
903 sync_file_info.metadata.size = file_info.size;
904 sync_file_info.metadata.last_modified = file_info.last_modified;
905 sync_file_info.changes = changes;
907 if (status == SYNC_STATUS_OK && sync_mode == SYNC_SNAPSHOT) {
908 if (!changes.empty()) {
909 // Now we create an empty mirror change record for URL (and we record
910 // changes to both mirror and original records during sync), so that
911 // we can reset to the mirror when the sync succeeds.
912 backend->change_tracker()->CreateFreshMirrorForURL(url);
915 // 'Unlock' the file for snapshot sync.
916 // (But keep it in writing status so that no other sync starts on
918 io_task_runner_->PostTask(
920 base::Bind(&LocalFileSyncContext::ClearSyncFlagOnIOThread,
921 this, url, true /* for_snapshot_sync */));
924 ui_task_runner_->PostTask(FROM_HERE,
925 base::Bind(callback, status, sync_file_info,
926 base::Passed(&snapshot)));
929 void LocalFileSyncContext::ClearSyncFlagOnIOThread(
930 const FileSystemURL& url,
931 bool for_snapshot_sync) {
932 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
935 sync_status()->EndSyncing(url);
937 if (for_snapshot_sync) {
938 // The caller will hold shared lock on this one.
939 sync_status()->StartWriting(url);
943 // Since a sync has finished the number of changes must have been updated.
944 UpdateChangesForOrigin(url.origin());
947 void LocalFileSyncContext::FinalizeSnapshotSyncOnIOThread(
948 const FileSystemURL& url) {
949 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
952 sync_status()->EndWriting(url);
954 // Since a sync has finished the number of changes must have been updated.
955 UpdateChangesForOrigin(url.origin());
958 void LocalFileSyncContext::DidApplyRemoteChange(
959 const FileSystemURL& url,
960 const SyncStatusCallback& callback_on_ui,
961 base::File::Error file_error) {
962 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
963 root_delete_helper_.reset();
964 ui_task_runner_->PostTask(
966 base::Bind(callback_on_ui, FileErrorToSyncStatusCode(file_error)));
969 void LocalFileSyncContext::DidGetFileMetadata(
970 const SyncFileMetadataCallback& callback,
971 base::File::Error file_error,
972 const base::File::Info& file_info) {
973 DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
974 SyncFileMetadata metadata;
975 if (file_error == base::File::FILE_OK) {
976 metadata.file_type = file_info.is_directory ?
977 SYNC_FILE_TYPE_DIRECTORY : SYNC_FILE_TYPE_FILE;
978 metadata.size = file_info.size;
979 metadata.last_modified = file_info.last_modified;
981 ui_task_runner_->PostTask(
983 base::Bind(callback, FileErrorToSyncStatusCode(file_error), metadata));
986 base::TimeDelta LocalFileSyncContext::NotifyChangesDuration() {
987 if (mock_notify_changes_duration_in_sec_ >= 0)
988 return base::TimeDelta::FromSeconds(mock_notify_changes_duration_in_sec_);
989 return base::TimeDelta::FromSeconds(kNotifyChangesDurationInSec);
992 void LocalFileSyncContext::DidCreateDirectoryForCopyIn(
993 FileSystemContext* file_system_context,
994 const base::FilePath& local_path,
995 const FileSystemURL& dest_url,
996 const StatusCallback& callback,
997 base::File::Error error) {
998 if (error != base::File::FILE_OK) {
1003 FileSystemURL url_for_sync = CreateSyncableFileSystemURLForSync(
1004 file_system_context, dest_url);
1005 file_system_context->operation_runner()->CopyInForeignFile(
1006 local_path, url_for_sync, callback);
1009 } // namespace sync_file_system