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 30 using fileapi::FileSystemContext; 31 using fileapi::FileSystemFileUtil; 32 using fileapi::FileSystemOperation; 33 using fileapi::FileSystemOperationContext; 34 using fileapi::FileSystemURL; 35 36 namespace sync_file_system { 37 38 namespace { 39 40 const int kMaxConcurrentSyncableOperation = 3; 41 const int kNotifyChangesDurationInSec = 1; 42 const int kMaxURLsToFetchForLocalSync = 5; 43 44 const base::FilePath::CharType kSnapshotDir[] = FILE_PATH_LITERAL("snapshots"); 45 46 } // namespace 47 48 LocalFileSyncContext::LocalFileSyncContext( 49 const base::FilePath& base_path, 50 base::SingleThreadTaskRunner* ui_task_runner, 51 base::SingleThreadTaskRunner* io_task_runner) 52 : local_base_path_(base_path.Append(FILE_PATH_LITERAL("local"))), 53 ui_task_runner_(ui_task_runner), 54 io_task_runner_(io_task_runner), 55 shutdown_on_ui_(false), 56 shutdown_on_io_(false), 57 mock_notify_changes_duration_in_sec_(-1) { 58 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread()); 59 } 60 61 void LocalFileSyncContext::MaybeInitializeFileSystemContext( 62 const GURL& source_url, 63 FileSystemContext* file_system_context, 64 const SyncStatusCallback& callback) { 65 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread()); 66 if (ContainsKey(file_system_contexts_, file_system_context)) { 67 // The context has been already initialized. Just dispatch the callback 68 // with SYNC_STATUS_OK. 69 ui_task_runner_->PostTask(FROM_HERE, base::Bind(callback, SYNC_STATUS_OK)); 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 95 void 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 103 void 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 120 void LocalFileSyncContext::ClearChangesForURL( 121 FileSystemContext* file_system_context, 122 const FileSystemURL& url, 123 const base::Closure& done_callback) { 124 // This is initially called on UI thread and to be relayed to FILE thread. 125 DCHECK(file_system_context); 126 if (!file_system_context->default_file_task_runner()-> 127 RunsTasksOnCurrentThread()) { 128 DCHECK(ui_task_runner_->RunsTasksOnCurrentThread()); 129 file_system_context->default_file_task_runner()->PostTask( 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 147 void LocalFileSyncContext::FinalizeSnapshotSync( 148 fileapi::FileSystemContext* file_system_context, 149 const fileapi::FileSystemURL& url, 150 SyncStatusCode sync_finish_status, 151 const base::Closure& done_callback) { 152 DCHECK(file_system_context); 153 DCHECK(url.is_valid()); 154 if (!file_system_context->default_file_task_runner()-> 155 RunsTasksOnCurrentThread()) { 156 file_system_context->default_file_task_runner()->PostTask( 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 188 void 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 215 void 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 244 void 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 268 void 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 301 void 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 325 void 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 351 void 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 413 void 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 444 void 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 466 void 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 497 void LocalFileSyncContext::AddOriginChangeObserver( 498 LocalOriginChangeObserver* observer) { 499 origin_change_observers_.AddObserver(observer); 500 } 501 502 void LocalFileSyncContext::RemoveOriginChangeObserver( 503 LocalOriginChangeObserver* observer) { 504 origin_change_observers_.RemoveObserver(observer); 505 } 506 507 base::WeakPtr<SyncableFileOperationRunner> 508 LocalFileSyncContext::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 515 LocalFileSyncStatus* LocalFileSyncContext::sync_status() const { 516 DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); 517 return sync_status_.get(); 518 } 519 520 void 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 535 void LocalFileSyncContext::OnWriteEnabled(const FileSystemURL& url) { 536 DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); 537 // Nothing to do for now. 538 } 539 540 LocalFileSyncContext::~LocalFileSyncContext() { 541 } 542 543 void 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 556 void 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 568 void LocalFileSyncContext::NotifyAvailableChanges( 569 const std::set<GURL>& origins) { 570 FOR_EACH_OBSERVER(LocalOriginChangeObserver, origin_change_observers_, 571 OnChangesAvailableInOrigins(origins)); 572 } 573 574 void 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 583 void 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 636 SyncStatusCode 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 664 void 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 694 void 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 726 void 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 740 void 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 771 void 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 787 void 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 888 void 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 907 void 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 919 void 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 931 void 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 950 base::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 956 void 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