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