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/common/fileapi/file_system_util.h"
23 const int64 kFlushIntervalInBytes
= 10 << 20; // 10MB.
25 class CopyOrMoveOperationDelegate::CopyOrMoveImpl
{
27 virtual ~CopyOrMoveImpl() {}
29 const CopyOrMoveOperationDelegate::StatusCallback
& callback
) = 0;
30 virtual void Cancel() = 0;
36 DISALLOW_COPY_AND_ASSIGN(CopyOrMoveImpl
);
41 // Copies a file on a (same) file system. Just delegate the operation to
42 // |operation_runner|.
43 class CopyOrMoveOnSameFileSystemImpl
44 : public CopyOrMoveOperationDelegate::CopyOrMoveImpl
{
46 CopyOrMoveOnSameFileSystemImpl(
47 FileSystemOperationRunner
* operation_runner
,
48 CopyOrMoveOperationDelegate::OperationType operation_type
,
49 const FileSystemURL
& src_url
,
50 const FileSystemURL
& dest_url
,
51 CopyOrMoveOperationDelegate::CopyOrMoveOption option
,
52 const FileSystemOperation::CopyFileProgressCallback
&
53 file_progress_callback
)
54 : operation_runner_(operation_runner
),
55 operation_type_(operation_type
),
59 file_progress_callback_(file_progress_callback
) {
63 const CopyOrMoveOperationDelegate::StatusCallback
& callback
) override
{
64 if (operation_type_
== CopyOrMoveOperationDelegate::OPERATION_MOVE
) {
65 operation_runner_
->MoveFileLocal(src_url_
, dest_url_
, option_
, callback
);
67 operation_runner_
->CopyFileLocal(
68 src_url_
, dest_url_
, option_
, file_progress_callback_
, callback
);
72 void Cancel() override
{
73 // We can do nothing for the copy/move operation on a local file system.
74 // Assuming the operation is quickly done, it should be ok to just wait
75 // for the completion.
79 FileSystemOperationRunner
* operation_runner_
;
80 CopyOrMoveOperationDelegate::OperationType operation_type_
;
81 FileSystemURL src_url_
;
82 FileSystemURL dest_url_
;
83 CopyOrMoveOperationDelegate::CopyOrMoveOption option_
;
84 FileSystemOperation::CopyFileProgressCallback file_progress_callback_
;
85 DISALLOW_COPY_AND_ASSIGN(CopyOrMoveOnSameFileSystemImpl
);
88 // Specifically for cross file system copy/move operation, this class creates
89 // a snapshot file, validates it if necessary, runs copying process,
90 // validates the created file, and removes source file for move (noop for
92 class SnapshotCopyOrMoveImpl
93 : public CopyOrMoveOperationDelegate::CopyOrMoveImpl
{
95 SnapshotCopyOrMoveImpl(
96 FileSystemOperationRunner
* operation_runner
,
97 CopyOrMoveOperationDelegate::OperationType operation_type
,
98 const FileSystemURL
& src_url
,
99 const FileSystemURL
& dest_url
,
100 CopyOrMoveOperationDelegate::CopyOrMoveOption option
,
101 CopyOrMoveFileValidatorFactory
* validator_factory
,
102 const FileSystemOperation::CopyFileProgressCallback
&
103 file_progress_callback
)
104 : operation_runner_(operation_runner
),
105 operation_type_(operation_type
),
109 validator_factory_(validator_factory
),
110 file_progress_callback_(file_progress_callback
),
111 cancel_requested_(false),
112 weak_factory_(this) {
116 const CopyOrMoveOperationDelegate::StatusCallback
& callback
) override
{
117 file_progress_callback_
.Run(0);
118 operation_runner_
->CreateSnapshotFile(
120 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterCreateSnapshot
,
121 weak_factory_
.GetWeakPtr(), callback
));
124 void Cancel() override
{ cancel_requested_
= true; }
127 void RunAfterCreateSnapshot(
128 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
129 base::File::Error error
,
130 const base::File::Info
& file_info
,
131 const base::FilePath
& platform_path
,
132 const scoped_refptr
<storage::ShareableFileReference
>& file_ref
) {
133 if (cancel_requested_
)
134 error
= base::File::FILE_ERROR_ABORT
;
136 if (error
!= base::File::FILE_OK
) {
141 // For now we assume CreateSnapshotFile always return a valid local file
143 DCHECK(!platform_path
.empty());
145 if (!validator_factory_
) {
146 // No validation is needed.
147 RunAfterPreWriteValidation(platform_path
, file_info
, file_ref
, callback
,
148 base::File::FILE_OK
);
152 // Run pre write validation.
155 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterPreWriteValidation
,
156 weak_factory_
.GetWeakPtr(),
157 platform_path
, file_info
, file_ref
, callback
));
160 void RunAfterPreWriteValidation(
161 const base::FilePath
& platform_path
,
162 const base::File::Info
& file_info
,
163 const scoped_refptr
<storage::ShareableFileReference
>& file_ref
,
164 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
165 base::File::Error error
) {
166 if (cancel_requested_
)
167 error
= base::File::FILE_ERROR_ABORT
;
169 if (error
!= base::File::FILE_OK
) {
174 // |file_ref| is unused but necessary to keep the file alive until
175 // CopyInForeignFile() is completed.
176 operation_runner_
->CopyInForeignFile(
177 platform_path
, dest_url_
,
178 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterCopyInForeignFile
,
179 weak_factory_
.GetWeakPtr(), file_info
, file_ref
, callback
));
182 void RunAfterCopyInForeignFile(
183 const base::File::Info
& file_info
,
184 const scoped_refptr
<storage::ShareableFileReference
>& file_ref
,
185 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
186 base::File::Error error
) {
187 if (cancel_requested_
)
188 error
= base::File::FILE_ERROR_ABORT
;
190 if (error
!= base::File::FILE_OK
) {
195 file_progress_callback_
.Run(file_info
.size
);
197 if (option_
== FileSystemOperation::OPTION_NONE
) {
198 RunAfterTouchFile(callback
, base::File::FILE_OK
);
202 operation_runner_
->TouchFile(
203 dest_url_
, base::Time::Now() /* last_access */,
204 file_info
.last_modified
,
205 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterTouchFile
,
206 weak_factory_
.GetWeakPtr(), callback
));
209 void RunAfterTouchFile(
210 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
211 base::File::Error error
) {
212 // Even if TouchFile is failed, just ignore it.
214 if (cancel_requested_
) {
215 callback
.Run(base::File::FILE_ERROR_ABORT
);
219 // |validator_| is NULL when the destination filesystem does not do
222 // No validation is needed.
223 RunAfterPostWriteValidation(callback
, base::File::FILE_OK
);
228 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterPostWriteValidation
,
229 weak_factory_
.GetWeakPtr(), callback
));
232 void RunAfterPostWriteValidation(
233 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
234 base::File::Error error
) {
235 if (cancel_requested_
) {
236 callback
.Run(base::File::FILE_ERROR_ABORT
);
240 if (error
!= base::File::FILE_OK
) {
241 // Failed to validate. Remove the destination file.
242 operation_runner_
->Remove(
243 dest_url_
, true /* recursive */,
244 base::Bind(&SnapshotCopyOrMoveImpl::DidRemoveDestForError
,
245 weak_factory_
.GetWeakPtr(), error
, callback
));
249 if (operation_type_
== CopyOrMoveOperationDelegate::OPERATION_COPY
) {
250 callback
.Run(base::File::FILE_OK
);
254 DCHECK_EQ(CopyOrMoveOperationDelegate::OPERATION_MOVE
, operation_type_
);
256 // Remove the source for finalizing move operation.
257 operation_runner_
->Remove(
258 src_url_
, true /* recursive */,
259 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterRemoveSourceForMove
,
260 weak_factory_
.GetWeakPtr(), callback
));
263 void RunAfterRemoveSourceForMove(
264 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
265 base::File::Error error
) {
266 if (cancel_requested_
)
267 error
= base::File::FILE_ERROR_ABORT
;
269 if (error
== base::File::FILE_ERROR_NOT_FOUND
)
270 error
= base::File::FILE_OK
;
274 void DidRemoveDestForError(
275 base::File::Error prior_error
,
276 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
277 base::File::Error error
) {
278 if (error
!= base::File::FILE_OK
) {
279 VLOG(1) << "Error removing destination file after validation error: "
282 callback
.Run(prior_error
);
285 // Runs pre-write validation.
286 void PreWriteValidation(
287 const base::FilePath
& platform_path
,
288 const CopyOrMoveOperationDelegate::StatusCallback
& callback
) {
289 DCHECK(validator_factory_
);
291 validator_factory_
->CreateCopyOrMoveFileValidator(
292 src_url_
, platform_path
));
293 validator_
->StartPreWriteValidation(callback
);
296 // Runs post-write validation.
297 void PostWriteValidation(
298 const CopyOrMoveOperationDelegate::StatusCallback
& callback
) {
299 operation_runner_
->CreateSnapshotFile(
302 &SnapshotCopyOrMoveImpl::PostWriteValidationAfterCreateSnapshotFile
,
303 weak_factory_
.GetWeakPtr(), callback
));
306 void PostWriteValidationAfterCreateSnapshotFile(
307 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
308 base::File::Error error
,
309 const base::File::Info
& file_info
,
310 const base::FilePath
& platform_path
,
311 const scoped_refptr
<storage::ShareableFileReference
>& file_ref
) {
312 if (cancel_requested_
)
313 error
= base::File::FILE_ERROR_ABORT
;
315 if (error
!= base::File::FILE_OK
) {
321 // Note: file_ref passed here to keep the file alive until after
322 // the StartPostWriteValidation operation finishes.
323 validator_
->StartPostWriteValidation(
325 base::Bind(&SnapshotCopyOrMoveImpl::DidPostWriteValidation
,
326 weak_factory_
.GetWeakPtr(), file_ref
, callback
));
329 // |file_ref| is unused; it is passed here to make sure the reference is
330 // alive until after post-write validation is complete.
331 void DidPostWriteValidation(
332 const scoped_refptr
<storage::ShareableFileReference
>& file_ref
,
333 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
334 base::File::Error error
) {
338 FileSystemOperationRunner
* operation_runner_
;
339 CopyOrMoveOperationDelegate::OperationType operation_type_
;
340 FileSystemURL src_url_
;
341 FileSystemURL dest_url_
;
343 CopyOrMoveOperationDelegate::CopyOrMoveOption option_
;
344 CopyOrMoveFileValidatorFactory
* validator_factory_
;
345 scoped_ptr
<CopyOrMoveFileValidator
> validator_
;
346 FileSystemOperation::CopyFileProgressCallback file_progress_callback_
;
347 bool cancel_requested_
;
348 base::WeakPtrFactory
<SnapshotCopyOrMoveImpl
> weak_factory_
;
349 DISALLOW_COPY_AND_ASSIGN(SnapshotCopyOrMoveImpl
);
352 // The size of buffer for StreamCopyHelper.
353 const int kReadBufferSize
= 32768;
355 // To avoid too many progress callbacks, it should be called less
356 // frequently than 50ms.
357 const int kMinProgressCallbackInvocationSpanInMilliseconds
= 50;
359 // Specifically for cross file system copy/move operation, this class uses
360 // stream reader and writer for copying. Validator is not supported, so if
361 // necessary SnapshotCopyOrMoveImpl should be used.
362 class StreamCopyOrMoveImpl
363 : public CopyOrMoveOperationDelegate::CopyOrMoveImpl
{
365 StreamCopyOrMoveImpl(
366 FileSystemOperationRunner
* operation_runner
,
367 FileSystemContext
* file_system_context
,
368 CopyOrMoveOperationDelegate::OperationType operation_type
,
369 const FileSystemURL
& src_url
,
370 const FileSystemURL
& dest_url
,
371 CopyOrMoveOperationDelegate::CopyOrMoveOption option
,
372 scoped_ptr
<storage::FileStreamReader
> reader
,
373 scoped_ptr
<FileStreamWriter
> writer
,
374 const FileSystemOperation::CopyFileProgressCallback
&
375 file_progress_callback
)
376 : operation_runner_(operation_runner
),
377 file_system_context_(file_system_context
),
378 operation_type_(operation_type
),
382 reader_(reader
.Pass()),
383 writer_(writer
.Pass()),
384 file_progress_callback_(file_progress_callback
),
385 cancel_requested_(false),
386 weak_factory_(this) {}
389 const CopyOrMoveOperationDelegate::StatusCallback
& callback
) override
{
390 // Reader can be created even if the entry does not exist or the entry is
391 // a directory. To check errors before destination file creation,
392 // check metadata first.
393 operation_runner_
->GetMetadata(
395 base::Bind(&StreamCopyOrMoveImpl::RunAfterGetMetadataForSource
,
396 weak_factory_
.GetWeakPtr(), callback
));
399 void Cancel() override
{
400 cancel_requested_
= true;
402 copy_helper_
->Cancel();
406 void NotifyOnStartUpdate(const FileSystemURL
& url
) {
407 if (file_system_context_
->GetUpdateObservers(url
.type())) {
408 file_system_context_
->GetUpdateObservers(url
.type())
409 ->Notify(&FileUpdateObserver::OnStartUpdate
, base::MakeTuple(url
));
413 void NotifyOnModifyFile(const FileSystemURL
& url
) {
414 if (file_system_context_
->GetChangeObservers(url
.type())) {
415 file_system_context_
->GetChangeObservers(url
.type())
416 ->Notify(&FileChangeObserver::OnModifyFile
, base::MakeTuple(url
));
420 void NotifyOnEndUpdate(const FileSystemURL
& url
) {
421 if (file_system_context_
->GetUpdateObservers(url
.type())) {
422 file_system_context_
->GetUpdateObservers(url
.type())
423 ->Notify(&FileUpdateObserver::OnEndUpdate
, base::MakeTuple(url
));
427 void RunAfterGetMetadataForSource(
428 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
429 base::File::Error error
,
430 const base::File::Info
& file_info
) {
431 if (cancel_requested_
)
432 error
= base::File::FILE_ERROR_ABORT
;
434 if (error
!= base::File::FILE_OK
) {
439 if (file_info
.is_directory
) {
440 // If not a directory, failed with appropriate error code.
441 callback
.Run(base::File::FILE_ERROR_NOT_A_FILE
);
445 // To use FileStreamWriter, we need to ensure the destination file exists.
446 operation_runner_
->CreateFile(
448 true /* exclusive */,
449 base::Bind(&StreamCopyOrMoveImpl::RunAfterCreateFileForDestination
,
450 weak_factory_
.GetWeakPtr(),
452 file_info
.last_modified
));
455 void RunAfterCreateFileForDestination(
456 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
457 const base::Time
& last_modified
,
458 base::File::Error error
) {
459 if (cancel_requested_
)
460 error
= base::File::FILE_ERROR_ABORT
;
461 // This conversion is to return the consistent status code with
462 // FileSystemFileUtil::Copy.
463 if (error
== base::File::FILE_ERROR_NOT_A_FILE
)
464 error
= base::File::FILE_ERROR_INVALID_OPERATION
;
466 if (error
!= base::File::FILE_OK
&&
467 error
!= base::File::FILE_ERROR_EXISTS
) {
472 if (error
== base::File::FILE_ERROR_EXISTS
) {
473 operation_runner_
->Truncate(
476 base::Bind(&StreamCopyOrMoveImpl::RunAfterTruncateForDestination
,
477 weak_factory_
.GetWeakPtr(),
482 RunAfterTruncateForDestination(
483 callback
, last_modified
, base::File::FILE_OK
);
486 void RunAfterTruncateForDestination(
487 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
488 const base::Time
& last_modified
,
489 base::File::Error error
) {
490 if (cancel_requested_
)
491 error
= base::File::FILE_ERROR_ABORT
;
493 if (error
!= base::File::FILE_OK
) {
498 NotifyOnStartUpdate(dest_url_
);
499 DCHECK(!copy_helper_
);
500 copy_helper_
.reset(new CopyOrMoveOperationDelegate::StreamCopyHelper(
501 reader_
.Pass(), writer_
.Pass(), dest_url_
.mount_option().flush_policy(),
502 kReadBufferSize
, file_progress_callback_
,
503 base::TimeDelta::FromMilliseconds(
504 kMinProgressCallbackInvocationSpanInMilliseconds
)));
506 base::Bind(&StreamCopyOrMoveImpl::RunAfterStreamCopy
,
507 weak_factory_
.GetWeakPtr(), callback
, last_modified
));
510 void RunAfterStreamCopy(
511 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
512 const base::Time
& last_modified
,
513 base::File::Error error
) {
514 NotifyOnModifyFile(dest_url_
);
515 NotifyOnEndUpdate(dest_url_
);
516 if (cancel_requested_
)
517 error
= base::File::FILE_ERROR_ABORT
;
519 if (error
!= base::File::FILE_OK
) {
524 if (option_
== FileSystemOperation::OPTION_NONE
) {
525 RunAfterTouchFile(callback
, base::File::FILE_OK
);
529 operation_runner_
->TouchFile(
530 dest_url_
, base::Time::Now() /* last_access */, last_modified
,
531 base::Bind(&StreamCopyOrMoveImpl::RunAfterTouchFile
,
532 weak_factory_
.GetWeakPtr(), callback
));
535 void RunAfterTouchFile(
536 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
537 base::File::Error error
) {
538 // Even if TouchFile is failed, just ignore it.
539 if (cancel_requested_
) {
540 callback
.Run(base::File::FILE_ERROR_ABORT
);
544 if (operation_type_
== CopyOrMoveOperationDelegate::OPERATION_COPY
) {
545 callback
.Run(base::File::FILE_OK
);
549 DCHECK_EQ(CopyOrMoveOperationDelegate::OPERATION_MOVE
, operation_type_
);
551 // Remove the source for finalizing move operation.
552 operation_runner_
->Remove(
553 src_url_
, false /* recursive */,
554 base::Bind(&StreamCopyOrMoveImpl::RunAfterRemoveForMove
,
555 weak_factory_
.GetWeakPtr(), callback
));
558 void RunAfterRemoveForMove(
559 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
560 base::File::Error error
) {
561 if (cancel_requested_
)
562 error
= base::File::FILE_ERROR_ABORT
;
563 if (error
== base::File::FILE_ERROR_NOT_FOUND
)
564 error
= base::File::FILE_OK
;
568 FileSystemOperationRunner
* operation_runner_
;
569 scoped_refptr
<FileSystemContext
> file_system_context_
;
570 CopyOrMoveOperationDelegate::OperationType operation_type_
;
571 FileSystemURL src_url_
;
572 FileSystemURL dest_url_
;
573 CopyOrMoveOperationDelegate::CopyOrMoveOption option_
;
574 scoped_ptr
<storage::FileStreamReader
> reader_
;
575 scoped_ptr
<FileStreamWriter
> writer_
;
576 FileSystemOperation::CopyFileProgressCallback file_progress_callback_
;
577 scoped_ptr
<CopyOrMoveOperationDelegate::StreamCopyHelper
> copy_helper_
;
578 bool cancel_requested_
;
579 base::WeakPtrFactory
<StreamCopyOrMoveImpl
> weak_factory_
;
580 DISALLOW_COPY_AND_ASSIGN(StreamCopyOrMoveImpl
);
585 CopyOrMoveOperationDelegate::StreamCopyHelper::StreamCopyHelper(
586 scoped_ptr
<storage::FileStreamReader
> reader
,
587 scoped_ptr
<FileStreamWriter
> writer
,
588 storage::FlushPolicy flush_policy
,
590 const FileSystemOperation::CopyFileProgressCallback
& file_progress_callback
,
591 const base::TimeDelta
& min_progress_callback_invocation_span
)
592 : reader_(reader
.Pass()),
593 writer_(writer
.Pass()),
594 flush_policy_(flush_policy
),
595 file_progress_callback_(file_progress_callback
),
596 io_buffer_(new net::IOBufferWithSize(buffer_size
)),
597 num_copied_bytes_(0),
598 previous_flush_offset_(0),
599 min_progress_callback_invocation_span_(
600 min_progress_callback_invocation_span
),
601 cancel_requested_(false),
602 weak_factory_(this) {
605 CopyOrMoveOperationDelegate::StreamCopyHelper::~StreamCopyHelper() {
608 void CopyOrMoveOperationDelegate::StreamCopyHelper::Run(
609 const StatusCallback
& callback
) {
610 file_progress_callback_
.Run(0);
611 last_progress_callback_invocation_time_
= base::Time::Now();
615 void CopyOrMoveOperationDelegate::StreamCopyHelper::Cancel() {
616 cancel_requested_
= true;
619 void CopyOrMoveOperationDelegate::StreamCopyHelper::Read(
620 const StatusCallback
& callback
) {
621 int result
= reader_
->Read(
622 io_buffer_
.get(), io_buffer_
->size(),
623 base::Bind(&StreamCopyHelper::DidRead
,
624 weak_factory_
.GetWeakPtr(), callback
));
625 if (result
!= net::ERR_IO_PENDING
)
626 DidRead(callback
, result
);
629 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidRead(
630 const StatusCallback
& callback
, int result
) {
631 if (cancel_requested_
) {
632 callback
.Run(base::File::FILE_ERROR_ABORT
);
637 callback
.Run(NetErrorToFileError(result
));
643 if (flush_policy_
== storage::FlushPolicy::FLUSH_ON_COMPLETION
)
644 Flush(callback
, true /* is_eof */);
646 callback
.Run(base::File::FILE_OK
);
650 Write(callback
, new net::DrainableIOBuffer(io_buffer_
.get(), result
));
653 void CopyOrMoveOperationDelegate::StreamCopyHelper::Write(
654 const StatusCallback
& callback
,
655 scoped_refptr
<net::DrainableIOBuffer
> buffer
) {
656 DCHECK_GT(buffer
->BytesRemaining(), 0);
658 int result
= writer_
->Write(
659 buffer
.get(), buffer
->BytesRemaining(),
660 base::Bind(&StreamCopyHelper::DidWrite
,
661 weak_factory_
.GetWeakPtr(), callback
, buffer
));
662 if (result
!= net::ERR_IO_PENDING
)
663 DidWrite(callback
, buffer
, result
);
666 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidWrite(
667 const StatusCallback
& callback
,
668 scoped_refptr
<net::DrainableIOBuffer
> buffer
,
670 if (cancel_requested_
) {
671 callback
.Run(base::File::FILE_ERROR_ABORT
);
676 callback
.Run(NetErrorToFileError(result
));
680 buffer
->DidConsume(result
);
681 num_copied_bytes_
+= result
;
683 // Check the elapsed time since last |file_progress_callback_| invocation.
684 base::Time now
= base::Time::Now();
685 if (now
- last_progress_callback_invocation_time_
>=
686 min_progress_callback_invocation_span_
) {
687 file_progress_callback_
.Run(num_copied_bytes_
);
688 last_progress_callback_invocation_time_
= now
;
691 if (buffer
->BytesRemaining() > 0) {
692 Write(callback
, buffer
);
696 if (flush_policy_
== storage::FlushPolicy::FLUSH_ON_COMPLETION
&&
697 (num_copied_bytes_
- previous_flush_offset_
) > kFlushIntervalInBytes
) {
698 Flush(callback
, false /* not is_eof */);
704 void CopyOrMoveOperationDelegate::StreamCopyHelper::Flush(
705 const StatusCallback
& callback
, bool is_eof
) {
706 int result
= writer_
->Flush(
707 base::Bind(&StreamCopyHelper::DidFlush
,
708 weak_factory_
.GetWeakPtr(), callback
, is_eof
));
709 if (result
!= net::ERR_IO_PENDING
)
710 DidFlush(callback
, is_eof
, result
);
713 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidFlush(
714 const StatusCallback
& callback
, bool is_eof
, int result
) {
715 if (cancel_requested_
) {
716 callback
.Run(base::File::FILE_ERROR_ABORT
);
720 previous_flush_offset_
= num_copied_bytes_
;
722 callback
.Run(NetErrorToFileError(result
));
727 CopyOrMoveOperationDelegate::CopyOrMoveOperationDelegate(
728 FileSystemContext
* file_system_context
,
729 const FileSystemURL
& src_root
,
730 const FileSystemURL
& dest_root
,
731 OperationType operation_type
,
732 CopyOrMoveOption option
,
733 ErrorBehavior error_behavior
,
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 error_behavior_(error_behavior
),
742 progress_callback_(progress_callback
),
744 weak_factory_(this) {
745 same_file_system_
= src_root_
.IsInSameFileSystem(dest_root_
);
748 CopyOrMoveOperationDelegate::~CopyOrMoveOperationDelegate() {
749 STLDeleteElements(&running_copy_set_
);
752 void CopyOrMoveOperationDelegate::Run() {
753 // Not supported; this should never be called.
757 void CopyOrMoveOperationDelegate::RunRecursively() {
758 // Perform light-weight checks first.
760 // It is an error to try to copy/move an entry into its child.
761 if (same_file_system_
&& src_root_
.path().IsParent(dest_root_
.path())) {
762 callback_
.Run(base::File::FILE_ERROR_INVALID_OPERATION
);
766 if (same_file_system_
&& src_root_
.path() == dest_root_
.path()) {
767 // In JS API this should return error, but we return success because Pepper
768 // wants to return success and we have a code path that returns error in
769 // Blink for JS (http://crbug.com/329517).
770 callback_
.Run(base::File::FILE_OK
);
774 // Start to process the source directory recursively.
775 // TODO(kinuko): This could be too expensive for same_file_system_==true
776 // and operation==MOVE case, probably we can just rename the root directory.
777 // http://crbug.com/172187
778 StartRecursiveOperation(src_root_
, error_behavior_
, callback_
);
781 void CopyOrMoveOperationDelegate::ProcessFile(
782 const FileSystemURL
& src_url
,
783 const StatusCallback
& callback
) {
784 if (!progress_callback_
.is_null()) {
785 progress_callback_
.Run(
786 FileSystemOperation::BEGIN_COPY_ENTRY
, src_url
, FileSystemURL(), 0);
789 FileSystemURL dest_url
= CreateDestURL(src_url
);
790 CopyOrMoveImpl
* impl
= NULL
;
791 if (same_file_system_
&&
792 (file_system_context()
793 ->GetFileSystemBackend(src_url
.type())
794 ->HasInplaceCopyImplementation(src_url
.type()) ||
795 operation_type_
== OPERATION_MOVE
)) {
796 impl
= new CopyOrMoveOnSameFileSystemImpl(
797 operation_runner(), operation_type_
, src_url
, dest_url
, option_
,
798 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress
,
799 weak_factory_
.GetWeakPtr(), src_url
));
801 // Cross filesystem case.
802 base::File::Error error
= base::File::FILE_ERROR_FAILED
;
803 CopyOrMoveFileValidatorFactory
* validator_factory
=
804 file_system_context()->GetCopyOrMoveFileValidatorFactory(
805 dest_root_
.type(), &error
);
806 if (error
!= base::File::FILE_OK
) {
807 if (!progress_callback_
.is_null())
808 progress_callback_
.Run(FileSystemOperation::ERROR_COPY_ENTRY
, src_url
,
815 if (!validator_factory
) {
816 scoped_ptr
<storage::FileStreamReader
> reader
=
817 file_system_context()->CreateFileStreamReader(
818 src_url
, 0 /* offset */, storage::kMaximumLength
, base::Time());
819 scoped_ptr
<FileStreamWriter
> writer
=
820 file_system_context()->CreateFileStreamWriter(dest_url
, 0);
821 if (reader
&& writer
) {
822 impl
= new StreamCopyOrMoveImpl(
824 file_system_context(),
831 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress
,
832 weak_factory_
.GetWeakPtr(),
838 impl
= new SnapshotCopyOrMoveImpl(
839 operation_runner(), operation_type_
, src_url
, dest_url
, option_
,
841 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress
,
842 weak_factory_
.GetWeakPtr(), src_url
));
846 // Register the running task.
847 running_copy_set_
.insert(impl
);
848 impl
->Run(base::Bind(
849 &CopyOrMoveOperationDelegate::DidCopyOrMoveFile
,
850 weak_factory_
.GetWeakPtr(), src_url
, dest_url
, callback
, impl
));
853 void CopyOrMoveOperationDelegate::ProcessDirectory(
854 const FileSystemURL
& src_url
,
855 const StatusCallback
& callback
) {
856 if (src_url
== src_root_
) {
857 // The src_root_ looks to be a directory.
858 // Try removing the dest_root_ to see if it exists and/or it is an
860 // We do not invoke |progress_callback_| for source root, because it is
861 // already called in ProcessFile().
862 operation_runner()->RemoveDirectory(
864 base::Bind(&CopyOrMoveOperationDelegate::DidTryRemoveDestRoot
,
865 weak_factory_
.GetWeakPtr(), callback
));
869 if (!progress_callback_
.is_null()) {
870 progress_callback_
.Run(
871 FileSystemOperation::BEGIN_COPY_ENTRY
, src_url
, FileSystemURL(), 0);
874 ProcessDirectoryInternal(src_url
, CreateDestURL(src_url
), callback
);
877 void CopyOrMoveOperationDelegate::PostProcessDirectory(
878 const FileSystemURL
& src_url
,
879 const StatusCallback
& callback
) {
880 if (option_
== FileSystemOperation::OPTION_NONE
) {
881 PostProcessDirectoryAfterTouchFile(
882 src_url
, callback
, base::File::FILE_OK
);
886 operation_runner()->GetMetadata(
889 &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterGetMetadata
,
890 weak_factory_
.GetWeakPtr(), src_url
, callback
));
893 void CopyOrMoveOperationDelegate::OnCancel() {
894 // Request to cancel all running Copy/Move file.
895 for (std::set
<CopyOrMoveImpl
*>::iterator iter
= running_copy_set_
.begin();
896 iter
!= running_copy_set_
.end(); ++iter
)
900 void CopyOrMoveOperationDelegate::DidCopyOrMoveFile(
901 const FileSystemURL
& src_url
,
902 const FileSystemURL
& dest_url
,
903 const StatusCallback
& callback
,
904 CopyOrMoveImpl
* impl
,
905 base::File::Error error
) {
906 running_copy_set_
.erase(impl
);
909 if (!progress_callback_
.is_null() && error
!= base::File::FILE_OK
&&
910 error
!= base::File::FILE_ERROR_NOT_A_FILE
)
911 progress_callback_
.Run(FileSystemOperation::ERROR_COPY_ENTRY
, src_url
,
914 if (!progress_callback_
.is_null() && error
== base::File::FILE_OK
) {
915 progress_callback_
.Run(
916 FileSystemOperation::END_COPY_ENTRY
, src_url
, dest_url
, 0);
922 void CopyOrMoveOperationDelegate::DidTryRemoveDestRoot(
923 const StatusCallback
& callback
,
924 base::File::Error error
) {
925 if (error
== base::File::FILE_ERROR_NOT_A_DIRECTORY
) {
926 callback_
.Run(base::File::FILE_ERROR_INVALID_OPERATION
);
929 if (error
!= base::File::FILE_OK
&&
930 error
!= base::File::FILE_ERROR_NOT_FOUND
) {
931 callback_
.Run(error
);
935 ProcessDirectoryInternal(src_root_
, dest_root_
, callback
);
938 void CopyOrMoveOperationDelegate::ProcessDirectoryInternal(
939 const FileSystemURL
& src_url
,
940 const FileSystemURL
& dest_url
,
941 const StatusCallback
& callback
) {
942 // If operation_type == Move we may need to record directories and
943 // restore directory timestamps in the end, though it may have
944 // negative performance impact.
945 // See http://crbug.com/171284 for more details.
946 operation_runner()->CreateDirectory(
947 dest_url
, false /* exclusive */, false /* recursive */,
948 base::Bind(&CopyOrMoveOperationDelegate::DidCreateDirectory
,
949 weak_factory_
.GetWeakPtr(), src_url
, dest_url
, callback
));
952 void CopyOrMoveOperationDelegate::DidCreateDirectory(
953 const FileSystemURL
& src_url
,
954 const FileSystemURL
& dest_url
,
955 const StatusCallback
& callback
,
956 base::File::Error error
) {
957 if (!progress_callback_
.is_null() && error
== base::File::FILE_OK
) {
958 progress_callback_
.Run(
959 FileSystemOperation::END_COPY_ENTRY
, src_url
, dest_url
, 0);
965 void CopyOrMoveOperationDelegate::PostProcessDirectoryAfterGetMetadata(
966 const FileSystemURL
& src_url
,
967 const StatusCallback
& callback
,
968 base::File::Error error
,
969 const base::File::Info
& file_info
) {
970 if (error
!= base::File::FILE_OK
) {
971 // Ignore the error, and run post process which should run after TouchFile.
972 PostProcessDirectoryAfterTouchFile(
973 src_url
, callback
, base::File::FILE_OK
);
977 operation_runner()->TouchFile(
978 CreateDestURL(src_url
), base::Time::Now() /* last access */,
979 file_info
.last_modified
,
981 &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterTouchFile
,
982 weak_factory_
.GetWeakPtr(), src_url
, callback
));
985 void CopyOrMoveOperationDelegate::PostProcessDirectoryAfterTouchFile(
986 const FileSystemURL
& src_url
,
987 const StatusCallback
& callback
,
988 base::File::Error error
) {
989 // Even if the TouchFile is failed, just ignore it.
991 if (operation_type_
== OPERATION_COPY
) {
992 callback
.Run(base::File::FILE_OK
);
996 DCHECK_EQ(OPERATION_MOVE
, operation_type_
);
998 // All files and subdirectories in the directory should be moved here,
999 // so remove the source directory for finalizing move operation.
1000 operation_runner()->Remove(
1001 src_url
, false /* recursive */,
1002 base::Bind(&CopyOrMoveOperationDelegate::DidRemoveSourceForMove
,
1003 weak_factory_
.GetWeakPtr(), callback
));
1006 void CopyOrMoveOperationDelegate::DidRemoveSourceForMove(
1007 const StatusCallback
& callback
,
1008 base::File::Error error
) {
1009 if (error
== base::File::FILE_ERROR_NOT_FOUND
)
1010 error
= base::File::FILE_OK
;
1011 callback
.Run(error
);
1014 void CopyOrMoveOperationDelegate::OnCopyFileProgress(
1015 const FileSystemURL
& src_url
, int64 size
) {
1016 if (!progress_callback_
.is_null()) {
1017 progress_callback_
.Run(
1018 FileSystemOperation::PROGRESS
, src_url
, FileSystemURL(), size
);
1022 FileSystemURL
CopyOrMoveOperationDelegate::CreateDestURL(
1023 const FileSystemURL
& src_url
) const {
1024 DCHECK_EQ(src_root_
.type(), src_url
.type());
1025 DCHECK_EQ(src_root_
.origin(), src_url
.origin());
1027 base::FilePath relative
= dest_root_
.virtual_path();
1028 src_root_
.virtual_path().AppendRelativePath(src_url
.virtual_path(),
1030 return file_system_context()->CreateCrackedFileSystemURL(
1031 dest_root_
.origin(),
1032 dest_root_
.mount_type(),
1036 } // namespace storage