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 "webkit/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 "webkit/browser/blob/file_stream_reader.h"
12 #include "webkit/browser/fileapi/copy_or_move_file_validator.h"
13 #include "webkit/browser/fileapi/file_stream_writer.h"
14 #include "webkit/browser/fileapi/file_system_context.h"
15 #include "webkit/browser/fileapi/file_system_operation_runner.h"
16 #include "webkit/browser/fileapi/file_system_url.h"
17 #include "webkit/browser/fileapi/recursive_operation_delegate.h"
18 #include "webkit/common/blob/shareable_file_reference.h"
19 #include "webkit/common/fileapi/file_system_util.h"
23 class CopyOrMoveOperationDelegate::CopyOrMoveImpl
{
25 virtual ~CopyOrMoveImpl() {}
27 const CopyOrMoveOperationDelegate::StatusCallback
& callback
) = 0;
28 virtual void Cancel() = 0;
34 DISALLOW_COPY_AND_ASSIGN(CopyOrMoveImpl
);
39 // Copies a file on a (same) file system. Just delegate the operation to
40 // |operation_runner|.
41 class CopyOrMoveOnSameFileSystemImpl
42 : public CopyOrMoveOperationDelegate::CopyOrMoveImpl
{
44 CopyOrMoveOnSameFileSystemImpl(
45 FileSystemOperationRunner
* operation_runner
,
46 CopyOrMoveOperationDelegate::OperationType operation_type
,
47 const FileSystemURL
& src_url
,
48 const FileSystemURL
& dest_url
,
49 CopyOrMoveOperationDelegate::CopyOrMoveOption option
,
50 const FileSystemOperation::CopyFileProgressCallback
&
51 file_progress_callback
)
52 : operation_runner_(operation_runner
),
53 operation_type_(operation_type
),
57 file_progress_callback_(file_progress_callback
) {
61 const CopyOrMoveOperationDelegate::StatusCallback
& callback
) OVERRIDE
{
62 if (operation_type_
== CopyOrMoveOperationDelegate::OPERATION_MOVE
) {
63 operation_runner_
->MoveFileLocal(src_url_
, dest_url_
, option_
, callback
);
65 operation_runner_
->CopyFileLocal(
66 src_url_
, dest_url_
, option_
, file_progress_callback_
, callback
);
70 virtual void Cancel() OVERRIDE
{
71 // We can do nothing for the copy/move operation on a local file system.
72 // Assuming the operation is quickly done, it should be ok to just wait
73 // for the completion.
77 FileSystemOperationRunner
* operation_runner_
;
78 CopyOrMoveOperationDelegate::OperationType operation_type_
;
79 FileSystemURL src_url_
;
80 FileSystemURL dest_url_
;
81 CopyOrMoveOperationDelegate::CopyOrMoveOption option_
;
82 FileSystemOperation::CopyFileProgressCallback file_progress_callback_
;
83 DISALLOW_COPY_AND_ASSIGN(CopyOrMoveOnSameFileSystemImpl
);
86 // Specifically for cross file system copy/move operation, this class creates
87 // a snapshot file, validates it if necessary, runs copying process,
88 // validates the created file, and removes source file for move (noop for
90 class SnapshotCopyOrMoveImpl
91 : public CopyOrMoveOperationDelegate::CopyOrMoveImpl
{
93 SnapshotCopyOrMoveImpl(
94 FileSystemOperationRunner
* operation_runner
,
95 CopyOrMoveOperationDelegate::OperationType operation_type
,
96 const FileSystemURL
& src_url
,
97 const FileSystemURL
& dest_url
,
98 CopyOrMoveOperationDelegate::CopyOrMoveOption option
,
99 CopyOrMoveFileValidatorFactory
* validator_factory
,
100 const FileSystemOperation::CopyFileProgressCallback
&
101 file_progress_callback
)
102 : operation_runner_(operation_runner
),
103 operation_type_(operation_type
),
107 validator_factory_(validator_factory
),
108 file_progress_callback_(file_progress_callback
),
109 cancel_requested_(false),
110 weak_factory_(this) {
114 const CopyOrMoveOperationDelegate::StatusCallback
& callback
) OVERRIDE
{
115 file_progress_callback_
.Run(0);
116 operation_runner_
->CreateSnapshotFile(
118 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterCreateSnapshot
,
119 weak_factory_
.GetWeakPtr(), callback
));
122 virtual void Cancel() OVERRIDE
{
123 cancel_requested_
= true;
127 void RunAfterCreateSnapshot(
128 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
129 base::PlatformFileError error
,
130 const base::PlatformFileInfo
& file_info
,
131 const base::FilePath
& platform_path
,
132 const scoped_refptr
<webkit_blob::ShareableFileReference
>& file_ref
) {
133 if (cancel_requested_
)
134 error
= base::PLATFORM_FILE_ERROR_ABORT
;
136 if (error
!= base::PLATFORM_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::PLATFORM_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::PlatformFileInfo
& file_info
,
163 const scoped_refptr
<webkit_blob::ShareableFileReference
>& file_ref
,
164 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
165 base::PlatformFileError error
) {
166 if (cancel_requested_
)
167 error
= base::PLATFORM_FILE_ERROR_ABORT
;
169 if (error
!= base::PLATFORM_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::PlatformFileInfo
& file_info
,
184 const scoped_refptr
<webkit_blob::ShareableFileReference
>& file_ref
,
185 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
186 base::PlatformFileError error
) {
187 if (cancel_requested_
)
188 error
= base::PLATFORM_FILE_ERROR_ABORT
;
190 if (error
!= base::PLATFORM_FILE_OK
) {
195 file_progress_callback_
.Run(file_info
.size
);
197 if (option_
== FileSystemOperation::OPTION_NONE
) {
198 RunAfterTouchFile(callback
, base::PLATFORM_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::PlatformFileError error
) {
212 // Even if TouchFile is failed, just ignore it.
214 if (cancel_requested_
) {
215 callback
.Run(base::PLATFORM_FILE_ERROR_ABORT
);
219 // |validator_| is NULL when the destination filesystem does not do
222 // No validation is needed.
223 RunAfterPostWriteValidation(callback
, base::PLATFORM_FILE_OK
);
228 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterPostWriteValidation
,
229 weak_factory_
.GetWeakPtr(), callback
));
232 void RunAfterPostWriteValidation(
233 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
234 base::PlatformFileError error
) {
235 if (cancel_requested_
) {
236 callback
.Run(base::PLATFORM_FILE_ERROR_ABORT
);
240 if (error
!= base::PLATFORM_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::PLATFORM_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::PlatformFileError error
) {
266 if (cancel_requested_
)
267 error
= base::PLATFORM_FILE_ERROR_ABORT
;
269 if (error
== base::PLATFORM_FILE_ERROR_NOT_FOUND
)
270 error
= base::PLATFORM_FILE_OK
;
274 void DidRemoveDestForError(
275 base::PlatformFileError prior_error
,
276 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
277 base::PlatformFileError error
) {
278 if (error
!= base::PLATFORM_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::PlatformFileError error
,
309 const base::PlatformFileInfo
& file_info
,
310 const base::FilePath
& platform_path
,
311 const scoped_refptr
<webkit_blob::ShareableFileReference
>& file_ref
) {
312 if (cancel_requested_
)
313 error
= base::PLATFORM_FILE_ERROR_ABORT
;
315 if (error
!= base::PLATFORM_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
<webkit_blob::ShareableFileReference
>& file_ref
,
333 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
334 base::PlatformFileError 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 CopyOrMoveOperationDelegate::OperationType operation_type
,
368 const FileSystemURL
& src_url
,
369 const FileSystemURL
& dest_url
,
370 CopyOrMoveOperationDelegate::CopyOrMoveOption option
,
371 scoped_ptr
<webkit_blob::FileStreamReader
> reader
,
372 scoped_ptr
<FileStreamWriter
> writer
,
373 const FileSystemOperation::CopyFileProgressCallback
&
374 file_progress_callback
)
375 : operation_runner_(operation_runner
),
376 operation_type_(operation_type
),
380 reader_(reader
.Pass()),
381 writer_(writer
.Pass()),
382 file_progress_callback_(file_progress_callback
),
383 cancel_requested_(false),
384 weak_factory_(this) {
388 const CopyOrMoveOperationDelegate::StatusCallback
& callback
) OVERRIDE
{
389 // Reader can be created even if the entry does not exist or the entry is
390 // a directory. To check errors before destination file creation,
391 // check metadata first.
392 operation_runner_
->GetMetadata(
394 base::Bind(&StreamCopyOrMoveImpl::RunAfterGetMetadataForSource
,
395 weak_factory_
.GetWeakPtr(), callback
));
398 virtual void Cancel() OVERRIDE
{
399 cancel_requested_
= true;
401 copy_helper_
->Cancel();
405 void RunAfterGetMetadataForSource(
406 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
407 base::PlatformFileError error
,
408 const base::PlatformFileInfo
& file_info
) {
409 if (cancel_requested_
)
410 error
= base::PLATFORM_FILE_ERROR_ABORT
;
412 if (error
!= base::PLATFORM_FILE_OK
) {
417 if (file_info
.is_directory
) {
418 // If not a directory, failed with appropriate error code.
419 callback
.Run(base::PLATFORM_FILE_ERROR_NOT_A_FILE
);
423 // To use FileStreamWriter, we need to ensure the destination file exists.
424 operation_runner_
->CreateFile(
425 dest_url_
, false /* exclusive */,
426 base::Bind(&StreamCopyOrMoveImpl::RunAfterCreateFileForDestination
,
427 weak_factory_
.GetWeakPtr(),
428 callback
, file_info
.last_modified
));
431 void RunAfterCreateFileForDestination(
432 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
433 const base::Time
& last_modified
,
434 base::PlatformFileError error
) {
435 if (cancel_requested_
)
436 error
= base::PLATFORM_FILE_ERROR_ABORT
;
438 if (error
!= base::PLATFORM_FILE_OK
) {
443 DCHECK(!copy_helper_
);
445 new CopyOrMoveOperationDelegate::StreamCopyHelper(
446 reader_
.Pass(), writer_
.Pass(),
448 file_progress_callback_
,
449 base::TimeDelta::FromMilliseconds(
450 kMinProgressCallbackInvocationSpanInMilliseconds
)));
452 base::Bind(&StreamCopyOrMoveImpl::RunAfterStreamCopy
,
453 weak_factory_
.GetWeakPtr(), callback
, last_modified
));
456 void RunAfterStreamCopy(
457 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
458 const base::Time
& last_modified
,
459 base::PlatformFileError error
) {
460 if (cancel_requested_
)
461 error
= base::PLATFORM_FILE_ERROR_ABORT
;
463 if (error
!= base::PLATFORM_FILE_OK
) {
468 if (option_
== FileSystemOperation::OPTION_NONE
) {
469 RunAfterTouchFile(callback
, base::PLATFORM_FILE_OK
);
473 operation_runner_
->TouchFile(
474 dest_url_
, base::Time::Now() /* last_access */, last_modified
,
475 base::Bind(&StreamCopyOrMoveImpl::RunAfterTouchFile
,
476 weak_factory_
.GetWeakPtr(), callback
));
479 void RunAfterTouchFile(
480 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
481 base::PlatformFileError error
) {
482 // Even if TouchFile is failed, just ignore it.
483 if (cancel_requested_
) {
484 callback
.Run(base::PLATFORM_FILE_ERROR_ABORT
);
488 if (operation_type_
== CopyOrMoveOperationDelegate::OPERATION_COPY
) {
489 callback
.Run(base::PLATFORM_FILE_OK
);
493 DCHECK_EQ(CopyOrMoveOperationDelegate::OPERATION_MOVE
, operation_type_
);
495 // Remove the source for finalizing move operation.
496 operation_runner_
->Remove(
497 src_url_
, false /* recursive */,
498 base::Bind(&StreamCopyOrMoveImpl::RunAfterRemoveForMove
,
499 weak_factory_
.GetWeakPtr(), callback
));
502 void RunAfterRemoveForMove(
503 const CopyOrMoveOperationDelegate::StatusCallback
& callback
,
504 base::PlatformFileError error
) {
505 if (cancel_requested_
)
506 error
= base::PLATFORM_FILE_ERROR_ABORT
;
507 if (error
== base::PLATFORM_FILE_ERROR_NOT_FOUND
)
508 error
= base::PLATFORM_FILE_OK
;
512 FileSystemOperationRunner
* operation_runner_
;
513 CopyOrMoveOperationDelegate::OperationType operation_type_
;
514 FileSystemURL src_url_
;
515 FileSystemURL dest_url_
;
516 CopyOrMoveOperationDelegate::CopyOrMoveOption option_
;
517 scoped_ptr
<webkit_blob::FileStreamReader
> reader_
;
518 scoped_ptr
<FileStreamWriter
> writer_
;
519 FileSystemOperation::CopyFileProgressCallback file_progress_callback_
;
520 scoped_ptr
<CopyOrMoveOperationDelegate::StreamCopyHelper
> copy_helper_
;
521 bool cancel_requested_
;
522 base::WeakPtrFactory
<StreamCopyOrMoveImpl
> weak_factory_
;
523 DISALLOW_COPY_AND_ASSIGN(StreamCopyOrMoveImpl
);
528 CopyOrMoveOperationDelegate::StreamCopyHelper::StreamCopyHelper(
529 scoped_ptr
<webkit_blob::FileStreamReader
> reader
,
530 scoped_ptr
<FileStreamWriter
> writer
,
532 const FileSystemOperation::CopyFileProgressCallback
&
533 file_progress_callback
,
534 const base::TimeDelta
& min_progress_callback_invocation_span
)
535 : reader_(reader
.Pass()),
536 writer_(writer
.Pass()),
537 file_progress_callback_(file_progress_callback
),
538 io_buffer_(new net::IOBufferWithSize(buffer_size
)),
539 num_copied_bytes_(0),
540 min_progress_callback_invocation_span_(
541 min_progress_callback_invocation_span
),
542 cancel_requested_(false),
543 weak_factory_(this) {
546 CopyOrMoveOperationDelegate::StreamCopyHelper::~StreamCopyHelper() {
549 void CopyOrMoveOperationDelegate::StreamCopyHelper::Run(
550 const StatusCallback
& callback
) {
551 file_progress_callback_
.Run(0);
552 last_progress_callback_invocation_time_
= base::Time::Now();
556 void CopyOrMoveOperationDelegate::StreamCopyHelper::Cancel() {
557 cancel_requested_
= true;
560 void CopyOrMoveOperationDelegate::StreamCopyHelper::Read(
561 const StatusCallback
& callback
) {
562 int result
= reader_
->Read(
563 io_buffer_
.get(), io_buffer_
->size(),
564 base::Bind(&StreamCopyHelper::DidRead
,
565 weak_factory_
.GetWeakPtr(), callback
));
566 if (result
!= net::ERR_IO_PENDING
)
567 DidRead(callback
, result
);
570 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidRead(
571 const StatusCallback
& callback
, int result
) {
572 if (cancel_requested_
) {
573 callback
.Run(base::PLATFORM_FILE_ERROR_ABORT
);
578 callback
.Run(NetErrorToPlatformFileError(result
));
584 callback
.Run(base::PLATFORM_FILE_OK
);
588 Write(callback
, new net::DrainableIOBuffer(io_buffer_
.get(), result
));
591 void CopyOrMoveOperationDelegate::StreamCopyHelper::Write(
592 const StatusCallback
& callback
,
593 scoped_refptr
<net::DrainableIOBuffer
> buffer
) {
594 DCHECK_GT(buffer
->BytesRemaining(), 0);
596 int result
= writer_
->Write(
597 buffer
.get(), buffer
->BytesRemaining(),
598 base::Bind(&StreamCopyHelper::DidWrite
,
599 weak_factory_
.GetWeakPtr(), callback
, buffer
));
600 if (result
!= net::ERR_IO_PENDING
)
601 DidWrite(callback
, buffer
, result
);
604 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidWrite(
605 const StatusCallback
& callback
,
606 scoped_refptr
<net::DrainableIOBuffer
> buffer
,
608 if (cancel_requested_
) {
609 callback
.Run(base::PLATFORM_FILE_ERROR_ABORT
);
614 callback
.Run(NetErrorToPlatformFileError(result
));
618 buffer
->DidConsume(result
);
619 num_copied_bytes_
+= result
;
621 // Check the elapsed time since last |file_progress_callback_| invocation.
622 base::Time now
= base::Time::Now();
623 if (now
- last_progress_callback_invocation_time_
>=
624 min_progress_callback_invocation_span_
) {
625 file_progress_callback_
.Run(num_copied_bytes_
);
626 last_progress_callback_invocation_time_
= now
;
629 if (buffer
->BytesRemaining() > 0) {
630 Write(callback
, buffer
);
637 CopyOrMoveOperationDelegate::CopyOrMoveOperationDelegate(
638 FileSystemContext
* file_system_context
,
639 const FileSystemURL
& src_root
,
640 const FileSystemURL
& dest_root
,
641 OperationType operation_type
,
642 CopyOrMoveOption option
,
643 const CopyProgressCallback
& progress_callback
,
644 const StatusCallback
& callback
)
645 : RecursiveOperationDelegate(file_system_context
),
647 dest_root_(dest_root
),
648 operation_type_(operation_type
),
650 progress_callback_(progress_callback
),
652 weak_factory_(this) {
653 same_file_system_
= src_root_
.IsInSameFileSystem(dest_root_
);
656 CopyOrMoveOperationDelegate::~CopyOrMoveOperationDelegate() {
657 STLDeleteElements(&running_copy_set_
);
660 void CopyOrMoveOperationDelegate::Run() {
661 // Not supported; this should never be called.
665 void CopyOrMoveOperationDelegate::RunRecursively() {
666 // Perform light-weight checks first.
668 // It is an error to try to copy/move an entry into its child.
669 if (same_file_system_
&& src_root_
.path().IsParent(dest_root_
.path())) {
670 callback_
.Run(base::PLATFORM_FILE_ERROR_INVALID_OPERATION
);
674 // It is an error to copy/move an entry into the same path.
675 if (same_file_system_
&& src_root_
.path() == dest_root_
.path()) {
676 callback_
.Run(base::PLATFORM_FILE_ERROR_EXISTS
);
680 // Start to process the source directory recursively.
681 // TODO(kinuko): This could be too expensive for same_file_system_==true
682 // and operation==MOVE case, probably we can just rename the root directory.
683 // http://crbug.com/172187
684 StartRecursiveOperation(src_root_
, callback_
);
687 void CopyOrMoveOperationDelegate::ProcessFile(
688 const FileSystemURL
& src_url
,
689 const StatusCallback
& callback
) {
690 if (!progress_callback_
.is_null()) {
691 progress_callback_
.Run(
692 FileSystemOperation::BEGIN_COPY_ENTRY
, src_url
, FileSystemURL(), 0);
695 FileSystemURL dest_url
= CreateDestURL(src_url
);
696 CopyOrMoveImpl
* impl
= NULL
;
697 if (same_file_system_
) {
698 impl
= new CopyOrMoveOnSameFileSystemImpl(
699 operation_runner(), operation_type_
, src_url
, dest_url
, option_
,
700 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress
,
701 weak_factory_
.GetWeakPtr(), src_url
));
703 // Cross filesystem case.
704 base::PlatformFileError error
= base::PLATFORM_FILE_ERROR_FAILED
;
705 CopyOrMoveFileValidatorFactory
* validator_factory
=
706 file_system_context()->GetCopyOrMoveFileValidatorFactory(
707 dest_root_
.type(), &error
);
708 if (error
!= base::PLATFORM_FILE_OK
) {
713 if (!validator_factory
) {
714 scoped_ptr
<webkit_blob::FileStreamReader
> reader
=
715 file_system_context()->CreateFileStreamReader(
716 src_url
, 0, base::Time());
717 scoped_ptr
<FileStreamWriter
> writer
=
718 file_system_context()->CreateFileStreamWriter(dest_url
, 0);
719 if (reader
&& writer
) {
720 impl
= new StreamCopyOrMoveImpl(
721 operation_runner(), operation_type_
, src_url
, dest_url
, option_
,
722 reader
.Pass(), writer
.Pass(),
723 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress
,
724 weak_factory_
.GetWeakPtr(), src_url
));
729 impl
= new SnapshotCopyOrMoveImpl(
730 operation_runner(), operation_type_
, src_url
, dest_url
, option_
,
732 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress
,
733 weak_factory_
.GetWeakPtr(), src_url
));
737 // Register the running task.
738 running_copy_set_
.insert(impl
);
739 impl
->Run(base::Bind(
740 &CopyOrMoveOperationDelegate::DidCopyOrMoveFile
,
741 weak_factory_
.GetWeakPtr(), src_url
, dest_url
, callback
, impl
));
744 void CopyOrMoveOperationDelegate::ProcessDirectory(
745 const FileSystemURL
& src_url
,
746 const StatusCallback
& callback
) {
747 if (src_url
== src_root_
) {
748 // The src_root_ looks to be a directory.
749 // Try removing the dest_root_ to see if it exists and/or it is an
751 // We do not invoke |progress_callback_| for source root, because it is
752 // already called in ProcessFile().
753 operation_runner()->RemoveDirectory(
755 base::Bind(&CopyOrMoveOperationDelegate::DidTryRemoveDestRoot
,
756 weak_factory_
.GetWeakPtr(), callback
));
760 if (!progress_callback_
.is_null()) {
761 progress_callback_
.Run(
762 FileSystemOperation::BEGIN_COPY_ENTRY
, src_url
, FileSystemURL(), 0);
765 ProcessDirectoryInternal(src_url
, CreateDestURL(src_url
), callback
);
768 void CopyOrMoveOperationDelegate::PostProcessDirectory(
769 const FileSystemURL
& src_url
,
770 const StatusCallback
& callback
) {
771 if (option_
== FileSystemOperation::OPTION_NONE
) {
772 PostProcessDirectoryAfterTouchFile(
773 src_url
, callback
, base::PLATFORM_FILE_OK
);
777 operation_runner()->GetMetadata(
780 &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterGetMetadata
,
781 weak_factory_
.GetWeakPtr(), src_url
, callback
));
784 void CopyOrMoveOperationDelegate::OnCancel() {
785 // Request to cancel all running Copy/Move file.
786 for (std::set
<CopyOrMoveImpl
*>::iterator iter
= running_copy_set_
.begin();
787 iter
!= running_copy_set_
.end(); ++iter
)
791 void CopyOrMoveOperationDelegate::DidCopyOrMoveFile(
792 const FileSystemURL
& src_url
,
793 const FileSystemURL
& dest_url
,
794 const StatusCallback
& callback
,
795 CopyOrMoveImpl
* impl
,
796 base::PlatformFileError error
) {
797 running_copy_set_
.erase(impl
);
800 if (!progress_callback_
.is_null() && error
== base::PLATFORM_FILE_OK
) {
801 progress_callback_
.Run(
802 FileSystemOperation::END_COPY_ENTRY
, src_url
, dest_url
, 0);
808 void CopyOrMoveOperationDelegate::DidTryRemoveDestRoot(
809 const StatusCallback
& callback
,
810 base::PlatformFileError error
) {
811 if (error
== base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY
) {
812 callback_
.Run(base::PLATFORM_FILE_ERROR_INVALID_OPERATION
);
815 if (error
!= base::PLATFORM_FILE_OK
&&
816 error
!= base::PLATFORM_FILE_ERROR_NOT_FOUND
) {
817 callback_
.Run(error
);
821 ProcessDirectoryInternal(src_root_
, dest_root_
, callback
);
824 void CopyOrMoveOperationDelegate::ProcessDirectoryInternal(
825 const FileSystemURL
& src_url
,
826 const FileSystemURL
& dest_url
,
827 const StatusCallback
& callback
) {
828 // If operation_type == Move we may need to record directories and
829 // restore directory timestamps in the end, though it may have
830 // negative performance impact.
831 // See http://crbug.com/171284 for more details.
832 operation_runner()->CreateDirectory(
833 dest_url
, false /* exclusive */, false /* recursive */,
834 base::Bind(&CopyOrMoveOperationDelegate::DidCreateDirectory
,
835 weak_factory_
.GetWeakPtr(), src_url
, dest_url
, callback
));
838 void CopyOrMoveOperationDelegate::DidCreateDirectory(
839 const FileSystemURL
& src_url
,
840 const FileSystemURL
& dest_url
,
841 const StatusCallback
& callback
,
842 base::PlatformFileError error
) {
843 if (!progress_callback_
.is_null() && error
== base::PLATFORM_FILE_OK
) {
844 progress_callback_
.Run(
845 FileSystemOperation::END_COPY_ENTRY
, src_url
, dest_url
, 0);
851 void CopyOrMoveOperationDelegate::PostProcessDirectoryAfterGetMetadata(
852 const FileSystemURL
& src_url
,
853 const StatusCallback
& callback
,
854 base::PlatformFileError error
,
855 const base::PlatformFileInfo
& file_info
) {
856 if (error
!= base::PLATFORM_FILE_OK
) {
857 // Ignore the error, and run post process which should run after TouchFile.
858 PostProcessDirectoryAfterTouchFile(
859 src_url
, callback
, base::PLATFORM_FILE_OK
);
863 operation_runner()->TouchFile(
864 CreateDestURL(src_url
), base::Time::Now() /* last access */,
865 file_info
.last_modified
,
867 &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterTouchFile
,
868 weak_factory_
.GetWeakPtr(), src_url
, callback
));
871 void CopyOrMoveOperationDelegate::PostProcessDirectoryAfterTouchFile(
872 const FileSystemURL
& src_url
,
873 const StatusCallback
& callback
,
874 base::PlatformFileError error
) {
875 // Even if the TouchFile is failed, just ignore it.
877 if (operation_type_
== OPERATION_COPY
) {
878 callback
.Run(base::PLATFORM_FILE_OK
);
882 DCHECK_EQ(OPERATION_MOVE
, operation_type_
);
884 // All files and subdirectories in the directory should be moved here,
885 // so remove the source directory for finalizing move operation.
886 operation_runner()->Remove(
887 src_url
, false /* recursive */,
888 base::Bind(&CopyOrMoveOperationDelegate::DidRemoveSourceForMove
,
889 weak_factory_
.GetWeakPtr(), callback
));
892 void CopyOrMoveOperationDelegate::DidRemoveSourceForMove(
893 const StatusCallback
& callback
,
894 base::PlatformFileError error
) {
895 if (error
== base::PLATFORM_FILE_ERROR_NOT_FOUND
)
896 error
= base::PLATFORM_FILE_OK
;
900 void CopyOrMoveOperationDelegate::OnCopyFileProgress(
901 const FileSystemURL
& src_url
, int64 size
) {
902 if (!progress_callback_
.is_null()) {
903 progress_callback_
.Run(
904 FileSystemOperation::PROGRESS
, src_url
, FileSystemURL(), size
);
908 FileSystemURL
CopyOrMoveOperationDelegate::CreateDestURL(
909 const FileSystemURL
& src_url
) const {
910 DCHECK_EQ(src_root_
.type(), src_url
.type());
911 DCHECK_EQ(src_root_
.origin(), src_url
.origin());
913 base::FilePath relative
= dest_root_
.virtual_path();
914 src_root_
.virtual_path().AppendRelativePath(src_url
.virtual_path(),
916 return file_system_context()->CreateCrackedFileSystemURL(
918 dest_root_
.mount_type(),
922 } // namespace fileapi