net cleanup: Remove unnecessary namespace prefixes.
[chromium-blink-merge.git] / storage / browser / fileapi / copy_or_move_operation_delegate.cc
blobbf3714f14f087ce8fdf6294153d84812113c9ce0
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/browser/fileapi/recursive_operation_delegate.h"
20 #include "storage/common/fileapi/file_system_util.h"
22 namespace storage {
24 const int64 kFlushIntervalInBytes = 10 << 20; // 10MB.
26 class CopyOrMoveOperationDelegate::CopyOrMoveImpl {
27 public:
28 virtual ~CopyOrMoveImpl() {}
29 virtual void Run(
30 const CopyOrMoveOperationDelegate::StatusCallback& callback) = 0;
31 virtual void Cancel() = 0;
33 protected:
34 CopyOrMoveImpl() {}
36 private:
37 DISALLOW_COPY_AND_ASSIGN(CopyOrMoveImpl);
40 namespace {
42 // Copies a file on a (same) file system. Just delegate the operation to
43 // |operation_runner|.
44 class CopyOrMoveOnSameFileSystemImpl
45 : public CopyOrMoveOperationDelegate::CopyOrMoveImpl {
46 public:
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),
57 src_url_(src_url),
58 dest_url_(dest_url),
59 option_(option),
60 file_progress_callback_(file_progress_callback) {
63 void Run(
64 const CopyOrMoveOperationDelegate::StatusCallback& callback) override {
65 if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_MOVE) {
66 operation_runner_->MoveFileLocal(src_url_, dest_url_, option_, callback);
67 } else {
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.
79 private:
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
92 // copy).
93 class SnapshotCopyOrMoveImpl
94 : public CopyOrMoveOperationDelegate::CopyOrMoveImpl {
95 public:
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),
107 src_url_(src_url),
108 dest_url_(dest_url),
109 option_(option),
110 validator_factory_(validator_factory),
111 file_progress_callback_(file_progress_callback),
112 cancel_requested_(false),
113 weak_factory_(this) {
116 void Run(
117 const CopyOrMoveOperationDelegate::StatusCallback& callback) override {
118 file_progress_callback_.Run(0);
119 operation_runner_->CreateSnapshotFile(
120 src_url_,
121 base::Bind(&SnapshotCopyOrMoveImpl::RunAfterCreateSnapshot,
122 weak_factory_.GetWeakPtr(), callback));
125 void Cancel() override { cancel_requested_ = true; }
127 private:
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) {
138 callback.Run(error);
139 return;
142 // For now we assume CreateSnapshotFile always return a valid local file
143 // path.
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);
150 return;
153 // Run pre write validation.
154 PreWriteValidation(
155 platform_path,
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) {
171 callback.Run(error);
172 return;
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) {
192 callback.Run(error);
193 return;
196 file_progress_callback_.Run(file_info.size);
198 if (option_ == FileSystemOperation::OPTION_NONE) {
199 RunAfterTouchFile(callback, base::File::FILE_OK);
200 return;
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);
217 return;
220 // |validator_| is NULL when the destination filesystem does not do
221 // validation.
222 if (!validator_) {
223 // No validation is needed.
224 RunAfterPostWriteValidation(callback, base::File::FILE_OK);
225 return;
228 PostWriteValidation(
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);
238 return;
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));
247 return;
250 if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_COPY) {
251 callback.Run(base::File::FILE_OK);
252 return;
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;
272 callback.Run(error);
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: "
281 << 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_);
291 validator_.reset(
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(
301 dest_url_,
302 base::Bind(
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) {
317 callback.Run(error);
318 return;
321 DCHECK(validator_);
322 // Note: file_ref passed here to keep the file alive until after
323 // the StartPostWriteValidation operation finishes.
324 validator_->StartPostWriteValidation(
325 platform_path,
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) {
336 callback.Run(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 {
365 public:
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),
380 src_url_(src_url),
381 dest_url_(dest_url),
382 option_(option),
383 reader_(reader.Pass()),
384 writer_(writer.Pass()),
385 file_progress_callback_(file_progress_callback),
386 cancel_requested_(false),
387 weak_factory_(this) {}
389 void Run(
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(
395 src_url_,
396 base::Bind(&StreamCopyOrMoveImpl::RunAfterGetMetadataForSource,
397 weak_factory_.GetWeakPtr(), callback));
400 void Cancel() override {
401 cancel_requested_ = true;
402 if (copy_helper_)
403 copy_helper_->Cancel();
406 private:
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) {
436 callback.Run(error);
437 return;
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);
443 return;
446 // To use FileStreamWriter, we need to ensure the destination file exists.
447 operation_runner_->CreateFile(
448 dest_url_,
449 true /* exclusive */,
450 base::Bind(&StreamCopyOrMoveImpl::RunAfterCreateFileForDestination,
451 weak_factory_.GetWeakPtr(),
452 callback,
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) {
469 callback.Run(error);
470 return;
473 if (error == base::File::FILE_ERROR_EXISTS) {
474 operation_runner_->Truncate(
475 dest_url_,
476 0 /* length */,
477 base::Bind(&StreamCopyOrMoveImpl::RunAfterTruncateForDestination,
478 weak_factory_.GetWeakPtr(),
479 callback,
480 last_modified));
481 return;
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) {
495 callback.Run(error);
496 return;
499 NotifyOnStartUpdate(dest_url_);
500 DCHECK(!copy_helper_);
501 copy_helper_.reset(new CopyOrMoveOperationDelegate::StreamCopyHelper(
502 reader_.Pass(), writer_.Pass(), dest_url_.mount_option().flush_policy(),
503 kReadBufferSize, file_progress_callback_,
504 base::TimeDelta::FromMilliseconds(
505 kMinProgressCallbackInvocationSpanInMilliseconds)));
506 copy_helper_->Run(
507 base::Bind(&StreamCopyOrMoveImpl::RunAfterStreamCopy,
508 weak_factory_.GetWeakPtr(), callback, last_modified));
511 void RunAfterStreamCopy(
512 const CopyOrMoveOperationDelegate::StatusCallback& callback,
513 const base::Time& last_modified,
514 base::File::Error error) {
515 NotifyOnModifyFile(dest_url_);
516 NotifyOnEndUpdate(dest_url_);
517 if (cancel_requested_)
518 error = base::File::FILE_ERROR_ABORT;
520 if (error != base::File::FILE_OK) {
521 callback.Run(error);
522 return;
525 if (option_ == FileSystemOperation::OPTION_NONE) {
526 RunAfterTouchFile(callback, base::File::FILE_OK);
527 return;
530 operation_runner_->TouchFile(
531 dest_url_, base::Time::Now() /* last_access */, last_modified,
532 base::Bind(&StreamCopyOrMoveImpl::RunAfterTouchFile,
533 weak_factory_.GetWeakPtr(), callback));
536 void RunAfterTouchFile(
537 const CopyOrMoveOperationDelegate::StatusCallback& callback,
538 base::File::Error error) {
539 // Even if TouchFile is failed, just ignore it.
540 if (cancel_requested_) {
541 callback.Run(base::File::FILE_ERROR_ABORT);
542 return;
545 if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_COPY) {
546 callback.Run(base::File::FILE_OK);
547 return;
550 DCHECK_EQ(CopyOrMoveOperationDelegate::OPERATION_MOVE, operation_type_);
552 // Remove the source for finalizing move operation.
553 operation_runner_->Remove(
554 src_url_, false /* recursive */,
555 base::Bind(&StreamCopyOrMoveImpl::RunAfterRemoveForMove,
556 weak_factory_.GetWeakPtr(), callback));
559 void RunAfterRemoveForMove(
560 const CopyOrMoveOperationDelegate::StatusCallback& callback,
561 base::File::Error error) {
562 if (cancel_requested_)
563 error = base::File::FILE_ERROR_ABORT;
564 if (error == base::File::FILE_ERROR_NOT_FOUND)
565 error = base::File::FILE_OK;
566 callback.Run(error);
569 FileSystemOperationRunner* operation_runner_;
570 scoped_refptr<FileSystemContext> file_system_context_;
571 CopyOrMoveOperationDelegate::OperationType operation_type_;
572 FileSystemURL src_url_;
573 FileSystemURL dest_url_;
574 CopyOrMoveOperationDelegate::CopyOrMoveOption option_;
575 scoped_ptr<storage::FileStreamReader> reader_;
576 scoped_ptr<FileStreamWriter> writer_;
577 FileSystemOperation::CopyFileProgressCallback file_progress_callback_;
578 scoped_ptr<CopyOrMoveOperationDelegate::StreamCopyHelper> copy_helper_;
579 bool cancel_requested_;
580 base::WeakPtrFactory<StreamCopyOrMoveImpl> weak_factory_;
581 DISALLOW_COPY_AND_ASSIGN(StreamCopyOrMoveImpl);
584 } // namespace
586 CopyOrMoveOperationDelegate::StreamCopyHelper::StreamCopyHelper(
587 scoped_ptr<storage::FileStreamReader> reader,
588 scoped_ptr<FileStreamWriter> writer,
589 storage::FlushPolicy flush_policy,
590 int buffer_size,
591 const FileSystemOperation::CopyFileProgressCallback& file_progress_callback,
592 const base::TimeDelta& min_progress_callback_invocation_span)
593 : reader_(reader.Pass()),
594 writer_(writer.Pass()),
595 flush_policy_(flush_policy),
596 file_progress_callback_(file_progress_callback),
597 io_buffer_(new net::IOBufferWithSize(buffer_size)),
598 num_copied_bytes_(0),
599 previous_flush_offset_(0),
600 min_progress_callback_invocation_span_(
601 min_progress_callback_invocation_span),
602 cancel_requested_(false),
603 weak_factory_(this) {
606 CopyOrMoveOperationDelegate::StreamCopyHelper::~StreamCopyHelper() {
609 void CopyOrMoveOperationDelegate::StreamCopyHelper::Run(
610 const StatusCallback& callback) {
611 file_progress_callback_.Run(0);
612 last_progress_callback_invocation_time_ = base::Time::Now();
613 Read(callback);
616 void CopyOrMoveOperationDelegate::StreamCopyHelper::Cancel() {
617 cancel_requested_ = true;
620 void CopyOrMoveOperationDelegate::StreamCopyHelper::Read(
621 const StatusCallback& callback) {
622 int result = reader_->Read(
623 io_buffer_.get(), io_buffer_->size(),
624 base::Bind(&StreamCopyHelper::DidRead,
625 weak_factory_.GetWeakPtr(), callback));
626 if (result != net::ERR_IO_PENDING)
627 DidRead(callback, result);
630 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidRead(
631 const StatusCallback& callback, int result) {
632 if (cancel_requested_) {
633 callback.Run(base::File::FILE_ERROR_ABORT);
634 return;
637 if (result < 0) {
638 callback.Run(NetErrorToFileError(result));
639 return;
642 if (result == 0) {
643 // Here is the EOF.
644 if (flush_policy_ == storage::FlushPolicy::FLUSH_ON_COMPLETION)
645 Flush(callback, true /* is_eof */);
646 else
647 callback.Run(base::File::FILE_OK);
648 return;
651 Write(callback, new net::DrainableIOBuffer(io_buffer_.get(), result));
654 void CopyOrMoveOperationDelegate::StreamCopyHelper::Write(
655 const StatusCallback& callback,
656 scoped_refptr<net::DrainableIOBuffer> buffer) {
657 DCHECK_GT(buffer->BytesRemaining(), 0);
659 int result = writer_->Write(
660 buffer.get(), buffer->BytesRemaining(),
661 base::Bind(&StreamCopyHelper::DidWrite,
662 weak_factory_.GetWeakPtr(), callback, buffer));
663 if (result != net::ERR_IO_PENDING)
664 DidWrite(callback, buffer, result);
667 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidWrite(
668 const StatusCallback& callback,
669 scoped_refptr<net::DrainableIOBuffer> buffer,
670 int result) {
671 if (cancel_requested_) {
672 callback.Run(base::File::FILE_ERROR_ABORT);
673 return;
676 if (result < 0) {
677 callback.Run(NetErrorToFileError(result));
678 return;
681 buffer->DidConsume(result);
682 num_copied_bytes_ += result;
684 // Check the elapsed time since last |file_progress_callback_| invocation.
685 base::Time now = base::Time::Now();
686 if (now - last_progress_callback_invocation_time_ >=
687 min_progress_callback_invocation_span_) {
688 file_progress_callback_.Run(num_copied_bytes_);
689 last_progress_callback_invocation_time_ = now;
692 if (buffer->BytesRemaining() > 0) {
693 Write(callback, buffer);
694 return;
697 if (flush_policy_ == storage::FlushPolicy::FLUSH_ON_COMPLETION &&
698 (num_copied_bytes_ - previous_flush_offset_) > kFlushIntervalInBytes) {
699 Flush(callback, false /* not is_eof */);
700 } else {
701 Read(callback);
705 void CopyOrMoveOperationDelegate::StreamCopyHelper::Flush(
706 const StatusCallback& callback, bool is_eof) {
707 int result = writer_->Flush(
708 base::Bind(&StreamCopyHelper::DidFlush,
709 weak_factory_.GetWeakPtr(), callback, is_eof));
710 if (result != net::ERR_IO_PENDING)
711 DidFlush(callback, is_eof, result);
714 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidFlush(
715 const StatusCallback& callback, bool is_eof, int result) {
716 if (cancel_requested_) {
717 callback.Run(base::File::FILE_ERROR_ABORT);
718 return;
721 previous_flush_offset_ = num_copied_bytes_;
722 if (is_eof)
723 callback.Run(NetErrorToFileError(result));
724 else
725 Read(callback);
728 CopyOrMoveOperationDelegate::CopyOrMoveOperationDelegate(
729 FileSystemContext* file_system_context,
730 const FileSystemURL& src_root,
731 const FileSystemURL& dest_root,
732 OperationType operation_type,
733 CopyOrMoveOption option,
734 const CopyProgressCallback& progress_callback,
735 const StatusCallback& callback)
736 : RecursiveOperationDelegate(file_system_context),
737 src_root_(src_root),
738 dest_root_(dest_root),
739 operation_type_(operation_type),
740 option_(option),
741 progress_callback_(progress_callback),
742 callback_(callback),
743 weak_factory_(this) {
744 same_file_system_ = src_root_.IsInSameFileSystem(dest_root_);
747 CopyOrMoveOperationDelegate::~CopyOrMoveOperationDelegate() {
748 STLDeleteElements(&running_copy_set_);
751 void CopyOrMoveOperationDelegate::Run() {
752 // Not supported; this should never be called.
753 NOTREACHED();
756 void CopyOrMoveOperationDelegate::RunRecursively() {
757 // Perform light-weight checks first.
759 // It is an error to try to copy/move an entry into its child.
760 if (same_file_system_ && src_root_.path().IsParent(dest_root_.path())) {
761 callback_.Run(base::File::FILE_ERROR_INVALID_OPERATION);
762 return;
765 if (same_file_system_ && src_root_.path() == dest_root_.path()) {
766 // In JS API this should return error, but we return success because Pepper
767 // wants to return success and we have a code path that returns error in
768 // Blink for JS (http://crbug.com/329517).
769 callback_.Run(base::File::FILE_OK);
770 return;
773 // Start to process the source directory recursively.
774 // TODO(kinuko): This could be too expensive for same_file_system_==true
775 // and operation==MOVE case, probably we can just rename the root directory.
776 // http://crbug.com/172187
777 StartRecursiveOperation(src_root_, callback_);
780 void CopyOrMoveOperationDelegate::ProcessFile(
781 const FileSystemURL& src_url,
782 const StatusCallback& callback) {
783 if (!progress_callback_.is_null()) {
784 progress_callback_.Run(
785 FileSystemOperation::BEGIN_COPY_ENTRY, src_url, FileSystemURL(), 0);
788 FileSystemURL dest_url = CreateDestURL(src_url);
789 CopyOrMoveImpl* impl = NULL;
790 if (same_file_system_ &&
791 (file_system_context()
792 ->GetFileSystemBackend(src_url.type())
793 ->HasInplaceCopyImplementation(src_url.type()) ||
794 operation_type_ == OPERATION_MOVE)) {
795 impl = new CopyOrMoveOnSameFileSystemImpl(
796 operation_runner(), operation_type_, src_url, dest_url, option_,
797 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress,
798 weak_factory_.GetWeakPtr(), src_url));
799 } else {
800 // Cross filesystem case.
801 base::File::Error error = base::File::FILE_ERROR_FAILED;
802 CopyOrMoveFileValidatorFactory* validator_factory =
803 file_system_context()->GetCopyOrMoveFileValidatorFactory(
804 dest_root_.type(), &error);
805 if (error != base::File::FILE_OK) {
806 callback.Run(error);
807 return;
810 if (!validator_factory) {
811 scoped_ptr<storage::FileStreamReader> reader =
812 file_system_context()->CreateFileStreamReader(
813 src_url, 0 /* offset */, storage::kMaximumLength, base::Time());
814 scoped_ptr<FileStreamWriter> writer =
815 file_system_context()->CreateFileStreamWriter(dest_url, 0);
816 if (reader && writer) {
817 impl = new StreamCopyOrMoveImpl(
818 operation_runner(),
819 file_system_context(),
820 operation_type_,
821 src_url,
822 dest_url,
823 option_,
824 reader.Pass(),
825 writer.Pass(),
826 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress,
827 weak_factory_.GetWeakPtr(),
828 src_url));
832 if (!impl) {
833 impl = new SnapshotCopyOrMoveImpl(
834 operation_runner(), operation_type_, src_url, dest_url, option_,
835 validator_factory,
836 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress,
837 weak_factory_.GetWeakPtr(), src_url));
841 // Register the running task.
842 running_copy_set_.insert(impl);
843 impl->Run(base::Bind(
844 &CopyOrMoveOperationDelegate::DidCopyOrMoveFile,
845 weak_factory_.GetWeakPtr(), src_url, dest_url, callback, impl));
848 void CopyOrMoveOperationDelegate::ProcessDirectory(
849 const FileSystemURL& src_url,
850 const StatusCallback& callback) {
851 if (src_url == src_root_) {
852 // The src_root_ looks to be a directory.
853 // Try removing the dest_root_ to see if it exists and/or it is an
854 // empty directory.
855 // We do not invoke |progress_callback_| for source root, because it is
856 // already called in ProcessFile().
857 operation_runner()->RemoveDirectory(
858 dest_root_,
859 base::Bind(&CopyOrMoveOperationDelegate::DidTryRemoveDestRoot,
860 weak_factory_.GetWeakPtr(), callback));
861 return;
864 if (!progress_callback_.is_null()) {
865 progress_callback_.Run(
866 FileSystemOperation::BEGIN_COPY_ENTRY, src_url, FileSystemURL(), 0);
869 ProcessDirectoryInternal(src_url, CreateDestURL(src_url), callback);
872 void CopyOrMoveOperationDelegate::PostProcessDirectory(
873 const FileSystemURL& src_url,
874 const StatusCallback& callback) {
875 if (option_ == FileSystemOperation::OPTION_NONE) {
876 PostProcessDirectoryAfterTouchFile(
877 src_url, callback, base::File::FILE_OK);
878 return;
881 operation_runner()->GetMetadata(
882 src_url,
883 base::Bind(
884 &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterGetMetadata,
885 weak_factory_.GetWeakPtr(), src_url, callback));
888 void CopyOrMoveOperationDelegate::OnCancel() {
889 // Request to cancel all running Copy/Move file.
890 for (std::set<CopyOrMoveImpl*>::iterator iter = running_copy_set_.begin();
891 iter != running_copy_set_.end(); ++iter)
892 (*iter)->Cancel();
895 void CopyOrMoveOperationDelegate::DidCopyOrMoveFile(
896 const FileSystemURL& src_url,
897 const FileSystemURL& dest_url,
898 const StatusCallback& callback,
899 CopyOrMoveImpl* impl,
900 base::File::Error error) {
901 running_copy_set_.erase(impl);
902 delete impl;
904 if (!progress_callback_.is_null() && error == base::File::FILE_OK) {
905 progress_callback_.Run(
906 FileSystemOperation::END_COPY_ENTRY, src_url, dest_url, 0);
909 callback.Run(error);
912 void CopyOrMoveOperationDelegate::DidTryRemoveDestRoot(
913 const StatusCallback& callback,
914 base::File::Error error) {
915 if (error == base::File::FILE_ERROR_NOT_A_DIRECTORY) {
916 callback_.Run(base::File::FILE_ERROR_INVALID_OPERATION);
917 return;
919 if (error != base::File::FILE_OK &&
920 error != base::File::FILE_ERROR_NOT_FOUND) {
921 callback_.Run(error);
922 return;
925 ProcessDirectoryInternal(src_root_, dest_root_, callback);
928 void CopyOrMoveOperationDelegate::ProcessDirectoryInternal(
929 const FileSystemURL& src_url,
930 const FileSystemURL& dest_url,
931 const StatusCallback& callback) {
932 // If operation_type == Move we may need to record directories and
933 // restore directory timestamps in the end, though it may have
934 // negative performance impact.
935 // See http://crbug.com/171284 for more details.
936 operation_runner()->CreateDirectory(
937 dest_url, false /* exclusive */, false /* recursive */,
938 base::Bind(&CopyOrMoveOperationDelegate::DidCreateDirectory,
939 weak_factory_.GetWeakPtr(), src_url, dest_url, callback));
942 void CopyOrMoveOperationDelegate::DidCreateDirectory(
943 const FileSystemURL& src_url,
944 const FileSystemURL& dest_url,
945 const StatusCallback& callback,
946 base::File::Error error) {
947 if (!progress_callback_.is_null() && error == base::File::FILE_OK) {
948 progress_callback_.Run(
949 FileSystemOperation::END_COPY_ENTRY, src_url, dest_url, 0);
952 callback.Run(error);
955 void CopyOrMoveOperationDelegate::PostProcessDirectoryAfterGetMetadata(
956 const FileSystemURL& src_url,
957 const StatusCallback& callback,
958 base::File::Error error,
959 const base::File::Info& file_info) {
960 if (error != base::File::FILE_OK) {
961 // Ignore the error, and run post process which should run after TouchFile.
962 PostProcessDirectoryAfterTouchFile(
963 src_url, callback, base::File::FILE_OK);
964 return;
967 operation_runner()->TouchFile(
968 CreateDestURL(src_url), base::Time::Now() /* last access */,
969 file_info.last_modified,
970 base::Bind(
971 &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterTouchFile,
972 weak_factory_.GetWeakPtr(), src_url, callback));
975 void CopyOrMoveOperationDelegate::PostProcessDirectoryAfterTouchFile(
976 const FileSystemURL& src_url,
977 const StatusCallback& callback,
978 base::File::Error error) {
979 // Even if the TouchFile is failed, just ignore it.
981 if (operation_type_ == OPERATION_COPY) {
982 callback.Run(base::File::FILE_OK);
983 return;
986 DCHECK_EQ(OPERATION_MOVE, operation_type_);
988 // All files and subdirectories in the directory should be moved here,
989 // so remove the source directory for finalizing move operation.
990 operation_runner()->Remove(
991 src_url, false /* recursive */,
992 base::Bind(&CopyOrMoveOperationDelegate::DidRemoveSourceForMove,
993 weak_factory_.GetWeakPtr(), callback));
996 void CopyOrMoveOperationDelegate::DidRemoveSourceForMove(
997 const StatusCallback& callback,
998 base::File::Error error) {
999 if (error == base::File::FILE_ERROR_NOT_FOUND)
1000 error = base::File::FILE_OK;
1001 callback.Run(error);
1004 void CopyOrMoveOperationDelegate::OnCopyFileProgress(
1005 const FileSystemURL& src_url, int64 size) {
1006 if (!progress_callback_.is_null()) {
1007 progress_callback_.Run(
1008 FileSystemOperation::PROGRESS, src_url, FileSystemURL(), size);
1012 FileSystemURL CopyOrMoveOperationDelegate::CreateDestURL(
1013 const FileSystemURL& src_url) const {
1014 DCHECK_EQ(src_root_.type(), src_url.type());
1015 DCHECK_EQ(src_root_.origin(), src_url.origin());
1017 base::FilePath relative = dest_root_.virtual_path();
1018 src_root_.virtual_path().AppendRelativePath(src_url.virtual_path(),
1019 &relative);
1020 return file_system_context()->CreateCrackedFileSystemURL(
1021 dest_root_.origin(),
1022 dest_root_.mount_type(),
1023 relative);
1026 } // namespace storage