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 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.
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 virtual void Cancel() OVERRIDE
{
126 cancel_requested_
= true;
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
;
139 if (error
!= base::File::FILE_OK
) {
144 // For now we assume CreateSnapshotFile always return a valid local file
146 DCHECK(!platform_path
.empty());
148 if (!validator_factory_
) {
149 // No validation is needed.
150 RunAfterPreWriteValidation(platform_path
, file_info
, file_ref
, callback
,
151 base::File::FILE_OK
);
155 // Run pre write validation.
158 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterPreWriteValidation
,
159 weak_factory_
.GetWeakPtr(),
160 platform_path
, file_info
, file_ref
, callback
));
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
;
172 if (error
!= base::File::FILE_OK
) {
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
));
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
;
193 if (error
!= base::File::FILE_OK
) {
198 file_progress_callback_
.Run(file_info
.size
);
200 if (option_
== FileSystemOperation::OPTION_NONE
) {
201 RunAfterTouchFile(callback
, base::File::FILE_OK
);
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
));
212 void RunAfterTouchFile(
213 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
214 base::File::Error error
) {
215 // Even if TouchFile is failed, just ignore it.
217 if (cancel_requested_
) {
218 callback
.Run(base::File::FILE_ERROR_ABORT
);
222 // |validator_| is NULL when the destination filesystem does not do
225 // No validation is needed.
226 RunAfterPostWriteValidation(callback
, base::File::FILE_OK
);
231 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterPostWriteValidation
,
232 weak_factory_
.GetWeakPtr(), callback
));
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
);
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
));
252 if (operation_type_
== CopyOrMoveOperationDelegate::OPERATION_COPY
) {
253 callback
.Run(base::File::FILE_OK
);
257 DCHECK_EQ(CopyOrMoveOperationDelegate::OPERATION_MOVE
, operation_type_
);
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
));
266 void RunAfterRemoveSourceForMove(
267 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
268 base::File::Error error
) {
269 if (cancel_requested_
)
270 error
= base::File::FILE_ERROR_ABORT
;
272 if (error
== base::File::FILE_ERROR_NOT_FOUND
)
273 error
= base::File::FILE_OK
;
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: "
285 callback
.Run(prior_error
);
288 // Runs pre-write validation.
289 void PreWriteValidation(
290 const base::FilePath
& platform_path
,
291 const CopyOrMoveOperationDelegate::StatusCallback
& callback
) {
292 DCHECK(validator_factory_
);
294 validator_factory_
->CreateCopyOrMoveFileValidator(
295 src_url_
, platform_path
));
296 validator_
->StartPreWriteValidation(callback
);
299 // Runs post-write validation.
300 void PostWriteValidation(
301 const CopyOrMoveOperationDelegate::StatusCallback
& callback
) {
302 operation_runner_
->CreateSnapshotFile(
305 &SnapshotCopyOrMoveImpl::PostWriteValidationAfterCreateSnapshotFile
,
306 weak_factory_
.GetWeakPtr(), callback
));
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
;
318 if (error
!= base::File::FILE_OK
) {
324 // Note: file_ref passed here to keep the file alive until after
325 // the StartPostWriteValidation operation finishes.
326 validator_
->StartPostWriteValidation(
328 base::Bind(&SnapshotCopyOrMoveImpl::DidPostWriteValidation
,
329 weak_factory_
.GetWeakPtr(), file_ref
, callback
));
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
) {
341 FileSystemOperationRunner
* operation_runner_
;
342 CopyOrMoveOperationDelegate::OperationType operation_type_
;
343 FileSystemURL src_url_
;
344 FileSystemURL dest_url_
;
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
);
355 // The size of buffer for StreamCopyHelper.
356 const int kReadBufferSize
= 32768;
358 // To avoid too many progress callbacks, it should be called less
359 // frequently than 50ms.
360 const int kMinProgressCallbackInvocationSpanInMilliseconds
= 50;
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
{
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
),
385 reader_(reader
.Pass()),
386 writer_(writer
.Pass()),
387 file_progress_callback_(file_progress_callback
),
388 cancel_requested_(false),
389 weak_factory_(this) {}
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(
398 base::Bind(&StreamCopyOrMoveImpl::RunAfterGetMetadataForSource
,
399 weak_factory_
.GetWeakPtr(), callback
));
402 virtual void Cancel() OVERRIDE
{
403 cancel_requested_
= true;
405 copy_helper_
->Cancel();
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
));
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
));
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
));
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
;
437 if (error
!= base::File::FILE_OK
) {
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
);
448 // To use FileStreamWriter, we need to ensure the destination file exists.
449 operation_runner_
->CreateFile(
451 true /* exclusive */,
452 base::Bind(&StreamCopyOrMoveImpl::RunAfterCreateFileForDestination
,
453 weak_factory_
.GetWeakPtr(),
455 file_info
.last_modified
));
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
;
469 if (error
!= base::File::FILE_OK
&&
470 error
!= base::File::FILE_ERROR_EXISTS
) {
475 if (error
== base::File::FILE_ERROR_EXISTS
) {
476 operation_runner_
->Truncate(
479 base::Bind(&StreamCopyOrMoveImpl::RunAfterTruncateForDestination
,
480 weak_factory_
.GetWeakPtr(),
485 RunAfterTruncateForDestination(
486 callback
, last_modified
, base::File::FILE_OK
);
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
;
496 if (error
!= base::File::FILE_OK
) {
501 const bool need_flush
= dest_url_
.mount_option().copy_sync_option() ==
502 storage::COPY_SYNC_OPTION_SYNC
;
504 NotifyOnStartUpdate(dest_url_
);
505 DCHECK(!copy_helper_
);
507 new CopyOrMoveOperationDelegate::StreamCopyHelper(
508 reader_
.Pass(), writer_
.Pass(),
511 file_progress_callback_
,
512 base::TimeDelta::FromMilliseconds(
513 kMinProgressCallbackInvocationSpanInMilliseconds
)));
515 base::Bind(&StreamCopyOrMoveImpl::RunAfterStreamCopy
,
516 weak_factory_
.GetWeakPtr(), callback
, last_modified
));
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
;
528 if (error
!= base::File::FILE_OK
) {
533 if (option_
== FileSystemOperation::OPTION_NONE
) {
534 RunAfterTouchFile(callback
, base::File::FILE_OK
);
538 operation_runner_
->TouchFile(
539 dest_url_
, base::Time::Now() /* last_access */, last_modified
,
540 base::Bind(&StreamCopyOrMoveImpl::RunAfterTouchFile
,
541 weak_factory_
.GetWeakPtr(), callback
));
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
);
553 if (operation_type_
== CopyOrMoveOperationDelegate::OPERATION_COPY
) {
554 callback
.Run(base::File::FILE_OK
);
558 DCHECK_EQ(CopyOrMoveOperationDelegate::OPERATION_MOVE
, operation_type_
);
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
));
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
;
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
);
594 CopyOrMoveOperationDelegate::StreamCopyHelper::StreamCopyHelper(
595 scoped_ptr
<storage::FileStreamReader
> reader
,
596 scoped_ptr
<FileStreamWriter
> writer
,
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) {
614 CopyOrMoveOperationDelegate::StreamCopyHelper::~StreamCopyHelper() {
617 void CopyOrMoveOperationDelegate::StreamCopyHelper::Run(
618 const StatusCallback
& callback
) {
619 file_progress_callback_
.Run(0);
620 last_progress_callback_invocation_time_
= base::Time::Now();
624 void CopyOrMoveOperationDelegate::StreamCopyHelper::Cancel() {
625 cancel_requested_
= true;
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
);
638 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidRead(
639 const StatusCallback
& callback
, int result
) {
640 if (cancel_requested_
) {
641 callback
.Run(base::File::FILE_ERROR_ABORT
);
646 callback
.Run(NetErrorToFileError(result
));
653 Flush(callback
, true /* is_eof */);
655 callback
.Run(base::File::FILE_OK
);
659 Write(callback
, new net::DrainableIOBuffer(io_buffer_
.get(), result
));
662 void CopyOrMoveOperationDelegate::StreamCopyHelper::Write(
663 const StatusCallback
& callback
,
664 scoped_refptr
<net::DrainableIOBuffer
> buffer
) {
665 DCHECK_GT(buffer
->BytesRemaining(), 0);
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
);
675 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidWrite(
676 const StatusCallback
& callback
,
677 scoped_refptr
<net::DrainableIOBuffer
> buffer
,
679 if (cancel_requested_
) {
680 callback
.Run(base::File::FILE_ERROR_ABORT
);
685 callback
.Run(NetErrorToFileError(result
));
689 buffer
->DidConsume(result
);
690 num_copied_bytes_
+= result
;
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
;
700 if (buffer
->BytesRemaining() > 0) {
701 Write(callback
, buffer
);
706 (num_copied_bytes_
- previous_flush_offset_
) > kFlushIntervalInBytes
) {
707 Flush(callback
, false /* not is_eof */);
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
);
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
);
729 previous_flush_offset_
= num_copied_bytes_
;
731 callback
.Run(NetErrorToFileError(result
));
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
),
746 dest_root_(dest_root
),
747 operation_type_(operation_type
),
749 progress_callback_(progress_callback
),
751 weak_factory_(this) {
752 same_file_system_
= src_root_
.IsInSameFileSystem(dest_root_
);
755 CopyOrMoveOperationDelegate::~CopyOrMoveOperationDelegate() {
756 STLDeleteElements(&running_copy_set_
);
759 void CopyOrMoveOperationDelegate::Run() {
760 // Not supported; this should never be called.
764 void CopyOrMoveOperationDelegate::RunRecursively() {
765 // Perform light-weight checks first.
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
);
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
);
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_
);
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);
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
));
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
) {
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(
827 file_system_context(),
834 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress
,
835 weak_factory_
.GetWeakPtr(),
841 impl
= new SnapshotCopyOrMoveImpl(
842 operation_runner(), operation_type_
, src_url
, dest_url
, option_
,
844 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress
,
845 weak_factory_
.GetWeakPtr(), src_url
));
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
));
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
863 // We do not invoke |progress_callback_| for source root, because it is
864 // already called in ProcessFile().
865 operation_runner()->RemoveDirectory(
867 base::Bind(&CopyOrMoveOperationDelegate::DidTryRemoveDestRoot
,
868 weak_factory_
.GetWeakPtr(), callback
));
872 if (!progress_callback_
.is_null()) {
873 progress_callback_
.Run(
874 FileSystemOperation::BEGIN_COPY_ENTRY
, src_url
, FileSystemURL(), 0);
877 ProcessDirectoryInternal(src_url
, CreateDestURL(src_url
), callback
);
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
);
889 operation_runner()->GetMetadata(
892 &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterGetMetadata
,
893 weak_factory_
.GetWeakPtr(), src_url
, callback
));
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
)
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
);
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);
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
);
927 if (error
!= base::File::FILE_OK
&&
928 error
!= base::File::FILE_ERROR_NOT_FOUND
) {
929 callback_
.Run(error
);
933 ProcessDirectoryInternal(src_root_
, dest_root_
, callback
);
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
));
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);
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
);
975 operation_runner()->TouchFile(
976 CreateDestURL(src_url
), base::Time::Now() /* last access */,
977 file_info
.last_modified
,
979 &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterTouchFile
,
980 weak_factory_
.GetWeakPtr(), src_url
, callback
));
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.
989 if (operation_type_
== OPERATION_COPY
) {
990 callback
.Run(base::File::FILE_OK
);
994 DCHECK_EQ(OPERATION_MOVE
, operation_type_
);
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
));
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
);
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
);
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());
1025 base::FilePath relative
= dest_root_
.virtual_path();
1026 src_root_
.virtual_path().AppendRelativePath(src_url
.virtual_path(),
1028 return file_system_context()->CreateCrackedFileSystemURL(
1029 dest_root_
.origin(),
1030 dest_root_
.mount_type(),
1034 } // namespace storage