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/shareable_file_reference.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_reader.h"
15 #include "storage/browser/fileapi/file_stream_writer.h"
16 #include "storage/browser/fileapi/file_system_context.h"
17 #include "storage/browser/fileapi/file_system_operation_runner.h"
18 #include "storage/browser/fileapi/file_system_url.h"
19 #include "storage/browser/fileapi/recursive_operation_delegate.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 NotifyOnStartUpdate(dest_url_
);
500 DCHECK(!copy_helper_
);
501 copy_helper_
.reset(new CopyOrMoveOperationDelegate::StreamCopyHelper(
502 reader_
.Pass(), writer_
.Pass(), dest_url_
.mount_option().flush_policy(),
503 kReadBufferSize
, file_progress_callback_
,
504 base::TimeDelta::FromMilliseconds(
505 kMinProgressCallbackInvocationSpanInMilliseconds
)));
507 base::Bind(&StreamCopyOrMoveImpl::RunAfterStreamCopy
,
508 weak_factory_
.GetWeakPtr(), callback
, last_modified
));
511 void RunAfterStreamCopy(
512 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
513 const base::Time
& last_modified
,
514 base::File::Error error
) {
515 NotifyOnModifyFile(dest_url_
);
516 NotifyOnEndUpdate(dest_url_
);
517 if (cancel_requested_
)
518 error
= base::File::FILE_ERROR_ABORT
;
520 if (error
!= base::File::FILE_OK
) {
525 if (option_
== FileSystemOperation::OPTION_NONE
) {
526 RunAfterTouchFile(callback
, base::File::FILE_OK
);
530 operation_runner_
->TouchFile(
531 dest_url_
, base::Time::Now() /* last_access */, last_modified
,
532 base::Bind(&StreamCopyOrMoveImpl::RunAfterTouchFile
,
533 weak_factory_
.GetWeakPtr(), callback
));
536 void RunAfterTouchFile(
537 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
538 base::File::Error error
) {
539 // Even if TouchFile is failed, just ignore it.
540 if (cancel_requested_
) {
541 callback
.Run(base::File::FILE_ERROR_ABORT
);
545 if (operation_type_
== CopyOrMoveOperationDelegate::OPERATION_COPY
) {
546 callback
.Run(base::File::FILE_OK
);
550 DCHECK_EQ(CopyOrMoveOperationDelegate::OPERATION_MOVE
, operation_type_
);
552 // Remove the source for finalizing move operation.
553 operation_runner_
->Remove(
554 src_url_
, false /* recursive */,
555 base::Bind(&StreamCopyOrMoveImpl::RunAfterRemoveForMove
,
556 weak_factory_
.GetWeakPtr(), callback
));
559 void RunAfterRemoveForMove(
560 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
561 base::File::Error error
) {
562 if (cancel_requested_
)
563 error
= base::File::FILE_ERROR_ABORT
;
564 if (error
== base::File::FILE_ERROR_NOT_FOUND
)
565 error
= base::File::FILE_OK
;
569 FileSystemOperationRunner
* operation_runner_
;
570 scoped_refptr
<FileSystemContext
> file_system_context_
;
571 CopyOrMoveOperationDelegate::OperationType operation_type_
;
572 FileSystemURL src_url_
;
573 FileSystemURL dest_url_
;
574 CopyOrMoveOperationDelegate::CopyOrMoveOption option_
;
575 scoped_ptr
<storage::FileStreamReader
> reader_
;
576 scoped_ptr
<FileStreamWriter
> writer_
;
577 FileSystemOperation::CopyFileProgressCallback file_progress_callback_
;
578 scoped_ptr
<CopyOrMoveOperationDelegate::StreamCopyHelper
> copy_helper_
;
579 bool cancel_requested_
;
580 base::WeakPtrFactory
<StreamCopyOrMoveImpl
> weak_factory_
;
581 DISALLOW_COPY_AND_ASSIGN(StreamCopyOrMoveImpl
);
586 CopyOrMoveOperationDelegate::StreamCopyHelper::StreamCopyHelper(
587 scoped_ptr
<storage::FileStreamReader
> reader
,
588 scoped_ptr
<FileStreamWriter
> writer
,
589 storage::FlushPolicy flush_policy
,
591 const FileSystemOperation::CopyFileProgressCallback
& file_progress_callback
,
592 const base::TimeDelta
& min_progress_callback_invocation_span
)
593 : reader_(reader
.Pass()),
594 writer_(writer
.Pass()),
595 flush_policy_(flush_policy
),
596 file_progress_callback_(file_progress_callback
),
597 io_buffer_(new net::IOBufferWithSize(buffer_size
)),
598 num_copied_bytes_(0),
599 previous_flush_offset_(0),
600 min_progress_callback_invocation_span_(
601 min_progress_callback_invocation_span
),
602 cancel_requested_(false),
603 weak_factory_(this) {
606 CopyOrMoveOperationDelegate::StreamCopyHelper::~StreamCopyHelper() {
609 void CopyOrMoveOperationDelegate::StreamCopyHelper::Run(
610 const StatusCallback
& callback
) {
611 file_progress_callback_
.Run(0);
612 last_progress_callback_invocation_time_
= base::Time::Now();
616 void CopyOrMoveOperationDelegate::StreamCopyHelper::Cancel() {
617 cancel_requested_
= true;
620 void CopyOrMoveOperationDelegate::StreamCopyHelper::Read(
621 const StatusCallback
& callback
) {
622 int result
= reader_
->Read(
623 io_buffer_
.get(), io_buffer_
->size(),
624 base::Bind(&StreamCopyHelper::DidRead
,
625 weak_factory_
.GetWeakPtr(), callback
));
626 if (result
!= net::ERR_IO_PENDING
)
627 DidRead(callback
, result
);
630 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidRead(
631 const StatusCallback
& callback
, int result
) {
632 if (cancel_requested_
) {
633 callback
.Run(base::File::FILE_ERROR_ABORT
);
638 callback
.Run(NetErrorToFileError(result
));
644 if (flush_policy_
== storage::FlushPolicy::FLUSH_ON_COMPLETION
)
645 Flush(callback
, true /* is_eof */);
647 callback
.Run(base::File::FILE_OK
);
651 Write(callback
, new net::DrainableIOBuffer(io_buffer_
.get(), result
));
654 void CopyOrMoveOperationDelegate::StreamCopyHelper::Write(
655 const StatusCallback
& callback
,
656 scoped_refptr
<net::DrainableIOBuffer
> buffer
) {
657 DCHECK_GT(buffer
->BytesRemaining(), 0);
659 int result
= writer_
->Write(
660 buffer
.get(), buffer
->BytesRemaining(),
661 base::Bind(&StreamCopyHelper::DidWrite
,
662 weak_factory_
.GetWeakPtr(), callback
, buffer
));
663 if (result
!= net::ERR_IO_PENDING
)
664 DidWrite(callback
, buffer
, result
);
667 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidWrite(
668 const StatusCallback
& callback
,
669 scoped_refptr
<net::DrainableIOBuffer
> buffer
,
671 if (cancel_requested_
) {
672 callback
.Run(base::File::FILE_ERROR_ABORT
);
677 callback
.Run(NetErrorToFileError(result
));
681 buffer
->DidConsume(result
);
682 num_copied_bytes_
+= result
;
684 // Check the elapsed time since last |file_progress_callback_| invocation.
685 base::Time now
= base::Time::Now();
686 if (now
- last_progress_callback_invocation_time_
>=
687 min_progress_callback_invocation_span_
) {
688 file_progress_callback_
.Run(num_copied_bytes_
);
689 last_progress_callback_invocation_time_
= now
;
692 if (buffer
->BytesRemaining() > 0) {
693 Write(callback
, buffer
);
697 if (flush_policy_
== storage::FlushPolicy::FLUSH_ON_COMPLETION
&&
698 (num_copied_bytes_
- previous_flush_offset_
) > kFlushIntervalInBytes
) {
699 Flush(callback
, false /* not is_eof */);
705 void CopyOrMoveOperationDelegate::StreamCopyHelper::Flush(
706 const StatusCallback
& callback
, bool is_eof
) {
707 int result
= writer_
->Flush(
708 base::Bind(&StreamCopyHelper::DidFlush
,
709 weak_factory_
.GetWeakPtr(), callback
, is_eof
));
710 if (result
!= net::ERR_IO_PENDING
)
711 DidFlush(callback
, is_eof
, result
);
714 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidFlush(
715 const StatusCallback
& callback
, bool is_eof
, int result
) {
716 if (cancel_requested_
) {
717 callback
.Run(base::File::FILE_ERROR_ABORT
);
721 previous_flush_offset_
= num_copied_bytes_
;
723 callback
.Run(NetErrorToFileError(result
));
728 CopyOrMoveOperationDelegate::CopyOrMoveOperationDelegate(
729 FileSystemContext
* file_system_context
,
730 const FileSystemURL
& src_root
,
731 const FileSystemURL
& dest_root
,
732 OperationType operation_type
,
733 CopyOrMoveOption option
,
734 const CopyProgressCallback
& progress_callback
,
735 const StatusCallback
& callback
)
736 : RecursiveOperationDelegate(file_system_context
),
738 dest_root_(dest_root
),
739 operation_type_(operation_type
),
741 progress_callback_(progress_callback
),
743 weak_factory_(this) {
744 same_file_system_
= src_root_
.IsInSameFileSystem(dest_root_
);
747 CopyOrMoveOperationDelegate::~CopyOrMoveOperationDelegate() {
748 STLDeleteElements(&running_copy_set_
);
751 void CopyOrMoveOperationDelegate::Run() {
752 // Not supported; this should never be called.
756 void CopyOrMoveOperationDelegate::RunRecursively() {
757 // Perform light-weight checks first.
759 // It is an error to try to copy/move an entry into its child.
760 if (same_file_system_
&& src_root_
.path().IsParent(dest_root_
.path())) {
761 callback_
.Run(base::File::FILE_ERROR_INVALID_OPERATION
);
765 if (same_file_system_
&& src_root_
.path() == dest_root_
.path()) {
766 // In JS API this should return error, but we return success because Pepper
767 // wants to return success and we have a code path that returns error in
768 // Blink for JS (http://crbug.com/329517).
769 callback_
.Run(base::File::FILE_OK
);
773 // Start to process the source directory recursively.
774 // TODO(kinuko): This could be too expensive for same_file_system_==true
775 // and operation==MOVE case, probably we can just rename the root directory.
776 // http://crbug.com/172187
777 StartRecursiveOperation(src_root_
, callback_
);
780 void CopyOrMoveOperationDelegate::ProcessFile(
781 const FileSystemURL
& src_url
,
782 const StatusCallback
& callback
) {
783 if (!progress_callback_
.is_null()) {
784 progress_callback_
.Run(
785 FileSystemOperation::BEGIN_COPY_ENTRY
, src_url
, FileSystemURL(), 0);
788 FileSystemURL dest_url
= CreateDestURL(src_url
);
789 CopyOrMoveImpl
* impl
= NULL
;
790 if (same_file_system_
&&
791 (file_system_context()
792 ->GetFileSystemBackend(src_url
.type())
793 ->HasInplaceCopyImplementation(src_url
.type()) ||
794 operation_type_
== OPERATION_MOVE
)) {
795 impl
= new CopyOrMoveOnSameFileSystemImpl(
796 operation_runner(), operation_type_
, src_url
, dest_url
, option_
,
797 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress
,
798 weak_factory_
.GetWeakPtr(), src_url
));
800 // Cross filesystem case.
801 base::File::Error error
= base::File::FILE_ERROR_FAILED
;
802 CopyOrMoveFileValidatorFactory
* validator_factory
=
803 file_system_context()->GetCopyOrMoveFileValidatorFactory(
804 dest_root_
.type(), &error
);
805 if (error
!= base::File::FILE_OK
) {
810 if (!validator_factory
) {
811 scoped_ptr
<storage::FileStreamReader
> reader
=
812 file_system_context()->CreateFileStreamReader(
813 src_url
, 0 /* offset */, storage::kMaximumLength
, base::Time());
814 scoped_ptr
<FileStreamWriter
> writer
=
815 file_system_context()->CreateFileStreamWriter(dest_url
, 0);
816 if (reader
&& writer
) {
817 impl
= new StreamCopyOrMoveImpl(
819 file_system_context(),
826 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress
,
827 weak_factory_
.GetWeakPtr(),
833 impl
= new SnapshotCopyOrMoveImpl(
834 operation_runner(), operation_type_
, src_url
, dest_url
, option_
,
836 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress
,
837 weak_factory_
.GetWeakPtr(), src_url
));
841 // Register the running task.
842 running_copy_set_
.insert(impl
);
843 impl
->Run(base::Bind(
844 &CopyOrMoveOperationDelegate::DidCopyOrMoveFile
,
845 weak_factory_
.GetWeakPtr(), src_url
, dest_url
, callback
, impl
));
848 void CopyOrMoveOperationDelegate::ProcessDirectory(
849 const FileSystemURL
& src_url
,
850 const StatusCallback
& callback
) {
851 if (src_url
== src_root_
) {
852 // The src_root_ looks to be a directory.
853 // Try removing the dest_root_ to see if it exists and/or it is an
855 // We do not invoke |progress_callback_| for source root, because it is
856 // already called in ProcessFile().
857 operation_runner()->RemoveDirectory(
859 base::Bind(&CopyOrMoveOperationDelegate::DidTryRemoveDestRoot
,
860 weak_factory_
.GetWeakPtr(), callback
));
864 if (!progress_callback_
.is_null()) {
865 progress_callback_
.Run(
866 FileSystemOperation::BEGIN_COPY_ENTRY
, src_url
, FileSystemURL(), 0);
869 ProcessDirectoryInternal(src_url
, CreateDestURL(src_url
), callback
);
872 void CopyOrMoveOperationDelegate::PostProcessDirectory(
873 const FileSystemURL
& src_url
,
874 const StatusCallback
& callback
) {
875 if (option_
== FileSystemOperation::OPTION_NONE
) {
876 PostProcessDirectoryAfterTouchFile(
877 src_url
, callback
, base::File::FILE_OK
);
881 operation_runner()->GetMetadata(
884 &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterGetMetadata
,
885 weak_factory_
.GetWeakPtr(), src_url
, callback
));
888 void CopyOrMoveOperationDelegate::OnCancel() {
889 // Request to cancel all running Copy/Move file.
890 for (std::set
<CopyOrMoveImpl
*>::iterator iter
= running_copy_set_
.begin();
891 iter
!= running_copy_set_
.end(); ++iter
)
895 void CopyOrMoveOperationDelegate::DidCopyOrMoveFile(
896 const FileSystemURL
& src_url
,
897 const FileSystemURL
& dest_url
,
898 const StatusCallback
& callback
,
899 CopyOrMoveImpl
* impl
,
900 base::File::Error error
) {
901 running_copy_set_
.erase(impl
);
904 if (!progress_callback_
.is_null() && error
== base::File::FILE_OK
) {
905 progress_callback_
.Run(
906 FileSystemOperation::END_COPY_ENTRY
, src_url
, dest_url
, 0);
912 void CopyOrMoveOperationDelegate::DidTryRemoveDestRoot(
913 const StatusCallback
& callback
,
914 base::File::Error error
) {
915 if (error
== base::File::FILE_ERROR_NOT_A_DIRECTORY
) {
916 callback_
.Run(base::File::FILE_ERROR_INVALID_OPERATION
);
919 if (error
!= base::File::FILE_OK
&&
920 error
!= base::File::FILE_ERROR_NOT_FOUND
) {
921 callback_
.Run(error
);
925 ProcessDirectoryInternal(src_root_
, dest_root_
, callback
);
928 void CopyOrMoveOperationDelegate::ProcessDirectoryInternal(
929 const FileSystemURL
& src_url
,
930 const FileSystemURL
& dest_url
,
931 const StatusCallback
& callback
) {
932 // If operation_type == Move we may need to record directories and
933 // restore directory timestamps in the end, though it may have
934 // negative performance impact.
935 // See http://crbug.com/171284 for more details.
936 operation_runner()->CreateDirectory(
937 dest_url
, false /* exclusive */, false /* recursive */,
938 base::Bind(&CopyOrMoveOperationDelegate::DidCreateDirectory
,
939 weak_factory_
.GetWeakPtr(), src_url
, dest_url
, callback
));
942 void CopyOrMoveOperationDelegate::DidCreateDirectory(
943 const FileSystemURL
& src_url
,
944 const FileSystemURL
& dest_url
,
945 const StatusCallback
& callback
,
946 base::File::Error error
) {
947 if (!progress_callback_
.is_null() && error
== base::File::FILE_OK
) {
948 progress_callback_
.Run(
949 FileSystemOperation::END_COPY_ENTRY
, src_url
, dest_url
, 0);
955 void CopyOrMoveOperationDelegate::PostProcessDirectoryAfterGetMetadata(
956 const FileSystemURL
& src_url
,
957 const StatusCallback
& callback
,
958 base::File::Error error
,
959 const base::File::Info
& file_info
) {
960 if (error
!= base::File::FILE_OK
) {
961 // Ignore the error, and run post process which should run after TouchFile.
962 PostProcessDirectoryAfterTouchFile(
963 src_url
, callback
, base::File::FILE_OK
);
967 operation_runner()->TouchFile(
968 CreateDestURL(src_url
), base::Time::Now() /* last access */,
969 file_info
.last_modified
,
971 &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterTouchFile
,
972 weak_factory_
.GetWeakPtr(), src_url
, callback
));
975 void CopyOrMoveOperationDelegate::PostProcessDirectoryAfterTouchFile(
976 const FileSystemURL
& src_url
,
977 const StatusCallback
& callback
,
978 base::File::Error error
) {
979 // Even if the TouchFile is failed, just ignore it.
981 if (operation_type_
== OPERATION_COPY
) {
982 callback
.Run(base::File::FILE_OK
);
986 DCHECK_EQ(OPERATION_MOVE
, operation_type_
);
988 // All files and subdirectories in the directory should be moved here,
989 // so remove the source directory for finalizing move operation.
990 operation_runner()->Remove(
991 src_url
, false /* recursive */,
992 base::Bind(&CopyOrMoveOperationDelegate::DidRemoveSourceForMove
,
993 weak_factory_
.GetWeakPtr(), callback
));
996 void CopyOrMoveOperationDelegate::DidRemoveSourceForMove(
997 const StatusCallback
& callback
,
998 base::File::Error error
) {
999 if (error
== base::File::FILE_ERROR_NOT_FOUND
)
1000 error
= base::File::FILE_OK
;
1001 callback
.Run(error
);
1004 void CopyOrMoveOperationDelegate::OnCopyFileProgress(
1005 const FileSystemURL
& src_url
, int64 size
) {
1006 if (!progress_callback_
.is_null()) {
1007 progress_callback_
.Run(
1008 FileSystemOperation::PROGRESS
, src_url
, FileSystemURL(), size
);
1012 FileSystemURL
CopyOrMoveOperationDelegate::CreateDestURL(
1013 const FileSystemURL
& src_url
) const {
1014 DCHECK_EQ(src_root_
.type(), src_url
.type());
1015 DCHECK_EQ(src_root_
.origin(), src_url
.origin());
1017 base::FilePath relative
= dest_root_
.virtual_path();
1018 src_root_
.virtual_path().AppendRelativePath(src_url
.virtual_path(),
1020 return file_system_context()->CreateCrackedFileSystemURL(
1021 dest_root_
.origin(),
1022 dest_root_
.mount_type(),
1026 } // namespace storage