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