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