Include all dupe types (event when value is zero) in scan stats.
[chromium-blink-merge.git] / storage / browser / fileapi / copy_or_move_operation_delegate.cc
blob1cb92f5a838d371443aa749598db26de68449003
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"
7 #include "base/bind.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"
21 namespace storage {
23 const int64 kFlushIntervalInBytes = 10 << 20; // 10MB.
25 class CopyOrMoveOperationDelegate::CopyOrMoveImpl {
26 public:
27 virtual ~CopyOrMoveImpl() {}
28 virtual void Run(
29 const CopyOrMoveOperationDelegate::StatusCallback& callback) = 0;
30 virtual void Cancel() = 0;
32 protected:
33 CopyOrMoveImpl() {}
35 private:
36 DISALLOW_COPY_AND_ASSIGN(CopyOrMoveImpl);
39 namespace {
41 // Copies a file on a (same) file system. Just delegate the operation to
42 // |operation_runner|.
43 class CopyOrMoveOnSameFileSystemImpl
44 : public CopyOrMoveOperationDelegate::CopyOrMoveImpl {
45 public:
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),
56 src_url_(src_url),
57 dest_url_(dest_url),
58 option_(option),
59 file_progress_callback_(file_progress_callback) {
62 void Run(
63 const CopyOrMoveOperationDelegate::StatusCallback& callback) override {
64 if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_MOVE) {
65 operation_runner_->MoveFileLocal(src_url_, dest_url_, option_, callback);
66 } else {
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.
78 private:
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
91 // copy).
92 class SnapshotCopyOrMoveImpl
93 : public CopyOrMoveOperationDelegate::CopyOrMoveImpl {
94 public:
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),
106 src_url_(src_url),
107 dest_url_(dest_url),
108 option_(option),
109 validator_factory_(validator_factory),
110 file_progress_callback_(file_progress_callback),
111 cancel_requested_(false),
112 weak_factory_(this) {
115 void Run(
116 const CopyOrMoveOperationDelegate::StatusCallback& callback) override {
117 file_progress_callback_.Run(0);
118 operation_runner_->CreateSnapshotFile(
119 src_url_,
120 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterCreateSnapshot,
121 weak_factory_.GetWeakPtr(), callback));
124 void Cancel() override { cancel_requested_ = true; }
126 private:
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) {
137 callback.Run(error);
138 return;
141 // For now we assume CreateSnapshotFile always return a valid local file
142 // path.
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);
149 return;
152 // Run pre write validation.
153 PreWriteValidation(
154 platform_path,
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) {
170 callback.Run(error);
171 return;
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) {
191 callback.Run(error);
192 return;
195 file_progress_callback_.Run(file_info.size);
197 if (option_ == FileSystemOperation::OPTION_NONE) {
198 RunAfterTouchFile(callback, base::File::FILE_OK);
199 return;
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);
216 return;
219 // |validator_| is NULL when the destination filesystem does not do
220 // validation.
221 if (!validator_) {
222 // No validation is needed.
223 RunAfterPostWriteValidation(callback, base::File::FILE_OK);
224 return;
227 PostWriteValidation(
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);
237 return;
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));
246 return;
249 if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_COPY) {
250 callback.Run(base::File::FILE_OK);
251 return;
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;
271 callback.Run(error);
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: "
280 << 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_);
290 validator_.reset(
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(
300 dest_url_,
301 base::Bind(
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) {
316 callback.Run(error);
317 return;
320 DCHECK(validator_);
321 // Note: file_ref passed here to keep the file alive until after
322 // the StartPostWriteValidation operation finishes.
323 validator_->StartPostWriteValidation(
324 platform_path,
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) {
335 callback.Run(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 {
364 public:
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),
379 src_url_(src_url),
380 dest_url_(dest_url),
381 option_(option),
382 reader_(reader.Pass()),
383 writer_(writer.Pass()),
384 file_progress_callback_(file_progress_callback),
385 cancel_requested_(false),
386 weak_factory_(this) {}
388 void Run(
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(
394 src_url_,
395 base::Bind(&StreamCopyOrMoveImpl::RunAfterGetMetadataForSource,
396 weak_factory_.GetWeakPtr(), callback));
399 void Cancel() override {
400 cancel_requested_ = true;
401 if (copy_helper_)
402 copy_helper_->Cancel();
405 private:
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) {
435 callback.Run(error);
436 return;
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);
442 return;
445 // To use FileStreamWriter, we need to ensure the destination file exists.
446 operation_runner_->CreateFile(
447 dest_url_,
448 true /* exclusive */,
449 base::Bind(&StreamCopyOrMoveImpl::RunAfterCreateFileForDestination,
450 weak_factory_.GetWeakPtr(),
451 callback,
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) {
468 callback.Run(error);
469 return;
472 if (error == base::File::FILE_ERROR_EXISTS) {
473 operation_runner_->Truncate(
474 dest_url_,
475 0 /* length */,
476 base::Bind(&StreamCopyOrMoveImpl::RunAfterTruncateForDestination,
477 weak_factory_.GetWeakPtr(),
478 callback,
479 last_modified));
480 return;
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) {
494 callback.Run(error);
495 return;
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)));
505 copy_helper_->Run(
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) {
520 callback.Run(error);
521 return;
524 if (option_ == FileSystemOperation::OPTION_NONE) {
525 RunAfterTouchFile(callback, base::File::FILE_OK);
526 return;
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);
541 return;
544 if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_COPY) {
545 callback.Run(base::File::FILE_OK);
546 return;
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;
565 callback.Run(error);
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);
583 } // namespace
585 CopyOrMoveOperationDelegate::StreamCopyHelper::StreamCopyHelper(
586 scoped_ptr<storage::FileStreamReader> reader,
587 scoped_ptr<FileStreamWriter> writer,
588 storage::FlushPolicy flush_policy,
589 int buffer_size,
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();
612 Read(callback);
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);
633 return;
636 if (result < 0) {
637 callback.Run(NetErrorToFileError(result));
638 return;
641 if (result == 0) {
642 // Here is the EOF.
643 if (flush_policy_ == storage::FlushPolicy::FLUSH_ON_COMPLETION)
644 Flush(callback, true /* is_eof */);
645 else
646 callback.Run(base::File::FILE_OK);
647 return;
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,
669 int result) {
670 if (cancel_requested_) {
671 callback.Run(base::File::FILE_ERROR_ABORT);
672 return;
675 if (result < 0) {
676 callback.Run(NetErrorToFileError(result));
677 return;
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);
693 return;
696 if (flush_policy_ == storage::FlushPolicy::FLUSH_ON_COMPLETION &&
697 (num_copied_bytes_ - previous_flush_offset_) > kFlushIntervalInBytes) {
698 Flush(callback, false /* not is_eof */);
699 } else {
700 Read(callback);
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);
717 return;
720 previous_flush_offset_ = num_copied_bytes_;
721 if (is_eof)
722 callback.Run(NetErrorToFileError(result));
723 else
724 Read(callback);
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 const CopyProgressCallback& progress_callback,
734 const StatusCallback& callback)
735 : RecursiveOperationDelegate(file_system_context),
736 src_root_(src_root),
737 dest_root_(dest_root),
738 operation_type_(operation_type),
739 option_(option),
740 progress_callback_(progress_callback),
741 callback_(callback),
742 weak_factory_(this) {
743 same_file_system_ = src_root_.IsInSameFileSystem(dest_root_);
746 CopyOrMoveOperationDelegate::~CopyOrMoveOperationDelegate() {
747 STLDeleteElements(&running_copy_set_);
750 void CopyOrMoveOperationDelegate::Run() {
751 // Not supported; this should never be called.
752 NOTREACHED();
755 void CopyOrMoveOperationDelegate::RunRecursively() {
756 // Perform light-weight checks first.
758 // It is an error to try to copy/move an entry into its child.
759 if (same_file_system_ && src_root_.path().IsParent(dest_root_.path())) {
760 callback_.Run(base::File::FILE_ERROR_INVALID_OPERATION);
761 return;
764 if (same_file_system_ && src_root_.path() == dest_root_.path()) {
765 // In JS API this should return error, but we return success because Pepper
766 // wants to return success and we have a code path that returns error in
767 // Blink for JS (http://crbug.com/329517).
768 callback_.Run(base::File::FILE_OK);
769 return;
772 // Start to process the source directory recursively.
773 // TODO(kinuko): This could be too expensive for same_file_system_==true
774 // and operation==MOVE case, probably we can just rename the root directory.
775 // http://crbug.com/172187
776 StartRecursiveOperation(src_root_, callback_);
779 void CopyOrMoveOperationDelegate::ProcessFile(
780 const FileSystemURL& src_url,
781 const StatusCallback& callback) {
782 if (!progress_callback_.is_null()) {
783 progress_callback_.Run(
784 FileSystemOperation::BEGIN_COPY_ENTRY, src_url, FileSystemURL(), 0);
787 FileSystemURL dest_url = CreateDestURL(src_url);
788 CopyOrMoveImpl* impl = NULL;
789 if (same_file_system_ &&
790 (file_system_context()
791 ->GetFileSystemBackend(src_url.type())
792 ->HasInplaceCopyImplementation(src_url.type()) ||
793 operation_type_ == OPERATION_MOVE)) {
794 impl = new CopyOrMoveOnSameFileSystemImpl(
795 operation_runner(), operation_type_, src_url, dest_url, option_,
796 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress,
797 weak_factory_.GetWeakPtr(), src_url));
798 } else {
799 // Cross filesystem case.
800 base::File::Error error = base::File::FILE_ERROR_FAILED;
801 CopyOrMoveFileValidatorFactory* validator_factory =
802 file_system_context()->GetCopyOrMoveFileValidatorFactory(
803 dest_root_.type(), &error);
804 if (error != base::File::FILE_OK) {
805 callback.Run(error);
806 return;
809 if (!validator_factory) {
810 scoped_ptr<storage::FileStreamReader> reader =
811 file_system_context()->CreateFileStreamReader(
812 src_url, 0 /* offset */, storage::kMaximumLength, base::Time());
813 scoped_ptr<FileStreamWriter> writer =
814 file_system_context()->CreateFileStreamWriter(dest_url, 0);
815 if (reader && writer) {
816 impl = new StreamCopyOrMoveImpl(
817 operation_runner(),
818 file_system_context(),
819 operation_type_,
820 src_url,
821 dest_url,
822 option_,
823 reader.Pass(),
824 writer.Pass(),
825 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress,
826 weak_factory_.GetWeakPtr(),
827 src_url));
831 if (!impl) {
832 impl = new SnapshotCopyOrMoveImpl(
833 operation_runner(), operation_type_, src_url, dest_url, option_,
834 validator_factory,
835 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress,
836 weak_factory_.GetWeakPtr(), src_url));
840 // Register the running task.
841 running_copy_set_.insert(impl);
842 impl->Run(base::Bind(
843 &CopyOrMoveOperationDelegate::DidCopyOrMoveFile,
844 weak_factory_.GetWeakPtr(), src_url, dest_url, callback, impl));
847 void CopyOrMoveOperationDelegate::ProcessDirectory(
848 const FileSystemURL& src_url,
849 const StatusCallback& callback) {
850 if (src_url == src_root_) {
851 // The src_root_ looks to be a directory.
852 // Try removing the dest_root_ to see if it exists and/or it is an
853 // empty directory.
854 // We do not invoke |progress_callback_| for source root, because it is
855 // already called in ProcessFile().
856 operation_runner()->RemoveDirectory(
857 dest_root_,
858 base::Bind(&CopyOrMoveOperationDelegate::DidTryRemoveDestRoot,
859 weak_factory_.GetWeakPtr(), callback));
860 return;
863 if (!progress_callback_.is_null()) {
864 progress_callback_.Run(
865 FileSystemOperation::BEGIN_COPY_ENTRY, src_url, FileSystemURL(), 0);
868 ProcessDirectoryInternal(src_url, CreateDestURL(src_url), callback);
871 void CopyOrMoveOperationDelegate::PostProcessDirectory(
872 const FileSystemURL& src_url,
873 const StatusCallback& callback) {
874 if (option_ == FileSystemOperation::OPTION_NONE) {
875 PostProcessDirectoryAfterTouchFile(
876 src_url, callback, base::File::FILE_OK);
877 return;
880 operation_runner()->GetMetadata(
881 src_url,
882 base::Bind(
883 &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterGetMetadata,
884 weak_factory_.GetWeakPtr(), src_url, callback));
887 void CopyOrMoveOperationDelegate::OnCancel() {
888 // Request to cancel all running Copy/Move file.
889 for (std::set<CopyOrMoveImpl*>::iterator iter = running_copy_set_.begin();
890 iter != running_copy_set_.end(); ++iter)
891 (*iter)->Cancel();
894 void CopyOrMoveOperationDelegate::DidCopyOrMoveFile(
895 const FileSystemURL& src_url,
896 const FileSystemURL& dest_url,
897 const StatusCallback& callback,
898 CopyOrMoveImpl* impl,
899 base::File::Error error) {
900 running_copy_set_.erase(impl);
901 delete impl;
903 if (!progress_callback_.is_null() && error == base::File::FILE_OK) {
904 progress_callback_.Run(
905 FileSystemOperation::END_COPY_ENTRY, src_url, dest_url, 0);
908 callback.Run(error);
911 void CopyOrMoveOperationDelegate::DidTryRemoveDestRoot(
912 const StatusCallback& callback,
913 base::File::Error error) {
914 if (error == base::File::FILE_ERROR_NOT_A_DIRECTORY) {
915 callback_.Run(base::File::FILE_ERROR_INVALID_OPERATION);
916 return;
918 if (error != base::File::FILE_OK &&
919 error != base::File::FILE_ERROR_NOT_FOUND) {
920 callback_.Run(error);
921 return;
924 ProcessDirectoryInternal(src_root_, dest_root_, callback);
927 void CopyOrMoveOperationDelegate::ProcessDirectoryInternal(
928 const FileSystemURL& src_url,
929 const FileSystemURL& dest_url,
930 const StatusCallback& callback) {
931 // If operation_type == Move we may need to record directories and
932 // restore directory timestamps in the end, though it may have
933 // negative performance impact.
934 // See http://crbug.com/171284 for more details.
935 operation_runner()->CreateDirectory(
936 dest_url, false /* exclusive */, false /* recursive */,
937 base::Bind(&CopyOrMoveOperationDelegate::DidCreateDirectory,
938 weak_factory_.GetWeakPtr(), src_url, dest_url, callback));
941 void CopyOrMoveOperationDelegate::DidCreateDirectory(
942 const FileSystemURL& src_url,
943 const FileSystemURL& dest_url,
944 const StatusCallback& callback,
945 base::File::Error error) {
946 if (!progress_callback_.is_null() && error == base::File::FILE_OK) {
947 progress_callback_.Run(
948 FileSystemOperation::END_COPY_ENTRY, src_url, dest_url, 0);
951 callback.Run(error);
954 void CopyOrMoveOperationDelegate::PostProcessDirectoryAfterGetMetadata(
955 const FileSystemURL& src_url,
956 const StatusCallback& callback,
957 base::File::Error error,
958 const base::File::Info& file_info) {
959 if (error != base::File::FILE_OK) {
960 // Ignore the error, and run post process which should run after TouchFile.
961 PostProcessDirectoryAfterTouchFile(
962 src_url, callback, base::File::FILE_OK);
963 return;
966 operation_runner()->TouchFile(
967 CreateDestURL(src_url), base::Time::Now() /* last access */,
968 file_info.last_modified,
969 base::Bind(
970 &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterTouchFile,
971 weak_factory_.GetWeakPtr(), src_url, callback));
974 void CopyOrMoveOperationDelegate::PostProcessDirectoryAfterTouchFile(
975 const FileSystemURL& src_url,
976 const StatusCallback& callback,
977 base::File::Error error) {
978 // Even if the TouchFile is failed, just ignore it.
980 if (operation_type_ == OPERATION_COPY) {
981 callback.Run(base::File::FILE_OK);
982 return;
985 DCHECK_EQ(OPERATION_MOVE, operation_type_);
987 // All files and subdirectories in the directory should be moved here,
988 // so remove the source directory for finalizing move operation.
989 operation_runner()->Remove(
990 src_url, false /* recursive */,
991 base::Bind(&CopyOrMoveOperationDelegate::DidRemoveSourceForMove,
992 weak_factory_.GetWeakPtr(), callback));
995 void CopyOrMoveOperationDelegate::DidRemoveSourceForMove(
996 const StatusCallback& callback,
997 base::File::Error error) {
998 if (error == base::File::FILE_ERROR_NOT_FOUND)
999 error = base::File::FILE_OK;
1000 callback.Run(error);
1003 void CopyOrMoveOperationDelegate::OnCopyFileProgress(
1004 const FileSystemURL& src_url, int64 size) {
1005 if (!progress_callback_.is_null()) {
1006 progress_callback_.Run(
1007 FileSystemOperation::PROGRESS, src_url, FileSystemURL(), size);
1011 FileSystemURL CopyOrMoveOperationDelegate::CreateDestURL(
1012 const FileSystemURL& src_url) const {
1013 DCHECK_EQ(src_root_.type(), src_url.type());
1014 DCHECK_EQ(src_root_.origin(), src_url.origin());
1016 base::FilePath relative = dest_root_.virtual_path();
1017 src_root_.virtual_path().AppendRelativePath(src_url.virtual_path(),
1018 &relative);
1019 return file_system_context()->CreateCrackedFileSystemURL(
1020 dest_root_.origin(),
1021 dest_root_.mount_type(),
1022 relative);
1025 } // namespace storage