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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 536 void LocalFileSyncContext::AddOriginChangeObserver( 537 LocalOriginChangeObserver* observer) { 538 origin_change_observers_.AddObserver(observer); 539 } 540 541 void LocalFileSyncContext::RemoveOriginChangeObserver( 542 LocalOriginChangeObserver* observer) { 543 origin_change_observers_.RemoveObserver(observer); 544 } 545 546 base::WeakPtr<SyncableFileOperationRunner> 547 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 554 LocalFileSyncStatus* LocalFileSyncContext::sync_status() const { 555 DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); 556 return sync_status_.get(); 557 } 558 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 573 void LocalFileSyncContext::OnWriteEnabled(const FileSystemURL& url) { 574 DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); 575 // Nothing to do for now. 576 } 577 578 LocalFileSyncContext::~LocalFileSyncContext() { 579 } 580 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 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 606 void LocalFileSyncContext::NotifyAvailableChanges( 607 const std::set<GURL>& origins) { 608 FOR_EACH_OBSERVER(LocalOriginChangeObserver, origin_change_observers_, 609 OnChangesAvailableInOrigins(origins)); 610 } 611 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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