1 // Copyright (c) 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 "storage/browser/fileapi/copy_or_move_operation_delegate.h" 6 7 #include "base/bind.h" 8 #include "base/files/file_path.h" 9 #include "net/base/io_buffer.h" 10 #include "net/base/net_errors.h" 11 #include "storage/browser/blob/file_stream_reader.h" 12 #include "storage/browser/fileapi/copy_or_move_file_validator.h" 13 #include "storage/browser/fileapi/file_observers.h" 14 #include "storage/browser/fileapi/file_stream_writer.h" 15 #include "storage/browser/fileapi/file_system_context.h" 16 #include "storage/browser/fileapi/file_system_operation_runner.h" 17 #include "storage/browser/fileapi/file_system_url.h" 18 #include "storage/browser/fileapi/recursive_operation_delegate.h" 19 #include "storage/common/blob/shareable_file_reference.h" 20 #include "storage/common/fileapi/file_system_util.h" 21 22 namespace storage { 23 24 const int64 kFlushIntervalInBytes = 10 << 20; // 10MB. 25 26 class CopyOrMoveOperationDelegate::CopyOrMoveImpl { 27 public: 28 virtual ~CopyOrMoveImpl() {} 29 virtual void Run( 30 const CopyOrMoveOperationDelegate::StatusCallback& callback) = 0; 31 virtual void Cancel() = 0; 32 33 protected: 34 CopyOrMoveImpl() {} 35 36 private: 37 DISALLOW_COPY_AND_ASSIGN(CopyOrMoveImpl); 38 }; 39 40 namespace { 41 42 // Copies a file on a (same) file system. Just delegate the operation to 43 // |operation_runner|. 44 class CopyOrMoveOnSameFileSystemImpl 45 : public CopyOrMoveOperationDelegate::CopyOrMoveImpl { 46 public: 47 CopyOrMoveOnSameFileSystemImpl( 48 FileSystemOperationRunner* operation_runner, 49 CopyOrMoveOperationDelegate::OperationType operation_type, 50 const FileSystemURL& src_url, 51 const FileSystemURL& dest_url, 52 CopyOrMoveOperationDelegate::CopyOrMoveOption option, 53 const FileSystemOperation::CopyFileProgressCallback& 54 file_progress_callback) 55 : operation_runner_(operation_runner), 56 operation_type_(operation_type), 57 src_url_(src_url), 58 dest_url_(dest_url), 59 option_(option), 60 file_progress_callback_(file_progress_callback) { 61 } 62 63 virtual void Run( 64 const CopyOrMoveOperationDelegate::StatusCallback& callback) OVERRIDE { 65 if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_MOVE) { 66 operation_runner_->MoveFileLocal(src_url_, dest_url_, option_, callback); 67 } else { 68 operation_runner_->CopyFileLocal( 69 src_url_, dest_url_, option_, file_progress_callback_, callback); 70 } 71 } 72 73 virtual void Cancel() OVERRIDE { 74 // We can do nothing for the copy/move operation on a local file system. 75 // Assuming the operation is quickly done, it should be ok to just wait 76 // for the completion. 77 } 78 79 private: 80 FileSystemOperationRunner* operation_runner_; 81 CopyOrMoveOperationDelegate::OperationType operation_type_; 82 FileSystemURL src_url_; 83 FileSystemURL dest_url_; 84 CopyOrMoveOperationDelegate::CopyOrMoveOption option_; 85 FileSystemOperation::CopyFileProgressCallback file_progress_callback_; 86 DISALLOW_COPY_AND_ASSIGN(CopyOrMoveOnSameFileSystemImpl); 87 }; 88 89 // Specifically for cross file system copy/move operation, this class creates 90 // a snapshot file, validates it if necessary, runs copying process, 91 // validates the created file, and removes source file for move (noop for 92 // copy). 93 class SnapshotCopyOrMoveImpl 94 : public CopyOrMoveOperationDelegate::CopyOrMoveImpl { 95 public: 96 SnapshotCopyOrMoveImpl( 97 FileSystemOperationRunner* operation_runner, 98 CopyOrMoveOperationDelegate::OperationType operation_type, 99 const FileSystemURL& src_url, 100 const FileSystemURL& dest_url, 101 CopyOrMoveOperationDelegate::CopyOrMoveOption option, 102 CopyOrMoveFileValidatorFactory* validator_factory, 103 const FileSystemOperation::CopyFileProgressCallback& 104 file_progress_callback) 105 : operation_runner_(operation_runner), 106 operation_type_(operation_type), 107 src_url_(src_url), 108 dest_url_(dest_url), 109 option_(option), 110 validator_factory_(validator_factory), 111 file_progress_callback_(file_progress_callback), 112 cancel_requested_(false), 113 weak_factory_(this) { 114 } 115 116 virtual void Run( 117 const CopyOrMoveOperationDelegate::StatusCallback& callback) OVERRIDE { 118 file_progress_callback_.Run(0); 119 operation_runner_->CreateSnapshotFile( 120 src_url_, 121 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterCreateSnapshot, 122 weak_factory_.GetWeakPtr(), callback)); 123 } 124 125 virtual void Cancel() OVERRIDE { 126 cancel_requested_ = true; 127 } 128 129 private: 130 void RunAfterCreateSnapshot( 131 const CopyOrMoveOperationDelegate::StatusCallback& callback, 132 base::File::Error error, 133 const base::File::Info& file_info, 134 const base::FilePath& platform_path, 135 const scoped_refptr<storage::ShareableFileReference>& file_ref) { 136 if (cancel_requested_) 137 error = base::File::FILE_ERROR_ABORT; 138 139 if (error != base::File::FILE_OK) { 140 callback.Run(error); 141 return; 142 } 143 144 // For now we assume CreateSnapshotFile always return a valid local file 145 // path. 146 DCHECK(!platform_path.empty()); 147 148 if (!validator_factory_) { 149 // No validation is needed. 150 RunAfterPreWriteValidation(platform_path, file_info, file_ref, callback, 151 base::File::FILE_OK); 152 return; 153 } 154 155 // Run pre write validation. 156 PreWriteValidation( 157 platform_path, 158 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterPreWriteValidation, 159 weak_factory_.GetWeakPtr(), 160 platform_path, file_info, file_ref, callback)); 161 } 162 163 void RunAfterPreWriteValidation( 164 const base::FilePath& platform_path, 165 const base::File::Info& file_info, 166 const scoped_refptr<storage::ShareableFileReference>& file_ref, 167 const CopyOrMoveOperationDelegate::StatusCallback& callback, 168 base::File::Error error) { 169 if (cancel_requested_) 170 error = base::File::FILE_ERROR_ABORT; 171 172 if (error != base::File::FILE_OK) { 173 callback.Run(error); 174 return; 175 } 176 177 // |file_ref| is unused but necessary to keep the file alive until 178 // CopyInForeignFile() is completed. 179 operation_runner_->CopyInForeignFile( 180 platform_path, dest_url_, 181 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterCopyInForeignFile, 182 weak_factory_.GetWeakPtr(), file_info, file_ref, callback)); 183 } 184 185 void RunAfterCopyInForeignFile( 186 const base::File::Info& file_info, 187 const scoped_refptr<storage::ShareableFileReference>& file_ref, 188 const CopyOrMoveOperationDelegate::StatusCallback& callback, 189 base::File::Error error) { 190 if (cancel_requested_) 191 error = base::File::FILE_ERROR_ABORT; 192 193 if (error != base::File::FILE_OK) { 194 callback.Run(error); 195 return; 196 } 197 198 file_progress_callback_.Run(file_info.size); 199 200 if (option_ == FileSystemOperation::OPTION_NONE) { 201 RunAfterTouchFile(callback, base::File::FILE_OK); 202 return; 203 } 204 205 operation_runner_->TouchFile( 206 dest_url_, base::Time::Now() /* last_access */, 207 file_info.last_modified, 208 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterTouchFile, 209 weak_factory_.GetWeakPtr(), callback)); 210 } 211 212 void RunAfterTouchFile( 213 const CopyOrMoveOperationDelegate::StatusCallback& callback, 214 base::File::Error error) { 215 // Even if TouchFile is failed, just ignore it. 216 217 if (cancel_requested_) { 218 callback.Run(base::File::FILE_ERROR_ABORT); 219 return; 220 } 221 222 // |validator_| is NULL when the destination filesystem does not do 223 // validation. 224 if (!validator_) { 225 // No validation is needed. 226 RunAfterPostWriteValidation(callback, base::File::FILE_OK); 227 return; 228 } 229 230 PostWriteValidation( 231 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterPostWriteValidation, 232 weak_factory_.GetWeakPtr(), callback)); 233 } 234 235 void RunAfterPostWriteValidation( 236 const CopyOrMoveOperationDelegate::StatusCallback& callback, 237 base::File::Error error) { 238 if (cancel_requested_) { 239 callback.Run(base::File::FILE_ERROR_ABORT); 240 return; 241 } 242 243 if (error != base::File::FILE_OK) { 244 // Failed to validate. Remove the destination file. 245 operation_runner_->Remove( 246 dest_url_, true /* recursive */, 247 base::Bind(&SnapshotCopyOrMoveImpl::DidRemoveDestForError, 248 weak_factory_.GetWeakPtr(), error, callback)); 249 return; 250 } 251 252 if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_COPY) { 253 callback.Run(base::File::FILE_OK); 254 return; 255 } 256 257 DCHECK_EQ(CopyOrMoveOperationDelegate::OPERATION_MOVE, operation_type_); 258 259 // Remove the source for finalizing move operation. 260 operation_runner_->Remove( 261 src_url_, true /* recursive */, 262 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterRemoveSourceForMove, 263 weak_factory_.GetWeakPtr(), callback)); 264 } 265 266 void RunAfterRemoveSourceForMove( 267 const CopyOrMoveOperationDelegate::StatusCallback& callback, 268 base::File::Error error) { 269 if (cancel_requested_) 270 error = base::File::FILE_ERROR_ABORT; 271 272 if (error == base::File::FILE_ERROR_NOT_FOUND) 273 error = base::File::FILE_OK; 274 callback.Run(error); 275 } 276 277 void DidRemoveDestForError( 278 base::File::Error prior_error, 279 const CopyOrMoveOperationDelegate::StatusCallback& callback, 280 base::File::Error error) { 281 if (error != base::File::FILE_OK) { 282 VLOG(1) << "Error removing destination file after validation error: " 283 << error; 284 } 285 callback.Run(prior_error); 286 } 287 288 // Runs pre-write validation. 289 void PreWriteValidation( 290 const base::FilePath& platform_path, 291 const CopyOrMoveOperationDelegate::StatusCallback& callback) { 292 DCHECK(validator_factory_); 293 validator_.reset( 294 validator_factory_->CreateCopyOrMoveFileValidator( 295 src_url_, platform_path)); 296 validator_->StartPreWriteValidation(callback); 297 } 298 299 // Runs post-write validation. 300 void PostWriteValidation( 301 const CopyOrMoveOperationDelegate::StatusCallback& callback) { 302 operation_runner_->CreateSnapshotFile( 303 dest_url_, 304 base::Bind( 305 &SnapshotCopyOrMoveImpl::PostWriteValidationAfterCreateSnapshotFile, 306 weak_factory_.GetWeakPtr(), callback)); 307 } 308 309 void PostWriteValidationAfterCreateSnapshotFile( 310 const CopyOrMoveOperationDelegate::StatusCallback& callback, 311 base::File::Error error, 312 const base::File::Info& file_info, 313 const base::FilePath& platform_path, 314 const scoped_refptr<storage::ShareableFileReference>& file_ref) { 315 if (cancel_requested_) 316 error = base::File::FILE_ERROR_ABORT; 317 318 if (error != base::File::FILE_OK) { 319 callback.Run(error); 320 return; 321 } 322 323 DCHECK(validator_); 324 // Note: file_ref passed here to keep the file alive until after 325 // the StartPostWriteValidation operation finishes. 326 validator_->StartPostWriteValidation( 327 platform_path, 328 base::Bind(&SnapshotCopyOrMoveImpl::DidPostWriteValidation, 329 weak_factory_.GetWeakPtr(), file_ref, callback)); 330 } 331 332 // |file_ref| is unused; it is passed here to make sure the reference is 333 // alive until after post-write validation is complete. 334 void DidPostWriteValidation( 335 const scoped_refptr<storage::ShareableFileReference>& file_ref, 336 const CopyOrMoveOperationDelegate::StatusCallback& callback, 337 base::File::Error error) { 338 callback.Run(error); 339 } 340 341 FileSystemOperationRunner* operation_runner_; 342 CopyOrMoveOperationDelegate::OperationType operation_type_; 343 FileSystemURL src_url_; 344 FileSystemURL dest_url_; 345 346 CopyOrMoveOperationDelegate::CopyOrMoveOption option_; 347 CopyOrMoveFileValidatorFactory* validator_factory_; 348 scoped_ptr<CopyOrMoveFileValidator> validator_; 349 FileSystemOperation::CopyFileProgressCallback file_progress_callback_; 350 bool cancel_requested_; 351 base::WeakPtrFactory<SnapshotCopyOrMoveImpl> weak_factory_; 352 DISALLOW_COPY_AND_ASSIGN(SnapshotCopyOrMoveImpl); 353 }; 354 355 // The size of buffer for StreamCopyHelper. 356 const int kReadBufferSize = 32768; 357 358 // To avoid too many progress callbacks, it should be called less 359 // frequently than 50ms. 360 const int kMinProgressCallbackInvocationSpanInMilliseconds = 50; 361 362 // Specifically for cross file system copy/move operation, this class uses 363 // stream reader and writer for copying. Validator is not supported, so if 364 // necessary SnapshotCopyOrMoveImpl should be used. 365 class StreamCopyOrMoveImpl 366 : public CopyOrMoveOperationDelegate::CopyOrMoveImpl { 367 public: 368 StreamCopyOrMoveImpl( 369 FileSystemOperationRunner* operation_runner, 370 FileSystemContext* file_system_context, 371 CopyOrMoveOperationDelegate::OperationType operation_type, 372 const FileSystemURL& src_url, 373 const FileSystemURL& dest_url, 374 CopyOrMoveOperationDelegate::CopyOrMoveOption option, 375 scoped_ptr<storage::FileStreamReader> reader, 376 scoped_ptr<FileStreamWriter> writer, 377 const FileSystemOperation::CopyFileProgressCallback& 378 file_progress_callback) 379 : operation_runner_(operation_runner), 380 file_system_context_(file_system_context), 381 operation_type_(operation_type), 382 src_url_(src_url), 383 dest_url_(dest_url), 384 option_(option), 385 reader_(reader.Pass()), 386 writer_(writer.Pass()), 387 file_progress_callback_(file_progress_callback), 388 cancel_requested_(false), 389 weak_factory_(this) {} 390 391 virtual void Run( 392 const CopyOrMoveOperationDelegate::StatusCallback& callback) OVERRIDE { 393 // Reader can be created even if the entry does not exist or the entry is 394 // a directory. To check errors before destination file creation, 395 // check metadata first. 396 operation_runner_->GetMetadata( 397 src_url_, 398 base::Bind(&StreamCopyOrMoveImpl::RunAfterGetMetadataForSource, 399 weak_factory_.GetWeakPtr(), callback)); 400 } 401 402 virtual void Cancel() OVERRIDE { 403 cancel_requested_ = true; 404 if (copy_helper_) 405 copy_helper_->Cancel(); 406 } 407 408 private: 409 void NotifyOnStartUpdate(const FileSystemURL& url) { 410 if (file_system_context_->GetUpdateObservers(url.type())) { 411 file_system_context_->GetUpdateObservers(url.type()) 412 ->Notify(&FileUpdateObserver::OnStartUpdate, MakeTuple(url)); 413 } 414 } 415 416 void NotifyOnModifyFile(const FileSystemURL& url) { 417 if (file_system_context_->GetChangeObservers(url.type())) { 418 file_system_context_->GetChangeObservers(url.type()) 419 ->Notify(&FileChangeObserver::OnModifyFile, MakeTuple(url)); 420 } 421 } 422 423 void NotifyOnEndUpdate(const FileSystemURL& url) { 424 if (file_system_context_->GetUpdateObservers(url.type())) { 425 file_system_context_->GetUpdateObservers(url.type()) 426 ->Notify(&FileUpdateObserver::OnEndUpdate, MakeTuple(url)); 427 } 428 } 429 430 void RunAfterGetMetadataForSource( 431 const CopyOrMoveOperationDelegate::StatusCallback& callback, 432 base::File::Error error, 433 const base::File::Info& file_info) { 434 if (cancel_requested_) 435 error = base::File::FILE_ERROR_ABORT; 436 437 if (error != base::File::FILE_OK) { 438 callback.Run(error); 439 return; 440 } 441 442 if (file_info.is_directory) { 443 // If not a directory, failed with appropriate error code. 444 callback.Run(base::File::FILE_ERROR_NOT_A_FILE); 445 return; 446 } 447 448 // To use FileStreamWriter, we need to ensure the destination file exists. 449 operation_runner_->CreateFile( 450 dest_url_, 451 true /* exclusive */, 452 base::Bind(&StreamCopyOrMoveImpl::RunAfterCreateFileForDestination, 453 weak_factory_.GetWeakPtr(), 454 callback, 455 file_info.last_modified)); 456 } 457 458 void RunAfterCreateFileForDestination( 459 const CopyOrMoveOperationDelegate::StatusCallback& callback, 460 const base::Time& last_modified, 461 base::File::Error error) { 462 if (cancel_requested_) 463 error = base::File::FILE_ERROR_ABORT; 464 // This conversion is to return the consistent status code with 465 // FileSystemFileUtil::Copy. 466 if (error == base::File::FILE_ERROR_NOT_A_FILE) 467 error = base::File::FILE_ERROR_INVALID_OPERATION; 468 469 if (error != base::File::FILE_OK && 470 error != base::File::FILE_ERROR_EXISTS) { 471 callback.Run(error); 472 return; 473 } 474 475 if (error == base::File::FILE_ERROR_EXISTS) { 476 operation_runner_->Truncate( 477 dest_url_, 478 0 /* length */, 479 base::Bind(&StreamCopyOrMoveImpl::RunAfterTruncateForDestination, 480 weak_factory_.GetWeakPtr(), 481 callback, 482 last_modified)); 483 return; 484 } 485 RunAfterTruncateForDestination( 486 callback, last_modified, base::File::FILE_OK); 487 } 488 489 void RunAfterTruncateForDestination( 490 const CopyOrMoveOperationDelegate::StatusCallback& callback, 491 const base::Time& last_modified, 492 base::File::Error error) { 493 if (cancel_requested_) 494 error = base::File::FILE_ERROR_ABORT; 495 496 if (error != base::File::FILE_OK) { 497 callback.Run(error); 498 return; 499 } 500 501 const bool need_flush = dest_url_.mount_option().copy_sync_option() == 502 storage::COPY_SYNC_OPTION_SYNC; 503 504 NotifyOnStartUpdate(dest_url_); 505 DCHECK(!copy_helper_); 506 copy_helper_.reset( 507 new CopyOrMoveOperationDelegate::StreamCopyHelper( 508 reader_.Pass(), writer_.Pass(), 509 need_flush, 510 kReadBufferSize, 511 file_progress_callback_, 512 base::TimeDelta::FromMilliseconds( 513 kMinProgressCallbackInvocationSpanInMilliseconds))); 514 copy_helper_->Run( 515 base::Bind(&StreamCopyOrMoveImpl::RunAfterStreamCopy, 516 weak_factory_.GetWeakPtr(), callback, last_modified)); 517 } 518 519 void RunAfterStreamCopy( 520 const CopyOrMoveOperationDelegate::StatusCallback& callback, 521 const base::Time& last_modified, 522 base::File::Error error) { 523 NotifyOnModifyFile(dest_url_); 524 NotifyOnEndUpdate(dest_url_); 525 if (cancel_requested_) 526 error = base::File::FILE_ERROR_ABORT; 527 528 if (error != base::File::FILE_OK) { 529 callback.Run(error); 530 return; 531 } 532 533 if (option_ == FileSystemOperation::OPTION_NONE) { 534 RunAfterTouchFile(callback, base::File::FILE_OK); 535 return; 536 } 537 538 operation_runner_->TouchFile( 539 dest_url_, base::Time::Now() /* last_access */, last_modified, 540 base::Bind(&StreamCopyOrMoveImpl::RunAfterTouchFile, 541 weak_factory_.GetWeakPtr(), callback)); 542 } 543 544 void RunAfterTouchFile( 545 const CopyOrMoveOperationDelegate::StatusCallback& callback, 546 base::File::Error error) { 547 // Even if TouchFile is failed, just ignore it. 548 if (cancel_requested_) { 549 callback.Run(base::File::FILE_ERROR_ABORT); 550 return; 551 } 552 553 if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_COPY) { 554 callback.Run(base::File::FILE_OK); 555 return; 556 } 557 558 DCHECK_EQ(CopyOrMoveOperationDelegate::OPERATION_MOVE, operation_type_); 559 560 // Remove the source for finalizing move operation. 561 operation_runner_->Remove( 562 src_url_, false /* recursive */, 563 base::Bind(&StreamCopyOrMoveImpl::RunAfterRemoveForMove, 564 weak_factory_.GetWeakPtr(), callback)); 565 } 566 567 void RunAfterRemoveForMove( 568 const CopyOrMoveOperationDelegate::StatusCallback& callback, 569 base::File::Error error) { 570 if (cancel_requested_) 571 error = base::File::FILE_ERROR_ABORT; 572 if (error == base::File::FILE_ERROR_NOT_FOUND) 573 error = base::File::FILE_OK; 574 callback.Run(error); 575 } 576 577 FileSystemOperationRunner* operation_runner_; 578 scoped_refptr<FileSystemContext> file_system_context_; 579 CopyOrMoveOperationDelegate::OperationType operation_type_; 580 FileSystemURL src_url_; 581 FileSystemURL dest_url_; 582 CopyOrMoveOperationDelegate::CopyOrMoveOption option_; 583 scoped_ptr<storage::FileStreamReader> reader_; 584 scoped_ptr<FileStreamWriter> writer_; 585 FileSystemOperation::CopyFileProgressCallback file_progress_callback_; 586 scoped_ptr<CopyOrMoveOperationDelegate::StreamCopyHelper> copy_helper_; 587 bool cancel_requested_; 588 base::WeakPtrFactory<StreamCopyOrMoveImpl> weak_factory_; 589 DISALLOW_COPY_AND_ASSIGN(StreamCopyOrMoveImpl); 590 }; 591 592 } // namespace 593 594 CopyOrMoveOperationDelegate::StreamCopyHelper::StreamCopyHelper( 595 scoped_ptr<storage::FileStreamReader> reader, 596 scoped_ptr<FileStreamWriter> writer, 597 bool need_flush, 598 int buffer_size, 599 const FileSystemOperation::CopyFileProgressCallback& file_progress_callback, 600 const base::TimeDelta& min_progress_callback_invocation_span) 601 : reader_(reader.Pass()), 602 writer_(writer.Pass()), 603 need_flush_(need_flush), 604 file_progress_callback_(file_progress_callback), 605 io_buffer_(new net::IOBufferWithSize(buffer_size)), 606 num_copied_bytes_(0), 607 previous_flush_offset_(0), 608 min_progress_callback_invocation_span_( 609 min_progress_callback_invocation_span), 610 cancel_requested_(false), 611 weak_factory_(this) { 612 } 613 614 CopyOrMoveOperationDelegate::StreamCopyHelper::~StreamCopyHelper() { 615 } 616 617 void CopyOrMoveOperationDelegate::StreamCopyHelper::Run( 618 const StatusCallback& callback) { 619 file_progress_callback_.Run(0); 620 last_progress_callback_invocation_time_ = base::Time::Now(); 621 Read(callback); 622 } 623 624 void CopyOrMoveOperationDelegate::StreamCopyHelper::Cancel() { 625 cancel_requested_ = true; 626 } 627 628 void CopyOrMoveOperationDelegate::StreamCopyHelper::Read( 629 const StatusCallback& callback) { 630 int result = reader_->Read( 631 io_buffer_.get(), io_buffer_->size(), 632 base::Bind(&StreamCopyHelper::DidRead, 633 weak_factory_.GetWeakPtr(), callback)); 634 if (result != net::ERR_IO_PENDING) 635 DidRead(callback, result); 636 } 637 638 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidRead( 639 const StatusCallback& callback, int result) { 640 if (cancel_requested_) { 641 callback.Run(base::File::FILE_ERROR_ABORT); 642 return; 643 } 644 645 if (result < 0) { 646 callback.Run(NetErrorToFileError(result)); 647 return; 648 } 649 650 if (result == 0) { 651 // Here is the EOF. 652 if (need_flush_) 653 Flush(callback, true /* is_eof */); 654 else 655 callback.Run(base::File::FILE_OK); 656 return; 657 } 658 659 Write(callback, new net::DrainableIOBuffer(io_buffer_.get(), result)); 660 } 661 662 void CopyOrMoveOperationDelegate::StreamCopyHelper::Write( 663 const StatusCallback& callback, 664 scoped_refptr<net::DrainableIOBuffer> buffer) { 665 DCHECK_GT(buffer->BytesRemaining(), 0); 666 667 int result = writer_->Write( 668 buffer.get(), buffer->BytesRemaining(), 669 base::Bind(&StreamCopyHelper::DidWrite, 670 weak_factory_.GetWeakPtr(), callback, buffer)); 671 if (result != net::ERR_IO_PENDING) 672 DidWrite(callback, buffer, result); 673 } 674 675 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidWrite( 676 const StatusCallback& callback, 677 scoped_refptr<net::DrainableIOBuffer> buffer, 678 int result) { 679 if (cancel_requested_) { 680 callback.Run(base::File::FILE_ERROR_ABORT); 681 return; 682 } 683 684 if (result < 0) { 685 callback.Run(NetErrorToFileError(result)); 686 return; 687 } 688 689 buffer->DidConsume(result); 690 num_copied_bytes_ += result; 691 692 // Check the elapsed time since last |file_progress_callback_| invocation. 693 base::Time now = base::Time::Now(); 694 if (now - last_progress_callback_invocation_time_ >= 695 min_progress_callback_invocation_span_) { 696 file_progress_callback_.Run(num_copied_bytes_); 697 last_progress_callback_invocation_time_ = now; 698 } 699 700 if (buffer->BytesRemaining() > 0) { 701 Write(callback, buffer); 702 return; 703 } 704 705 if (need_flush_ && 706 (num_copied_bytes_ - previous_flush_offset_) > kFlushIntervalInBytes) { 707 Flush(callback, false /* not is_eof */); 708 } else { 709 Read(callback); 710 } 711 } 712 713 void CopyOrMoveOperationDelegate::StreamCopyHelper::Flush( 714 const StatusCallback& callback, bool is_eof) { 715 int result = writer_->Flush( 716 base::Bind(&StreamCopyHelper::DidFlush, 717 weak_factory_.GetWeakPtr(), callback, is_eof)); 718 if (result != net::ERR_IO_PENDING) 719 DidFlush(callback, is_eof, result); 720 } 721 722 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidFlush( 723 const StatusCallback& callback, bool is_eof, int result) { 724 if (cancel_requested_) { 725 callback.Run(base::File::FILE_ERROR_ABORT); 726 return; 727 } 728 729 previous_flush_offset_ = num_copied_bytes_; 730 if (is_eof) 731 callback.Run(NetErrorToFileError(result)); 732 else 733 Read(callback); 734 } 735 736 CopyOrMoveOperationDelegate::CopyOrMoveOperationDelegate( 737 FileSystemContext* file_system_context, 738 const FileSystemURL& src_root, 739 const FileSystemURL& dest_root, 740 OperationType operation_type, 741 CopyOrMoveOption option, 742 const CopyProgressCallback& progress_callback, 743 const StatusCallback& callback) 744 : RecursiveOperationDelegate(file_system_context), 745 src_root_(src_root), 746 dest_root_(dest_root), 747 operation_type_(operation_type), 748 option_(option), 749 progress_callback_(progress_callback), 750 callback_(callback), 751 weak_factory_(this) { 752 same_file_system_ = src_root_.IsInSameFileSystem(dest_root_); 753 } 754 755 CopyOrMoveOperationDelegate::~CopyOrMoveOperationDelegate() { 756 STLDeleteElements(&running_copy_set_); 757 } 758 759 void CopyOrMoveOperationDelegate::Run() { 760 // Not supported; this should never be called. 761 NOTREACHED(); 762 } 763 764 void CopyOrMoveOperationDelegate::RunRecursively() { 765 // Perform light-weight checks first. 766 767 // It is an error to try to copy/move an entry into its child. 768 if (same_file_system_ && src_root_.path().IsParent(dest_root_.path())) { 769 callback_.Run(base::File::FILE_ERROR_INVALID_OPERATION); 770 return; 771 } 772 773 if (same_file_system_ && src_root_.path() == dest_root_.path()) { 774 // In JS API this should return error, but we return success because Pepper 775 // wants to return success and we have a code path that returns error in 776 // Blink for JS (http://crbug.com/329517). 777 callback_.Run(base::File::FILE_OK); 778 return; 779 } 780 781 // Start to process the source directory recursively. 782 // TODO(kinuko): This could be too expensive for same_file_system_==true 783 // and operation==MOVE case, probably we can just rename the root directory. 784 // http://crbug.com/172187 785 StartRecursiveOperation(src_root_, callback_); 786 } 787 788 void CopyOrMoveOperationDelegate::ProcessFile( 789 const FileSystemURL& src_url, 790 const StatusCallback& callback) { 791 if (!progress_callback_.is_null()) { 792 progress_callback_.Run( 793 FileSystemOperation::BEGIN_COPY_ENTRY, src_url, FileSystemURL(), 0); 794 } 795 796 FileSystemURL dest_url = CreateDestURL(src_url); 797 CopyOrMoveImpl* impl = NULL; 798 if (same_file_system_ && 799 (file_system_context() 800 ->GetFileSystemBackend(src_url.type()) 801 ->HasInplaceCopyImplementation(src_url.type()) || 802 operation_type_ == OPERATION_MOVE)) { 803 impl = new CopyOrMoveOnSameFileSystemImpl( 804 operation_runner(), operation_type_, src_url, dest_url, option_, 805 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress, 806 weak_factory_.GetWeakPtr(), src_url)); 807 } else { 808 // Cross filesystem case. 809 base::File::Error error = base::File::FILE_ERROR_FAILED; 810 CopyOrMoveFileValidatorFactory* validator_factory = 811 file_system_context()->GetCopyOrMoveFileValidatorFactory( 812 dest_root_.type(), &error); 813 if (error != base::File::FILE_OK) { 814 callback.Run(error); 815 return; 816 } 817 818 if (!validator_factory) { 819 scoped_ptr<storage::FileStreamReader> reader = 820 file_system_context()->CreateFileStreamReader( 821 src_url, 0 /* offset */, storage::kMaximumLength, base::Time()); 822 scoped_ptr<FileStreamWriter> writer = 823 file_system_context()->CreateFileStreamWriter(dest_url, 0); 824 if (reader && writer) { 825 impl = new StreamCopyOrMoveImpl( 826 operation_runner(), 827 file_system_context(), 828 operation_type_, 829 src_url, 830 dest_url, 831 option_, 832 reader.Pass(), 833 writer.Pass(), 834 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress, 835 weak_factory_.GetWeakPtr(), 836 src_url)); 837 } 838 } 839 840 if (!impl) { 841 impl = new SnapshotCopyOrMoveImpl( 842 operation_runner(), operation_type_, src_url, dest_url, option_, 843 validator_factory, 844 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress, 845 weak_factory_.GetWeakPtr(), src_url)); 846 } 847 } 848 849 // Register the running task. 850 running_copy_set_.insert(impl); 851 impl->Run(base::Bind( 852 &CopyOrMoveOperationDelegate::DidCopyOrMoveFile, 853 weak_factory_.GetWeakPtr(), src_url, dest_url, callback, impl)); 854 } 855 856 void CopyOrMoveOperationDelegate::ProcessDirectory( 857 const FileSystemURL& src_url, 858 const StatusCallback& callback) { 859 if (src_url == src_root_) { 860 // The src_root_ looks to be a directory. 861 // Try removing the dest_root_ to see if it exists and/or it is an 862 // empty directory. 863 // We do not invoke |progress_callback_| for source root, because it is 864 // already called in ProcessFile(). 865 operation_runner()->RemoveDirectory( 866 dest_root_, 867 base::Bind(&CopyOrMoveOperationDelegate::DidTryRemoveDestRoot, 868 weak_factory_.GetWeakPtr(), callback)); 869 return; 870 } 871 872 if (!progress_callback_.is_null()) { 873 progress_callback_.Run( 874 FileSystemOperation::BEGIN_COPY_ENTRY, src_url, FileSystemURL(), 0); 875 } 876 877 ProcessDirectoryInternal(src_url, CreateDestURL(src_url), callback); 878 } 879 880 void CopyOrMoveOperationDelegate::PostProcessDirectory( 881 const FileSystemURL& src_url, 882 const StatusCallback& callback) { 883 if (option_ == FileSystemOperation::OPTION_NONE) { 884 PostProcessDirectoryAfterTouchFile( 885 src_url, callback, base::File::FILE_OK); 886 return; 887 } 888 889 operation_runner()->GetMetadata( 890 src_url, 891 base::Bind( 892 &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterGetMetadata, 893 weak_factory_.GetWeakPtr(), src_url, callback)); 894 } 895 896 void CopyOrMoveOperationDelegate::OnCancel() { 897 // Request to cancel all running Copy/Move file. 898 for (std::set<CopyOrMoveImpl*>::iterator iter = running_copy_set_.begin(); 899 iter != running_copy_set_.end(); ++iter) 900 (*iter)->Cancel(); 901 } 902 903 void CopyOrMoveOperationDelegate::DidCopyOrMoveFile( 904 const FileSystemURL& src_url, 905 const FileSystemURL& dest_url, 906 const StatusCallback& callback, 907 CopyOrMoveImpl* impl, 908 base::File::Error error) { 909 running_copy_set_.erase(impl); 910 delete impl; 911 912 if (!progress_callback_.is_null() && error == base::File::FILE_OK) { 913 progress_callback_.Run( 914 FileSystemOperation::END_COPY_ENTRY, src_url, dest_url, 0); 915 } 916 917 callback.Run(error); 918 } 919 920 void CopyOrMoveOperationDelegate::DidTryRemoveDestRoot( 921 const StatusCallback& callback, 922 base::File::Error error) { 923 if (error == base::File::FILE_ERROR_NOT_A_DIRECTORY) { 924 callback_.Run(base::File::FILE_ERROR_INVALID_OPERATION); 925 return; 926 } 927 if (error != base::File::FILE_OK && 928 error != base::File::FILE_ERROR_NOT_FOUND) { 929 callback_.Run(error); 930 return; 931 } 932 933 ProcessDirectoryInternal(src_root_, dest_root_, callback); 934 } 935 936 void CopyOrMoveOperationDelegate::ProcessDirectoryInternal( 937 const FileSystemURL& src_url, 938 const FileSystemURL& dest_url, 939 const StatusCallback& callback) { 940 // If operation_type == Move we may need to record directories and 941 // restore directory timestamps in the end, though it may have 942 // negative performance impact. 943 // See http://crbug.com/171284 for more details. 944 operation_runner()->CreateDirectory( 945 dest_url, false /* exclusive */, false /* recursive */, 946 base::Bind(&CopyOrMoveOperationDelegate::DidCreateDirectory, 947 weak_factory_.GetWeakPtr(), src_url, dest_url, callback)); 948 } 949 950 void CopyOrMoveOperationDelegate::DidCreateDirectory( 951 const FileSystemURL& src_url, 952 const FileSystemURL& dest_url, 953 const StatusCallback& callback, 954 base::File::Error error) { 955 if (!progress_callback_.is_null() && error == base::File::FILE_OK) { 956 progress_callback_.Run( 957 FileSystemOperation::END_COPY_ENTRY, src_url, dest_url, 0); 958 } 959 960 callback.Run(error); 961 } 962 963 void CopyOrMoveOperationDelegate::PostProcessDirectoryAfterGetMetadata( 964 const FileSystemURL& src_url, 965 const StatusCallback& callback, 966 base::File::Error error, 967 const base::File::Info& file_info) { 968 if (error != base::File::FILE_OK) { 969 // Ignore the error, and run post process which should run after TouchFile. 970 PostProcessDirectoryAfterTouchFile( 971 src_url, callback, base::File::FILE_OK); 972 return; 973 } 974 975 operation_runner()->TouchFile( 976 CreateDestURL(src_url), base::Time::Now() /* last access */, 977 file_info.last_modified, 978 base::Bind( 979 &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterTouchFile, 980 weak_factory_.GetWeakPtr(), src_url, callback)); 981 } 982 983 void CopyOrMoveOperationDelegate::PostProcessDirectoryAfterTouchFile( 984 const FileSystemURL& src_url, 985 const StatusCallback& callback, 986 base::File::Error error) { 987 // Even if the TouchFile is failed, just ignore it. 988 989 if (operation_type_ == OPERATION_COPY) { 990 callback.Run(base::File::FILE_OK); 991 return; 992 } 993 994 DCHECK_EQ(OPERATION_MOVE, operation_type_); 995 996 // All files and subdirectories in the directory should be moved here, 997 // so remove the source directory for finalizing move operation. 998 operation_runner()->Remove( 999 src_url, false /* recursive */, 1000 base::Bind(&CopyOrMoveOperationDelegate::DidRemoveSourceForMove, 1001 weak_factory_.GetWeakPtr(), callback)); 1002 } 1003 1004 void CopyOrMoveOperationDelegate::DidRemoveSourceForMove( 1005 const StatusCallback& callback, 1006 base::File::Error error) { 1007 if (error == base::File::FILE_ERROR_NOT_FOUND) 1008 error = base::File::FILE_OK; 1009 callback.Run(error); 1010 } 1011 1012 void CopyOrMoveOperationDelegate::OnCopyFileProgress( 1013 const FileSystemURL& src_url, int64 size) { 1014 if (!progress_callback_.is_null()) { 1015 progress_callback_.Run( 1016 FileSystemOperation::PROGRESS, src_url, FileSystemURL(), size); 1017 } 1018 } 1019 1020 FileSystemURL CopyOrMoveOperationDelegate::CreateDestURL( 1021 const FileSystemURL& src_url) const { 1022 DCHECK_EQ(src_root_.type(), src_url.type()); 1023 DCHECK_EQ(src_root_.origin(), src_url.origin()); 1024 1025 base::FilePath relative = dest_root_.virtual_path(); 1026 src_root_.virtual_path().AppendRelativePath(src_url.virtual_path(), 1027 &relative); 1028 return file_system_context()->CreateCrackedFileSystemURL( 1029 dest_root_.origin(), 1030 dest_root_.mount_type(), 1031 relative); 1032 } 1033 1034 } // namespace storage 1035