mesa gn build: suppress -Wstring-conversion warnings
[chromium-blink-merge.git] / storage / browser / fileapi / copy_or_move_operation_delegate.cc
blob5b1d7d2238597f81b7830647e6c8f8dc151e0790
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/file_stream_reader.h"
12 #include "storage/browser/fileapi/copy_or_move_file_validator.h"
13 #include "storage/browser/fileapi/file_observers.h"
14 #include "storage/browser/fileapi/file_stream_writer.h"
15 #include "storage/browser/fileapi/file_system_context.h"
16 #include "storage/browser/fileapi/file_system_operation_runner.h"
17 #include "storage/browser/fileapi/file_system_url.h"
18 #include "storage/browser/fileapi/recursive_operation_delegate.h"
19 #include "storage/common/blob/shareable_file_reference.h"
20 #include "storage/common/fileapi/file_system_util.h"
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 const bool need_flush = dest_url_.mount_option().copy_sync_option() ==
500 storage::COPY_SYNC_OPTION_SYNC;
502 NotifyOnStartUpdate(dest_url_);
503 DCHECK(!copy_helper_);
504 copy_helper_.reset(
505 new CopyOrMoveOperationDelegate::StreamCopyHelper(
506 reader_.Pass(), writer_.Pass(),
507 need_flush,
508 kReadBufferSize,
509 file_progress_callback_,
510 base::TimeDelta::FromMilliseconds(
511 kMinProgressCallbackInvocationSpanInMilliseconds)));
512 copy_helper_->Run(
513 base::Bind(&StreamCopyOrMoveImpl::RunAfterStreamCopy,
514 weak_factory_.GetWeakPtr(), callback, last_modified));
517 void RunAfterStreamCopy(
518 const CopyOrMoveOperationDelegate::StatusCallback& callback,
519 const base::Time& last_modified,
520 base::File::Error error) {
521 NotifyOnModifyFile(dest_url_);
522 NotifyOnEndUpdate(dest_url_);
523 if (cancel_requested_)
524 error = base::File::FILE_ERROR_ABORT;
526 if (error != base::File::FILE_OK) {
527 callback.Run(error);
528 return;
531 if (option_ == FileSystemOperation::OPTION_NONE) {
532 RunAfterTouchFile(callback, base::File::FILE_OK);
533 return;
536 operation_runner_->TouchFile(
537 dest_url_, base::Time::Now() /* last_access */, last_modified,
538 base::Bind(&StreamCopyOrMoveImpl::RunAfterTouchFile,
539 weak_factory_.GetWeakPtr(), callback));
542 void RunAfterTouchFile(
543 const CopyOrMoveOperationDelegate::StatusCallback& callback,
544 base::File::Error error) {
545 // Even if TouchFile is failed, just ignore it.
546 if (cancel_requested_) {
547 callback.Run(base::File::FILE_ERROR_ABORT);
548 return;
551 if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_COPY) {
552 callback.Run(base::File::FILE_OK);
553 return;
556 DCHECK_EQ(CopyOrMoveOperationDelegate::OPERATION_MOVE, operation_type_);
558 // Remove the source for finalizing move operation.
559 operation_runner_->Remove(
560 src_url_, false /* recursive */,
561 base::Bind(&StreamCopyOrMoveImpl::RunAfterRemoveForMove,
562 weak_factory_.GetWeakPtr(), callback));
565 void RunAfterRemoveForMove(
566 const CopyOrMoveOperationDelegate::StatusCallback& callback,
567 base::File::Error error) {
568 if (cancel_requested_)
569 error = base::File::FILE_ERROR_ABORT;
570 if (error == base::File::FILE_ERROR_NOT_FOUND)
571 error = base::File::FILE_OK;
572 callback.Run(error);
575 FileSystemOperationRunner* operation_runner_;
576 scoped_refptr<FileSystemContext> file_system_context_;
577 CopyOrMoveOperationDelegate::OperationType operation_type_;
578 FileSystemURL src_url_;
579 FileSystemURL dest_url_;
580 CopyOrMoveOperationDelegate::CopyOrMoveOption option_;
581 scoped_ptr<storage::FileStreamReader> reader_;
582 scoped_ptr<FileStreamWriter> writer_;
583 FileSystemOperation::CopyFileProgressCallback file_progress_callback_;
584 scoped_ptr<CopyOrMoveOperationDelegate::StreamCopyHelper> copy_helper_;
585 bool cancel_requested_;
586 base::WeakPtrFactory<StreamCopyOrMoveImpl> weak_factory_;
587 DISALLOW_COPY_AND_ASSIGN(StreamCopyOrMoveImpl);
590 } // namespace
592 CopyOrMoveOperationDelegate::StreamCopyHelper::StreamCopyHelper(
593 scoped_ptr<storage::FileStreamReader> reader,
594 scoped_ptr<FileStreamWriter> writer,
595 bool need_flush,
596 int buffer_size,
597 const FileSystemOperation::CopyFileProgressCallback& file_progress_callback,
598 const base::TimeDelta& min_progress_callback_invocation_span)
599 : reader_(reader.Pass()),
600 writer_(writer.Pass()),
601 need_flush_(need_flush),
602 file_progress_callback_(file_progress_callback),
603 io_buffer_(new net::IOBufferWithSize(buffer_size)),
604 num_copied_bytes_(0),
605 previous_flush_offset_(0),
606 min_progress_callback_invocation_span_(
607 min_progress_callback_invocation_span),
608 cancel_requested_(false),
609 weak_factory_(this) {
612 CopyOrMoveOperationDelegate::StreamCopyHelper::~StreamCopyHelper() {
615 void CopyOrMoveOperationDelegate::StreamCopyHelper::Run(
616 const StatusCallback& callback) {
617 file_progress_callback_.Run(0);
618 last_progress_callback_invocation_time_ = base::Time::Now();
619 Read(callback);
622 void CopyOrMoveOperationDelegate::StreamCopyHelper::Cancel() {
623 cancel_requested_ = true;
626 void CopyOrMoveOperationDelegate::StreamCopyHelper::Read(
627 const StatusCallback& callback) {
628 int result = reader_->Read(
629 io_buffer_.get(), io_buffer_->size(),
630 base::Bind(&StreamCopyHelper::DidRead,
631 weak_factory_.GetWeakPtr(), callback));
632 if (result != net::ERR_IO_PENDING)
633 DidRead(callback, result);
636 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidRead(
637 const StatusCallback& callback, int result) {
638 if (cancel_requested_) {
639 callback.Run(base::File::FILE_ERROR_ABORT);
640 return;
643 if (result < 0) {
644 callback.Run(NetErrorToFileError(result));
645 return;
648 if (result == 0) {
649 // Here is the EOF.
650 if (need_flush_)
651 Flush(callback, true /* is_eof */);
652 else
653 callback.Run(base::File::FILE_OK);
654 return;
657 Write(callback, new net::DrainableIOBuffer(io_buffer_.get(), result));
660 void CopyOrMoveOperationDelegate::StreamCopyHelper::Write(
661 const StatusCallback& callback,
662 scoped_refptr<net::DrainableIOBuffer> buffer) {
663 DCHECK_GT(buffer->BytesRemaining(), 0);
665 int result = writer_->Write(
666 buffer.get(), buffer->BytesRemaining(),
667 base::Bind(&StreamCopyHelper::DidWrite,
668 weak_factory_.GetWeakPtr(), callback, buffer));
669 if (result != net::ERR_IO_PENDING)
670 DidWrite(callback, buffer, result);
673 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidWrite(
674 const StatusCallback& callback,
675 scoped_refptr<net::DrainableIOBuffer> buffer,
676 int result) {
677 if (cancel_requested_) {
678 callback.Run(base::File::FILE_ERROR_ABORT);
679 return;
682 if (result < 0) {
683 callback.Run(NetErrorToFileError(result));
684 return;
687 buffer->DidConsume(result);
688 num_copied_bytes_ += result;
690 // Check the elapsed time since last |file_progress_callback_| invocation.
691 base::Time now = base::Time::Now();
692 if (now - last_progress_callback_invocation_time_ >=
693 min_progress_callback_invocation_span_) {
694 file_progress_callback_.Run(num_copied_bytes_);
695 last_progress_callback_invocation_time_ = now;
698 if (buffer->BytesRemaining() > 0) {
699 Write(callback, buffer);
700 return;
703 if (need_flush_ &&
704 (num_copied_bytes_ - previous_flush_offset_) > kFlushIntervalInBytes) {
705 Flush(callback, false /* not is_eof */);
706 } else {
707 Read(callback);
711 void CopyOrMoveOperationDelegate::StreamCopyHelper::Flush(
712 const StatusCallback& callback, bool is_eof) {
713 int result = writer_->Flush(
714 base::Bind(&StreamCopyHelper::DidFlush,
715 weak_factory_.GetWeakPtr(), callback, is_eof));
716 if (result != net::ERR_IO_PENDING)
717 DidFlush(callback, is_eof, result);
720 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidFlush(
721 const StatusCallback& callback, bool is_eof, int result) {
722 if (cancel_requested_) {
723 callback.Run(base::File::FILE_ERROR_ABORT);
724 return;
727 previous_flush_offset_ = num_copied_bytes_;
728 if (is_eof)
729 callback.Run(NetErrorToFileError(result));
730 else
731 Read(callback);
734 CopyOrMoveOperationDelegate::CopyOrMoveOperationDelegate(
735 FileSystemContext* file_system_context,
736 const FileSystemURL& src_root,
737 const FileSystemURL& dest_root,
738 OperationType operation_type,
739 CopyOrMoveOption option,
740 const CopyProgressCallback& progress_callback,
741 const StatusCallback& callback)
742 : RecursiveOperationDelegate(file_system_context),
743 src_root_(src_root),
744 dest_root_(dest_root),
745 operation_type_(operation_type),
746 option_(option),
747 progress_callback_(progress_callback),
748 callback_(callback),
749 weak_factory_(this) {
750 same_file_system_ = src_root_.IsInSameFileSystem(dest_root_);
753 CopyOrMoveOperationDelegate::~CopyOrMoveOperationDelegate() {
754 STLDeleteElements(&running_copy_set_);
757 void CopyOrMoveOperationDelegate::Run() {
758 // Not supported; this should never be called.
759 NOTREACHED();
762 void CopyOrMoveOperationDelegate::RunRecursively() {
763 // Perform light-weight checks first.
765 // It is an error to try to copy/move an entry into its child.
766 if (same_file_system_ && src_root_.path().IsParent(dest_root_.path())) {
767 callback_.Run(base::File::FILE_ERROR_INVALID_OPERATION);
768 return;
771 if (same_file_system_ && src_root_.path() == dest_root_.path()) {
772 // In JS API this should return error, but we return success because Pepper
773 // wants to return success and we have a code path that returns error in
774 // Blink for JS (http://crbug.com/329517).
775 callback_.Run(base::File::FILE_OK);
776 return;
779 // Start to process the source directory recursively.
780 // TODO(kinuko): This could be too expensive for same_file_system_==true
781 // and operation==MOVE case, probably we can just rename the root directory.
782 // http://crbug.com/172187
783 StartRecursiveOperation(src_root_, callback_);
786 void CopyOrMoveOperationDelegate::ProcessFile(
787 const FileSystemURL& src_url,
788 const StatusCallback& callback) {
789 if (!progress_callback_.is_null()) {
790 progress_callback_.Run(
791 FileSystemOperation::BEGIN_COPY_ENTRY, src_url, FileSystemURL(), 0);
794 FileSystemURL dest_url = CreateDestURL(src_url);
795 CopyOrMoveImpl* impl = NULL;
796 if (same_file_system_ &&
797 (file_system_context()
798 ->GetFileSystemBackend(src_url.type())
799 ->HasInplaceCopyImplementation(src_url.type()) ||
800 operation_type_ == OPERATION_MOVE)) {
801 impl = new CopyOrMoveOnSameFileSystemImpl(
802 operation_runner(), operation_type_, src_url, dest_url, option_,
803 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress,
804 weak_factory_.GetWeakPtr(), src_url));
805 } else {
806 // Cross filesystem case.
807 base::File::Error error = base::File::FILE_ERROR_FAILED;
808 CopyOrMoveFileValidatorFactory* validator_factory =
809 file_system_context()->GetCopyOrMoveFileValidatorFactory(
810 dest_root_.type(), &error);
811 if (error != base::File::FILE_OK) {
812 callback.Run(error);
813 return;
816 if (!validator_factory) {
817 scoped_ptr<storage::FileStreamReader> reader =
818 file_system_context()->CreateFileStreamReader(
819 src_url, 0 /* offset */, storage::kMaximumLength, base::Time());
820 scoped_ptr<FileStreamWriter> writer =
821 file_system_context()->CreateFileStreamWriter(dest_url, 0);
822 if (reader && writer) {
823 impl = new StreamCopyOrMoveImpl(
824 operation_runner(),
825 file_system_context(),
826 operation_type_,
827 src_url,
828 dest_url,
829 option_,
830 reader.Pass(),
831 writer.Pass(),
832 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress,
833 weak_factory_.GetWeakPtr(),
834 src_url));
838 if (!impl) {
839 impl = new SnapshotCopyOrMoveImpl(
840 operation_runner(), operation_type_, src_url, dest_url, option_,
841 validator_factory,
842 base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress,
843 weak_factory_.GetWeakPtr(), src_url));
847 // Register the running task.
848 running_copy_set_.insert(impl);
849 impl->Run(base::Bind(
850 &CopyOrMoveOperationDelegate::DidCopyOrMoveFile,
851 weak_factory_.GetWeakPtr(), src_url, dest_url, callback, impl));
854 void CopyOrMoveOperationDelegate::ProcessDirectory(
855 const FileSystemURL& src_url,
856 const StatusCallback& callback) {
857 if (src_url == src_root_) {
858 // The src_root_ looks to be a directory.
859 // Try removing the dest_root_ to see if it exists and/or it is an
860 // empty directory.
861 // We do not invoke |progress_callback_| for source root, because it is
862 // already called in ProcessFile().
863 operation_runner()->RemoveDirectory(
864 dest_root_,
865 base::Bind(&CopyOrMoveOperationDelegate::DidTryRemoveDestRoot,
866 weak_factory_.GetWeakPtr(), callback));
867 return;
870 if (!progress_callback_.is_null()) {
871 progress_callback_.Run(
872 FileSystemOperation::BEGIN_COPY_ENTRY, src_url, FileSystemURL(), 0);
875 ProcessDirectoryInternal(src_url, CreateDestURL(src_url), callback);
878 void CopyOrMoveOperationDelegate::PostProcessDirectory(
879 const FileSystemURL& src_url,
880 const StatusCallback& callback) {
881 if (option_ == FileSystemOperation::OPTION_NONE) {
882 PostProcessDirectoryAfterTouchFile(
883 src_url, callback, base::File::FILE_OK);
884 return;
887 operation_runner()->GetMetadata(
888 src_url,
889 base::Bind(
890 &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterGetMetadata,
891 weak_factory_.GetWeakPtr(), src_url, callback));
894 void CopyOrMoveOperationDelegate::OnCancel() {
895 // Request to cancel all running Copy/Move file.
896 for (std::set<CopyOrMoveImpl*>::iterator iter = running_copy_set_.begin();
897 iter != running_copy_set_.end(); ++iter)
898 (*iter)->Cancel();
901 void CopyOrMoveOperationDelegate::DidCopyOrMoveFile(
902 const FileSystemURL& src_url,
903 const FileSystemURL& dest_url,
904 const StatusCallback& callback,
905 CopyOrMoveImpl* impl,
906 base::File::Error error) {
907 running_copy_set_.erase(impl);
908 delete impl;
910 if (!progress_callback_.is_null() && error == base::File::FILE_OK) {
911 progress_callback_.Run(
912 FileSystemOperation::END_COPY_ENTRY, src_url, dest_url, 0);
915 callback.Run(error);
918 void CopyOrMoveOperationDelegate::DidTryRemoveDestRoot(
919 const StatusCallback& callback,
920 base::File::Error error) {
921 if (error == base::File::FILE_ERROR_NOT_A_DIRECTORY) {
922 callback_.Run(base::File::FILE_ERROR_INVALID_OPERATION);
923 return;
925 if (error != base::File::FILE_OK &&
926 error != base::File::FILE_ERROR_NOT_FOUND) {
927 callback_.Run(error);
928 return;
931 ProcessDirectoryInternal(src_root_, dest_root_, callback);
934 void CopyOrMoveOperationDelegate::ProcessDirectoryInternal(
935 const FileSystemURL& src_url,
936 const FileSystemURL& dest_url,
937 const StatusCallback& callback) {
938 // If operation_type == Move we may need to record directories and
939 // restore directory timestamps in the end, though it may have
940 // negative performance impact.
941 // See http://crbug.com/171284 for more details.
942 operation_runner()->CreateDirectory(
943 dest_url, false /* exclusive */, false /* recursive */,
944 base::Bind(&CopyOrMoveOperationDelegate::DidCreateDirectory,
945 weak_factory_.GetWeakPtr(), src_url, dest_url, callback));
948 void CopyOrMoveOperationDelegate::DidCreateDirectory(
949 const FileSystemURL& src_url,
950 const FileSystemURL& dest_url,
951 const StatusCallback& callback,
952 base::File::Error error) {
953 if (!progress_callback_.is_null() && error == base::File::FILE_OK) {
954 progress_callback_.Run(
955 FileSystemOperation::END_COPY_ENTRY, src_url, dest_url, 0);
958 callback.Run(error);
961 void CopyOrMoveOperationDelegate::PostProcessDirectoryAfterGetMetadata(
962 const FileSystemURL& src_url,
963 const StatusCallback& callback,
964 base::File::Error error,
965 const base::File::Info& file_info) {
966 if (error != base::File::FILE_OK) {
967 // Ignore the error, and run post process which should run after TouchFile.
968 PostProcessDirectoryAfterTouchFile(
969 src_url, callback, base::File::FILE_OK);
970 return;
973 operation_runner()->TouchFile(
974 CreateDestURL(src_url), base::Time::Now() /* last access */,
975 file_info.last_modified,
976 base::Bind(
977 &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterTouchFile,
978 weak_factory_.GetWeakPtr(), src_url, callback));
981 void CopyOrMoveOperationDelegate::PostProcessDirectoryAfterTouchFile(
982 const FileSystemURL& src_url,
983 const StatusCallback& callback,
984 base::File::Error error) {
985 // Even if the TouchFile is failed, just ignore it.
987 if (operation_type_ == OPERATION_COPY) {
988 callback.Run(base::File::FILE_OK);
989 return;
992 DCHECK_EQ(OPERATION_MOVE, operation_type_);
994 // All files and subdirectories in the directory should be moved here,
995 // so remove the source directory for finalizing move operation.
996 operation_runner()->Remove(
997 src_url, false /* recursive */,
998 base::Bind(&CopyOrMoveOperationDelegate::DidRemoveSourceForMove,
999 weak_factory_.GetWeakPtr(), callback));
1002 void CopyOrMoveOperationDelegate::DidRemoveSourceForMove(
1003 const StatusCallback& callback,
1004 base::File::Error error) {
1005 if (error == base::File::FILE_ERROR_NOT_FOUND)
1006 error = base::File::FILE_OK;
1007 callback.Run(error);
1010 void CopyOrMoveOperationDelegate::OnCopyFileProgress(
1011 const FileSystemURL& src_url, int64 size) {
1012 if (!progress_callback_.is_null()) {
1013 progress_callback_.Run(
1014 FileSystemOperation::PROGRESS, src_url, FileSystemURL(), size);
1018 FileSystemURL CopyOrMoveOperationDelegate::CreateDestURL(
1019 const FileSystemURL& src_url) const {
1020 DCHECK_EQ(src_root_.type(), src_url.type());
1021 DCHECK_EQ(src_root_.origin(), src_url.origin());
1023 base::FilePath relative = dest_root_.virtual_path();
1024 src_root_.virtual_path().AppendRelativePath(src_url.virtual_path(),
1025 &relative);
1026 return file_system_context()->CreateCrackedFileSystemURL(
1027 dest_root_.origin(),
1028 dest_root_.mount_type(),
1029 relative);
1032 } // namespace storage