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.
5 #include "storage/browser/fileapi/copy_or_move_operation_delegate.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"
24 const int64 kFlushIntervalInBytes = 10 << 20; // 10MB.
26 class CopyOrMoveOperationDelegate::CopyOrMoveImpl {
28 virtual ~CopyOrMoveImpl() {}
30 const CopyOrMoveOperationDelegate::StatusCallback& callback) = 0;
31 virtual void Cancel() = 0;
37 DISALLOW_COPY_AND_ASSIGN(CopyOrMoveImpl);
42 // Copies a file on a (same) file system. Just delegate the operation to
43 // |operation_runner|.
44 class CopyOrMoveOnSameFileSystemImpl
45 : public CopyOrMoveOperationDelegate::CopyOrMoveImpl {
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),
60 file_progress_callback_(file_progress_callback) {
64 const CopyOrMoveOperationDelegate::StatusCallback& callback) override {
65 if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_MOVE) {
66 operation_runner_->MoveFileLocal(src_url_, dest_url_, option_, callback);
68 operation_runner_->CopyFileLocal(
69 src_url_, dest_url_, option_, file_progress_callback_, callback);
73 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.
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);
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
93 class SnapshotCopyOrMoveImpl
94 : public CopyOrMoveOperationDelegate::CopyOrMoveImpl {
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),
110 validator_factory_(validator_factory),
111 file_progress_callback_(file_progress_callback),
112 cancel_requested_(false),
113 weak_factory_(this) {
117 const CopyOrMoveOperationDelegate::StatusCallback& callback) override {
118 file_progress_callback_.Run(0);
119 operation_runner_->CreateSnapshotFile(
121 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterCreateSnapshot,
122 weak_factory_.GetWeakPtr(), callback));
125 void Cancel() override { cancel_requested_ = true; }
128 void RunAfterCreateSnapshot(
129 const CopyOrMoveOperationDelegate::StatusCallback& callback,
130 base::File::Error error,
131 const base::File::Info& file_info,
132 const base::FilePath& platform_path,
133 const scoped_refptr<storage::ShareableFileReference>& file_ref) {
134 if (cancel_requested_)
135 error = base::File::FILE_ERROR_ABORT;
137 if (error != base::File::FILE_OK) {
142 // For now we assume CreateSnapshotFile always return a valid local file
144 DCHECK(!platform_path.empty());
146 if (!validator_factory_) {
147 // No validation is needed.
148 RunAfterPreWriteValidation(platform_path, file_info, file_ref, callback,
149 base::File::FILE_OK);
153 // Run pre write validation.
156 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterPreWriteValidation,
157 weak_factory_.GetWeakPtr(),
158 platform_path, file_info, file_ref, callback));
161 void RunAfterPreWriteValidation(
162 const base::FilePath& platform_path,
163 const base::File::Info& file_info,
164 const scoped_refptr<storage::ShareableFileReference>& file_ref,
165 const CopyOrMoveOperationDelegate::StatusCallback& callback,
166 base::File::Error error) {
167 if (cancel_requested_)
168 error = base::File::FILE_ERROR_ABORT;
170 if (error != base::File::FILE_OK) {
175 // |file_ref| is unused but necessary to keep the file alive until
176 // CopyInForeignFile() is completed.
177 operation_runner_->CopyInForeignFile(
178 platform_path, dest_url_,
179 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterCopyInForeignFile,
180 weak_factory_.GetWeakPtr(), file_info, file_ref, callback));
183 void RunAfterCopyInForeignFile(
184 const base::File::Info& file_info,
185 const scoped_refptr<storage::ShareableFileReference>& file_ref,
186 const CopyOrMoveOperationDelegate::StatusCallback& callback,
187 base::File::Error error) {
188 if (cancel_requested_)
189 error = base::File::FILE_ERROR_ABORT;
191 if (error != base::File::FILE_OK) {
196 file_progress_callback_.Run(file_info.size);
198 if (option_ == FileSystemOperation::OPTION_NONE) {
199 RunAfterTouchFile(callback, base::File::FILE_OK);
203 operation_runner_->TouchFile(
204 dest_url_, base::Time::Now() /* last_access */,
205 file_info.last_modified,
206 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterTouchFile,
207 weak_factory_.GetWeakPtr(), callback));
210 void RunAfterTouchFile(
211 const CopyOrMoveOperationDelegate::StatusCallback& callback,
212 base::File::Error error) {
213 // Even if TouchFile is failed, just ignore it.
215 if (cancel_requested_) {
216 callback.Run(base::File::FILE_ERROR_ABORT);
220 // |validator_| is NULL when the destination filesystem does not do
223 // No validation is needed.
224 RunAfterPostWriteValidation(callback, base::File::FILE_OK);
229 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterPostWriteValidation,
230 weak_factory_.GetWeakPtr(), callback));
233 void RunAfterPostWriteValidation(
234 const CopyOrMoveOperationDelegate::StatusCallback& callback,
235 base::File::Error error) {
236 if (cancel_requested_) {
237 callback.Run(base::File::FILE_ERROR_ABORT);
241 if (error != base::File::FILE_OK) {
242 // Failed to validate. Remove the destination file.
243 operation_runner_->Remove(
244 dest_url_, true /* recursive */,
245 base::Bind(&SnapshotCopyOrMoveImpl::DidRemoveDestForError,
246 weak_factory_.GetWeakPtr(), error, callback));
250 if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_COPY) {
251 callback.Run(base::File::FILE_OK);
255 DCHECK_EQ(CopyOrMoveOperationDelegate::OPERATION_MOVE, operation_type_);
257 // Remove the source for finalizing move operation.
258 operation_runner_->Remove(
259 src_url_, true /* recursive */,
260 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterRemoveSourceForMove,
261 weak_factory_.GetWeakPtr(), callback));
264 void RunAfterRemoveSourceForMove(
265 const CopyOrMoveOperationDelegate::StatusCallback& callback,
266 base::File::Error error) {
267 if (cancel_requested_)
268 error = base::File::FILE_ERROR_ABORT;
270 if (error == base::File::FILE_ERROR_NOT_FOUND)
271 error = base::File::FILE_OK;
275 void DidRemoveDestForError(
276 base::File::Error prior_error,
277 const CopyOrMoveOperationDelegate::StatusCallback& callback,
278 base::File::Error error) {
279 if (error != base::File::FILE_OK) {
280 VLOG(1) << "Error removing destination file after validation error: "
283 callback.Run(prior_error);
286 // Runs pre-write validation.
287 void PreWriteValidation(
288 const base::FilePath& platform_path,
289 const CopyOrMoveOperationDelegate::StatusCallback& callback) {
290 DCHECK(validator_factory_);
292 validator_factory_->CreateCopyOrMoveFileValidator(
293 src_url_, platform_path));
294 validator_->StartPreWriteValidation(callback);
297 // Runs post-write validation.
298 void PostWriteValidation(
299 const CopyOrMoveOperationDelegate::StatusCallback& callback) {
300 operation_runner_->CreateSnapshotFile(
303 &SnapshotCopyOrMoveImpl::PostWriteValidationAfterCreateSnapshotFile,
304 weak_factory_.GetWeakPtr(), callback));
307 void PostWriteValidationAfterCreateSnapshotFile(
308 const CopyOrMoveOperationDelegate::StatusCallback& callback,
309 base::File::Error error,
310 const base::File::Info& file_info,
311 const base::FilePath& platform_path,
312 const scoped_refptr<storage::ShareableFileReference>& file_ref) {
313 if (cancel_requested_)
314 error = base::File::FILE_ERROR_ABORT;
316 if (error != base::File::FILE_OK) {
322 // Note: file_ref passed here to keep the file alive until after
323 // the StartPostWriteValidation operation finishes.
324 validator_->StartPostWriteValidation(
326 base::Bind(&SnapshotCopyOrMoveImpl::DidPostWriteValidation,
327 weak_factory_.GetWeakPtr(), file_ref, callback));
330 // |file_ref| is unused; it is passed here to make sure the reference is
331 // alive until after post-write validation is complete.
332 void DidPostWriteValidation(
333 const scoped_refptr<storage::ShareableFileReference>& file_ref,
334 const CopyOrMoveOperationDelegate::StatusCallback& callback,
335 base::File::Error error) {
339 FileSystemOperationRunner* operation_runner_;
340 CopyOrMoveOperationDelegate::OperationType operation_type_;
341 FileSystemURL src_url_;
342 FileSystemURL dest_url_;
344 CopyOrMoveOperationDelegate::CopyOrMoveOption option_;
345 CopyOrMoveFileValidatorFactory* validator_factory_;
346 scoped_ptr<CopyOrMoveFileValidator> validator_;
347 FileSystemOperation::CopyFileProgressCallback file_progress_callback_;
348 bool cancel_requested_;
349 base::WeakPtrFactory<SnapshotCopyOrMoveImpl> weak_factory_;
350 DISALLOW_COPY_AND_ASSIGN(SnapshotCopyOrMoveImpl);
353 // The size of buffer for StreamCopyHelper.
354 const int kReadBufferSize = 32768;
356 // To avoid too many progress callbacks, it should be called less
357 // frequently than 50ms.
358 const int kMinProgressCallbackInvocationSpanInMilliseconds = 50;
360 // Specifically for cross file system copy/move operation, this class uses
361 // stream reader and writer for copying. Validator is not supported, so if
362 // necessary SnapshotCopyOrMoveImpl should be used.
363 class StreamCopyOrMoveImpl
364 : public CopyOrMoveOperationDelegate::CopyOrMoveImpl {
366 StreamCopyOrMoveImpl(
367 FileSystemOperationRunner* operation_runner,
368 FileSystemContext* file_system_context,
369 CopyOrMoveOperationDelegate::OperationType operation_type,
370 const FileSystemURL& src_url,
371 const FileSystemURL& dest_url,
372 CopyOrMoveOperationDelegate::CopyOrMoveOption option,
373 scoped_ptr<storage::FileStreamReader> reader,
374 scoped_ptr<FileStreamWriter> writer,
375 const FileSystemOperation::CopyFileProgressCallback&
376 file_progress_callback)
377 : operation_runner_(operation_runner),
378 file_system_context_(file_system_context),
379 operation_type_(operation_type),
383 reader_(reader.Pass()),
384 writer_(writer.Pass()),
385 file_progress_callback_(file_progress_callback),
386 cancel_requested_(false),
387 weak_factory_(this) {}
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(
396 base::Bind(&StreamCopyOrMoveImpl::RunAfterGetMetadataForSource,
397 weak_factory_.GetWeakPtr(), callback));
400 void Cancel() override {
401 cancel_requested_ = true;
403 copy_helper_->Cancel();
407 void NotifyOnStartUpdate(const FileSystemURL& url) {
408 if (file_system_context_->GetUpdateObservers(url.type())) {
409 file_system_context_->GetUpdateObservers(url.type())
410 ->Notify(&FileUpdateObserver::OnStartUpdate, MakeTuple(url));
414 void NotifyOnModifyFile(const FileSystemURL& url) {
415 if (file_system_context_->GetChangeObservers(url.type())) {
416 file_system_context_->GetChangeObservers(url.type())
417 ->Notify(&FileChangeObserver::OnModifyFile, MakeTuple(url));
421 void NotifyOnEndUpdate(const FileSystemURL& url) {
422 if (file_system_context_->GetUpdateObservers(url.type())) {
423 file_system_context_->GetUpdateObservers(url.type())
424 ->Notify(&FileUpdateObserver::OnEndUpdate, MakeTuple(url));
428 void RunAfterGetMetadataForSource(
429 const CopyOrMoveOperationDelegate::StatusCallback& callback,
430 base::File::Error error,
431 const base::File::Info& file_info) {
432 if (cancel_requested_)
433 error = base::File::FILE_ERROR_ABORT;
435 if (error != base::File::FILE_OK) {
440 if (file_info.is_directory) {
441 // If not a directory, failed with appropriate error code.
442 callback.Run(base::File::FILE_ERROR_NOT_A_FILE);
446 // To use FileStreamWriter, we need to ensure the destination file exists.
447 operation_runner_->CreateFile(
449 true /* exclusive */,
450 base::Bind(&StreamCopyOrMoveImpl::RunAfterCreateFileForDestination,
451 weak_factory_.GetWeakPtr(),
453 file_info.last_modified));
456 void RunAfterCreateFileForDestination(
457 const CopyOrMoveOperationDelegate::StatusCallback& callback,
458 const base::Time& last_modified,
459 base::File::Error error) {
460 if (cancel_requested_)
461 error = base::File::FILE_ERROR_ABORT;
462 // This conversion is to return the consistent status code with
463 // FileSystemFileUtil::Copy.
464 if (error == base::File::FILE_ERROR_NOT_A_FILE)
465 error = base::File::FILE_ERROR_INVALID_OPERATION;
467 if (error != base::File::FILE_OK &&
468 error != base::File::FILE_ERROR_EXISTS) {
473 if (error == base::File::FILE_ERROR_EXISTS) {
474 operation_runner_->Truncate(
477 base::Bind(&StreamCopyOrMoveImpl::RunAfterTruncateForDestination,
478 weak_factory_.GetWeakPtr(),
483 RunAfterTruncateForDestination(
484 callback, last_modified, base::File::FILE_OK);
487 void RunAfterTruncateForDestination(
488 const CopyOrMoveOperationDelegate::StatusCallback& callback,
489 const base::Time& last_modified,
490 base::File::Error error) {
491 if (cancel_requested_)
492 error = base::File::FILE_ERROR_ABORT;
494 if (error != base::File::FILE_OK) {
499 const bool need_flush = dest_url_.mount_option().copy_sync_option() ==
500 storage::COPY_SYNC_OPTION_SYNC;
502 NotifyOnStartUpdate(dest_url_);
503 DCHECK(!copy_helper_);
505 new CopyOrMoveOperationDelegate::StreamCopyHelper(
506 reader_.Pass(), writer_.Pass(),
509 file_progress_callback_,
510 base::TimeDelta::FromMilliseconds(
511 kMinProgressCallbackInvocationSpanInMilliseconds)));
513 base::Bind(&StreamCopyOrMoveImpl::RunAfterStreamCopy,
514 weak_factory_.GetWeakPtr(), callback, last_modified));
517 void RunAfterStreamCopy(
518 const CopyOrMoveOperationDelegate::StatusCallback& callback,
519 const base::Time& last_modified,
520 base::File::Error error) {
521 NotifyOnModifyFile(dest_url_);
522 NotifyOnEndUpdate(dest_url_);
523 if (cancel_requested_)
524 error = base::File::FILE_ERROR_ABORT;
526 if (error != base::File::FILE_OK) {
531 if (option_ == FileSystemOperation::OPTION_NONE) {
532 RunAfterTouchFile(callback, base::File::FILE_OK);
536 operation_runner_->TouchFile(
537 dest_url_, base::Time::Now() /* last_access */, last_modified,
538 base::Bind(&StreamCopyOrMoveImpl::RunAfterTouchFile,
539 weak_factory_.GetWeakPtr(), callback));
542 void RunAfterTouchFile(
543 const CopyOrMoveOperationDelegate::StatusCallback& callback,
544 base::File::Error error) {
545 // Even if TouchFile is failed, just ignore it.
546 if (cancel_requested_) {
547 callback.Run(base::File::FILE_ERROR_ABORT);
551 if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_COPY) {
552 callback.Run(base::File::FILE_OK);
556 DCHECK_EQ(CopyOrMoveOperationDelegate::OPERATION_MOVE, operation_type_);
558 // Remove the source for finalizing move operation.
559 operation_runner_->Remove(
560 src_url_, false /* recursive */,
561 base::Bind(&StreamCopyOrMoveImpl::RunAfterRemoveForMove,
562 weak_factory_.GetWeakPtr(), callback));
565 void RunAfterRemoveForMove(
566 const CopyOrMoveOperationDelegate::StatusCallback& callback,
567 base::File::Error error) {
568 if (cancel_requested_)
569 error = base::File::FILE_ERROR_ABORT;
570 if (error == base::File::FILE_ERROR_NOT_FOUND)
571 error = base::File::FILE_OK;
575 FileSystemOperationRunner* operation_runner_;
576 scoped_refptr<FileSystemContext> file_system_context_;
577 CopyOrMoveOperationDelegate::OperationType operation_type_;
578 FileSystemURL src_url_;
579 FileSystemURL dest_url_;
580 CopyOrMoveOperationDelegate::CopyOrMoveOption option_;
581 scoped_ptr<storage::FileStreamReader> reader_;
582 scoped_ptr<FileStreamWriter> writer_;
583 FileSystemOperation::CopyFileProgressCallback file_progress_callback_;
584 scoped_ptr<CopyOrMoveOperationDelegate::StreamCopyHelper> copy_helper_;
585 bool cancel_requested_;
586 base::WeakPtrFactory<StreamCopyOrMoveImpl> weak_factory_;
587 DISALLOW_COPY_AND_ASSIGN(StreamCopyOrMoveImpl);
592 CopyOrMoveOperationDelegate::StreamCopyHelper::StreamCopyHelper(
593 scoped_ptr<storage::FileStreamReader> reader,
594 scoped_ptr<FileStreamWriter> writer,
597 const FileSystemOperation::CopyFileProgressCallback& file_progress_callback,
598 const base::TimeDelta& min_progress_callback_invocation_span)
599 : reader_(reader.Pass()),
600 writer_(writer.Pass()),
601 need_flush_(need_flush),
602 file_progress_callback_(file_progress_callback),
603 io_buffer_(new net::IOBufferWithSize(buffer_size)),
604 num_copied_bytes_(0),
605 previous_flush_offset_(0),
606 min_progress_callback_invocation_span_(
607 min_progress_callback_invocation_span),
608 cancel_requested_(false),
609 weak_factory_(this) {
612 CopyOrMoveOperationDelegate::StreamCopyHelper::~StreamCopyHelper() {
615 void CopyOrMoveOperationDelegate::StreamCopyHelper::Run(
616 const StatusCallback& callback) {
617 file_progress_callback_.Run(0);
618 last_progress_callback_invocation_time_ = base::Time::Now();
622 void CopyOrMoveOperationDelegate::StreamCopyHelper::Cancel() {
623 cancel_requested_ = true;
626 void CopyOrMoveOperationDelegate::StreamCopyHelper::Read(
627 const StatusCallback& callback) {
628 int result = reader_->Read(
629 io_buffer_.get(), io_buffer_->size(),
630 base::Bind(&StreamCopyHelper::DidRead,
631 weak_factory_.GetWeakPtr(), callback));
632 if (result != net::ERR_IO_PENDING)
633 DidRead(callback, result);
636 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidRead(
637 const StatusCallback& callback, int result) {
638 if (cancel_requested_) {
639 callback.Run(base::File::FILE_ERROR_ABORT);
644 callback.Run(NetErrorToFileError(result));
651 Flush(callback, true /* is_eof */);
653 callback.Run(base::File::FILE_OK);
657 Write(callback, new net::DrainableIOBuffer(io_buffer_.get(), result));
660 void CopyOrMoveOperationDelegate::StreamCopyHelper::Write(
661 const StatusCallback& callback,
662 scoped_refptr<net::DrainableIOBuffer> buffer) {
663 DCHECK_GT(buffer->BytesRemaining(), 0);
665 int result = writer_->Write(
666 buffer.get(), buffer->BytesRemaining(),
667 base::Bind(&StreamCopyHelper::DidWrite,
668 weak_factory_.GetWeakPtr(), callback, buffer));
669 if (result != net::ERR_IO_PENDING)
670 DidWrite(callback, buffer, result);
673 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidWrite(
674 const StatusCallback& callback,
675 scoped_refptr<net::DrainableIOBuffer> buffer,
677 if (cancel_requested_) {
678 callback.Run(base::File::FILE_ERROR_ABORT);
683 callback.Run(NetErrorToFileError(result));
687 buffer->DidConsume(result);
688 num_copied_bytes_ += result;
690 // Check the elapsed time since last |file_progress_callback_| invocation.
691 base::Time now = base::Time::Now();
692 if (now - last_progress_callback_invocation_time_ >=
693 min_progress_callback_invocation_span_) {
694 file_progress_callback_.Run(num_copied_bytes_);
695 last_progress_callback_invocation_time_ = now;
698 if (buffer->BytesRemaining() > 0) {
699 Write(callback, buffer);
704 (num_copied_bytes_ - previous_flush_offset_) > kFlushIntervalInBytes) {
705 Flush(callback, false /* not is_eof */);
711 void CopyOrMoveOperationDelegate::StreamCopyHelper::Flush(
712 const StatusCallback& callback, bool is_eof) {
713 int result = writer_->Flush(
714 base::Bind(&StreamCopyHelper::DidFlush,
715 weak_factory_.GetWeakPtr(), callback, is_eof));
716 if (result != net::ERR_IO_PENDING)
717 DidFlush(callback, is_eof, result);
720 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidFlush(
721 const StatusCallback& callback, bool is_eof, int result) {
722 if (cancel_requested_) {
723 callback.Run(base::File::FILE_ERROR_ABORT);
727 previous_flush_offset_ = num_copied_bytes_;
729 callback.Run(NetErrorToFileError(result));
734 CopyOrMoveOperationDelegate::CopyOrMoveOperationDelegate(
735 FileSystemContext* file_system_context,
736 const FileSystemURL& src_root,
737 const FileSystemURL& dest_root,
738 OperationType operation_type,
739 CopyOrMoveOption option,
740 const CopyProgressCallback& progress_callback,
741 const StatusCallback& callback)
742 : RecursiveOperationDelegate(file_system_context),
744 dest_root_(dest_root),
745 operation_type_(operation_type),
747 progress_callback_(progress_callback),
749 weak_factory_(this) {
750 same_file_system_ = src_root_.IsInSameFileSystem(dest_root_);
753 CopyOrMoveOperationDelegate::~CopyOrMoveOperationDelegate() {
754 STLDeleteElements(&running_copy_set_);
757 void CopyOrMoveOperationDelegate::Run() {
758 // Not supported; this should never be called.
762 void CopyOrMoveOperationDelegate::RunRecursively() {
763 // Perform light-weight checks first.
765 // It is an error to try to copy/move an entry into its child.
766 if (same_file_system_ && src_root_.path().IsParent(dest_root_.path())) {
767 callback_.Run(base::File::FILE_ERROR_INVALID_OPERATION);
771 if (same_file_system_ && src_root_.path() == dest_root_.path()) {
772 // In JS API this should return error, but we return success because Pepper
773 // wants to return success and we have a code path that returns error in
774 // Blink for JS (http://crbug.com/329517).
775 callback_.Run(base::File::FILE_OK);
779 // Start to process the source directory recursively.
780 // TODO(kinuko): This could be too expensive for same_file_system_==true
781 // and operation==MOVE case, probably we can just rename the root directory.
782 // http://crbug.com/172187
783 StartRecursiveOperation(src_root_, callback_);
786 void CopyOrMoveOperationDelegate::ProcessFile(
787 const FileSystemURL& src_url,
788 const StatusCallback& callback) {
789 if (!progress_callback_.is_null()) {
790 progress_callback_.Run(
791 FileSystemOperation::BEGIN_COPY_ENTRY, src_url, FileSystemURL(), 0);
794 FileSystemURL dest_url = CreateDestURL(src_url);
795 CopyOrMoveImpl* impl = NULL;
796 if (same_file_system_ &&
797 (file_system_context()
798 ->GetFileSystemBackend(src_url.type())
799 ->HasInplaceCopyImplementation(src_url.type()) ||
800 operation_type_ == OPERATION_MOVE)) {
801 impl = new CopyOrMoveOnSameFileSystemImpl(
802 operation_runner(), operation_type_, src_url, dest_url, option_,
803 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress,
804 weak_factory_.GetWeakPtr(), src_url));
806 // Cross filesystem case.
807 base::File::Error error = base::File::FILE_ERROR_FAILED;
808 CopyOrMoveFileValidatorFactory* validator_factory =
809 file_system_context()->GetCopyOrMoveFileValidatorFactory(
810 dest_root_.type(), &error);
811 if (error != base::File::FILE_OK) {
816 if (!validator_factory) {
817 scoped_ptr<storage::FileStreamReader> reader =
818 file_system_context()->CreateFileStreamReader(
819 src_url, 0 /* offset */, storage::kMaximumLength, base::Time());
820 scoped_ptr<FileStreamWriter> writer =
821 file_system_context()->CreateFileStreamWriter(dest_url, 0);
822 if (reader && writer) {
823 impl = new StreamCopyOrMoveImpl(
825 file_system_context(),
832 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress,
833 weak_factory_.GetWeakPtr(),
839 impl = new SnapshotCopyOrMoveImpl(
840 operation_runner(), operation_type_, src_url, dest_url, option_,
842 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress,
843 weak_factory_.GetWeakPtr(), src_url));
847 // Register the running task.
848 running_copy_set_.insert(impl);
849 impl->Run(base::Bind(
850 &CopyOrMoveOperationDelegate::DidCopyOrMoveFile,
851 weak_factory_.GetWeakPtr(), src_url, dest_url, callback, impl));
854 void CopyOrMoveOperationDelegate::ProcessDirectory(
855 const FileSystemURL& src_url,
856 const StatusCallback& callback) {
857 if (src_url == src_root_) {
858 // The src_root_ looks to be a directory.
859 // Try removing the dest_root_ to see if it exists and/or it is an
861 // We do not invoke |progress_callback_| for source root, because it is
862 // already called in ProcessFile().
863 operation_runner()->RemoveDirectory(
865 base::Bind(&CopyOrMoveOperationDelegate::DidTryRemoveDestRoot,
866 weak_factory_.GetWeakPtr(), callback));
870 if (!progress_callback_.is_null()) {
871 progress_callback_.Run(
872 FileSystemOperation::BEGIN_COPY_ENTRY, src_url, FileSystemURL(), 0);
875 ProcessDirectoryInternal(src_url, CreateDestURL(src_url), callback);
878 void CopyOrMoveOperationDelegate::PostProcessDirectory(
879 const FileSystemURL& src_url,
880 const StatusCallback& callback) {
881 if (option_ == FileSystemOperation::OPTION_NONE) {
882 PostProcessDirectoryAfterTouchFile(
883 src_url, callback, base::File::FILE_OK);
887 operation_runner()->GetMetadata(
890 &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterGetMetadata,
891 weak_factory_.GetWeakPtr(), src_url, callback));
894 void CopyOrMoveOperationDelegate::OnCancel() {
895 // Request to cancel all running Copy/Move file.
896 for (std::set<CopyOrMoveImpl*>::iterator iter = running_copy_set_.begin();
897 iter != running_copy_set_.end(); ++iter)
901 void CopyOrMoveOperationDelegate::DidCopyOrMoveFile(
902 const FileSystemURL& src_url,
903 const FileSystemURL& dest_url,
904 const StatusCallback& callback,
905 CopyOrMoveImpl* impl,
906 base::File::Error error) {
907 running_copy_set_.erase(impl);
910 if (!progress_callback_.is_null() && error == base::File::FILE_OK) {
911 progress_callback_.Run(
912 FileSystemOperation::END_COPY_ENTRY, src_url, dest_url, 0);
918 void CopyOrMoveOperationDelegate::DidTryRemoveDestRoot(
919 const StatusCallback& callback,
920 base::File::Error error) {
921 if (error == base::File::FILE_ERROR_NOT_A_DIRECTORY) {
922 callback_.Run(base::File::FILE_ERROR_INVALID_OPERATION);
925 if (error != base::File::FILE_OK &&
926 error != base::File::FILE_ERROR_NOT_FOUND) {
927 callback_.Run(error);
931 ProcessDirectoryInternal(src_root_, dest_root_, callback);
934 void CopyOrMoveOperationDelegate::ProcessDirectoryInternal(
935 const FileSystemURL& src_url,
936 const FileSystemURL& dest_url,
937 const StatusCallback& callback) {
938 // If operation_type == Move we may need to record directories and
939 // restore directory timestamps in the end, though it may have
940 // negative performance impact.
941 // See http://crbug.com/171284 for more details.
942 operation_runner()->CreateDirectory(
943 dest_url, false /* exclusive */, false /* recursive */,
944 base::Bind(&CopyOrMoveOperationDelegate::DidCreateDirectory,
945 weak_factory_.GetWeakPtr(), src_url, dest_url, callback));
948 void CopyOrMoveOperationDelegate::DidCreateDirectory(
949 const FileSystemURL& src_url,
950 const FileSystemURL& dest_url,
951 const StatusCallback& callback,
952 base::File::Error error) {
953 if (!progress_callback_.is_null() && error == base::File::FILE_OK) {
954 progress_callback_.Run(
955 FileSystemOperation::END_COPY_ENTRY, src_url, dest_url, 0);
961 void CopyOrMoveOperationDelegate::PostProcessDirectoryAfterGetMetadata(
962 const FileSystemURL& src_url,
963 const StatusCallback& callback,
964 base::File::Error error,
965 const base::File::Info& file_info) {
966 if (error != base::File::FILE_OK) {
967 // Ignore the error, and run post process which should run after TouchFile.
968 PostProcessDirectoryAfterTouchFile(
969 src_url, callback, base::File::FILE_OK);
973 operation_runner()->TouchFile(
974 CreateDestURL(src_url), base::Time::Now() /* last access */,
975 file_info.last_modified,
977 &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterTouchFile,
978 weak_factory_.GetWeakPtr(), src_url, callback));
981 void CopyOrMoveOperationDelegate::PostProcessDirectoryAfterTouchFile(
982 const FileSystemURL& src_url,
983 const StatusCallback& callback,
984 base::File::Error error) {
985 // Even if the TouchFile is failed, just ignore it.
987 if (operation_type_ == OPERATION_COPY) {
988 callback.Run(base::File::FILE_OK);
992 DCHECK_EQ(OPERATION_MOVE, operation_type_);
994 // All files and subdirectories in the directory should be moved here,
995 // so remove the source directory for finalizing move operation.
996 operation_runner()->Remove(
997 src_url, false /* recursive */,
998 base::Bind(&CopyOrMoveOperationDelegate::DidRemoveSourceForMove,
999 weak_factory_.GetWeakPtr(), callback));
1002 void CopyOrMoveOperationDelegate::DidRemoveSourceForMove(
1003 const StatusCallback& callback,
1004 base::File::Error error) {
1005 if (error == base::File::FILE_ERROR_NOT_FOUND)
1006 error = base::File::FILE_OK;
1007 callback.Run(error);
1010 void CopyOrMoveOperationDelegate::OnCopyFileProgress(
1011 const FileSystemURL& src_url, int64 size) {
1012 if (!progress_callback_.is_null()) {
1013 progress_callback_.Run(
1014 FileSystemOperation::PROGRESS, src_url, FileSystemURL(), size);
1018 FileSystemURL CopyOrMoveOperationDelegate::CreateDestURL(
1019 const FileSystemURL& src_url) const {
1020 DCHECK_EQ(src_root_.type(), src_url.type());
1021 DCHECK_EQ(src_root_.origin(), src_url.origin());
1023 base::FilePath relative = dest_root_.virtual_path();
1024 src_root_.virtual_path().AppendRelativePath(src_url.virtual_path(),
1026 return file_system_context()->CreateCrackedFileSystemURL(
1027 dest_root_.origin(),
1028 dest_root_.mount_type(),
1032 } // namespace storage