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