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