Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / storage / browser / fileapi / copy_or_move_operation_delegate.cc
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.
4
5 #include "storage/browser/fileapi/copy_or_move_operation_delegate.h"
6
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"
21
22 namespace storage {
23
24 const int64 kFlushIntervalInBytes = 10 << 20;  // 10MB.
25
26 class CopyOrMoveOperationDelegate::CopyOrMoveImpl {
27  public:
28   virtual ~CopyOrMoveImpl() {}
29   virtual void Run(
30       const CopyOrMoveOperationDelegate::StatusCallback& callback) = 0;
31   virtual void Cancel() = 0;
32
33  protected:
34   CopyOrMoveImpl() {}
35
36  private:
37   DISALLOW_COPY_AND_ASSIGN(CopyOrMoveImpl);
38 };
39
40 namespace {
41
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) {
61   }
62
63   virtual 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);
70     }
71   }
72
73   virtual 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.
77   }
78
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);
87 };
88
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) {
114   }
115
116   virtual 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));
123   }
124
125   virtual void Cancel() OVERRIDE {
126     cancel_requested_ = true;
127   }
128
129  private:
130   void RunAfterCreateSnapshot(
131       const CopyOrMoveOperationDelegate::StatusCallback& callback,
132       base::File::Error error,
133       const base::File::Info& file_info,
134       const base::FilePath& platform_path,
135       const scoped_refptr<storage::ShareableFileReference>& file_ref) {
136     if (cancel_requested_)
137       error = base::File::FILE_ERROR_ABORT;
138
139     if (error != base::File::FILE_OK) {
140       callback.Run(error);
141       return;
142     }
143
144     // For now we assume CreateSnapshotFile always return a valid local file
145     // path.
146     DCHECK(!platform_path.empty());
147
148     if (!validator_factory_) {
149       // No validation is needed.
150       RunAfterPreWriteValidation(platform_path, file_info, file_ref, callback,
151                                  base::File::FILE_OK);
152       return;
153     }
154
155     // Run pre write validation.
156     PreWriteValidation(
157         platform_path,
158         base::Bind(&SnapshotCopyOrMoveImpl::RunAfterPreWriteValidation,
159                    weak_factory_.GetWeakPtr(),
160                    platform_path, file_info, file_ref, callback));
161   }
162
163   void RunAfterPreWriteValidation(
164       const base::FilePath& platform_path,
165       const base::File::Info& file_info,
166       const scoped_refptr<storage::ShareableFileReference>& file_ref,
167       const CopyOrMoveOperationDelegate::StatusCallback& callback,
168       base::File::Error error) {
169     if (cancel_requested_)
170       error = base::File::FILE_ERROR_ABORT;
171
172     if (error != base::File::FILE_OK) {
173       callback.Run(error);
174       return;
175     }
176
177     // |file_ref| is unused but necessary to keep the file alive until
178     // CopyInForeignFile() is completed.
179     operation_runner_->CopyInForeignFile(
180         platform_path, dest_url_,
181         base::Bind(&SnapshotCopyOrMoveImpl::RunAfterCopyInForeignFile,
182                    weak_factory_.GetWeakPtr(), file_info, file_ref, callback));
183   }
184
185   void RunAfterCopyInForeignFile(
186       const base::File::Info& file_info,
187       const scoped_refptr<storage::ShareableFileReference>& file_ref,
188       const CopyOrMoveOperationDelegate::StatusCallback& callback,
189       base::File::Error error) {
190     if (cancel_requested_)
191       error = base::File::FILE_ERROR_ABORT;
192
193     if (error != base::File::FILE_OK) {
194       callback.Run(error);
195       return;
196     }
197
198     file_progress_callback_.Run(file_info.size);
199
200     if (option_ == FileSystemOperation::OPTION_NONE) {
201       RunAfterTouchFile(callback, base::File::FILE_OK);
202       return;
203     }
204
205     operation_runner_->TouchFile(
206         dest_url_, base::Time::Now() /* last_access */,
207         file_info.last_modified,
208         base::Bind(&SnapshotCopyOrMoveImpl::RunAfterTouchFile,
209                    weak_factory_.GetWeakPtr(), callback));
210   }
211
212   void RunAfterTouchFile(
213       const CopyOrMoveOperationDelegate::StatusCallback& callback,
214       base::File::Error error) {
215     // Even if TouchFile is failed, just ignore it.
216
217     if (cancel_requested_) {
218       callback.Run(base::File::FILE_ERROR_ABORT);
219       return;
220     }
221
222     // |validator_| is NULL when the destination filesystem does not do
223     // validation.
224     if (!validator_) {
225       // No validation is needed.
226       RunAfterPostWriteValidation(callback, base::File::FILE_OK);
227       return;
228     }
229
230     PostWriteValidation(
231         base::Bind(&SnapshotCopyOrMoveImpl::RunAfterPostWriteValidation,
232                    weak_factory_.GetWeakPtr(), callback));
233   }
234
235   void RunAfterPostWriteValidation(
236       const CopyOrMoveOperationDelegate::StatusCallback& callback,
237       base::File::Error error) {
238     if (cancel_requested_) {
239       callback.Run(base::File::FILE_ERROR_ABORT);
240       return;
241     }
242
243     if (error != base::File::FILE_OK) {
244       // Failed to validate. Remove the destination file.
245       operation_runner_->Remove(
246           dest_url_, true /* recursive */,
247           base::Bind(&SnapshotCopyOrMoveImpl::DidRemoveDestForError,
248                      weak_factory_.GetWeakPtr(), error, callback));
249       return;
250     }
251
252     if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_COPY) {
253       callback.Run(base::File::FILE_OK);
254       return;
255     }
256
257     DCHECK_EQ(CopyOrMoveOperationDelegate::OPERATION_MOVE, operation_type_);
258
259     // Remove the source for finalizing move operation.
260     operation_runner_->Remove(
261         src_url_, true /* recursive */,
262         base::Bind(&SnapshotCopyOrMoveImpl::RunAfterRemoveSourceForMove,
263                    weak_factory_.GetWeakPtr(), callback));
264   }
265
266   void RunAfterRemoveSourceForMove(
267       const CopyOrMoveOperationDelegate::StatusCallback& callback,
268       base::File::Error error) {
269     if (cancel_requested_)
270       error = base::File::FILE_ERROR_ABORT;
271
272     if (error == base::File::FILE_ERROR_NOT_FOUND)
273       error = base::File::FILE_OK;
274     callback.Run(error);
275   }
276
277   void DidRemoveDestForError(
278       base::File::Error prior_error,
279       const CopyOrMoveOperationDelegate::StatusCallback& callback,
280       base::File::Error error) {
281     if (error != base::File::FILE_OK) {
282       VLOG(1) << "Error removing destination file after validation error: "
283               << error;
284     }
285     callback.Run(prior_error);
286   }
287
288   // Runs pre-write validation.
289   void PreWriteValidation(
290       const base::FilePath& platform_path,
291       const CopyOrMoveOperationDelegate::StatusCallback& callback) {
292     DCHECK(validator_factory_);
293     validator_.reset(
294         validator_factory_->CreateCopyOrMoveFileValidator(
295             src_url_, platform_path));
296     validator_->StartPreWriteValidation(callback);
297   }
298
299   // Runs post-write validation.
300   void PostWriteValidation(
301       const CopyOrMoveOperationDelegate::StatusCallback& callback) {
302     operation_runner_->CreateSnapshotFile(
303         dest_url_,
304         base::Bind(
305             &SnapshotCopyOrMoveImpl::PostWriteValidationAfterCreateSnapshotFile,
306             weak_factory_.GetWeakPtr(), callback));
307   }
308
309   void PostWriteValidationAfterCreateSnapshotFile(
310       const CopyOrMoveOperationDelegate::StatusCallback& callback,
311       base::File::Error error,
312       const base::File::Info& file_info,
313       const base::FilePath& platform_path,
314       const scoped_refptr<storage::ShareableFileReference>& file_ref) {
315     if (cancel_requested_)
316       error = base::File::FILE_ERROR_ABORT;
317
318     if (error != base::File::FILE_OK) {
319       callback.Run(error);
320       return;
321     }
322
323     DCHECK(validator_);
324     // Note: file_ref passed here to keep the file alive until after
325     // the StartPostWriteValidation operation finishes.
326     validator_->StartPostWriteValidation(
327         platform_path,
328         base::Bind(&SnapshotCopyOrMoveImpl::DidPostWriteValidation,
329                    weak_factory_.GetWeakPtr(), file_ref, callback));
330   }
331
332   // |file_ref| is unused; it is passed here to make sure the reference is
333   // alive until after post-write validation is complete.
334   void DidPostWriteValidation(
335       const scoped_refptr<storage::ShareableFileReference>& file_ref,
336       const CopyOrMoveOperationDelegate::StatusCallback& callback,
337       base::File::Error error) {
338     callback.Run(error);
339   }
340
341   FileSystemOperationRunner* operation_runner_;
342   CopyOrMoveOperationDelegate::OperationType operation_type_;
343   FileSystemURL src_url_;
344   FileSystemURL dest_url_;
345
346   CopyOrMoveOperationDelegate::CopyOrMoveOption option_;
347   CopyOrMoveFileValidatorFactory* validator_factory_;
348   scoped_ptr<CopyOrMoveFileValidator> validator_;
349   FileSystemOperation::CopyFileProgressCallback file_progress_callback_;
350   bool cancel_requested_;
351   base::WeakPtrFactory<SnapshotCopyOrMoveImpl> weak_factory_;
352   DISALLOW_COPY_AND_ASSIGN(SnapshotCopyOrMoveImpl);
353 };
354
355 // The size of buffer for StreamCopyHelper.
356 const int kReadBufferSize = 32768;
357
358 // To avoid too many progress callbacks, it should be called less
359 // frequently than 50ms.
360 const int kMinProgressCallbackInvocationSpanInMilliseconds = 50;
361
362 // Specifically for cross file system copy/move operation, this class uses
363 // stream reader and writer for copying. Validator is not supported, so if
364 // necessary SnapshotCopyOrMoveImpl should be used.
365 class StreamCopyOrMoveImpl
366     : public CopyOrMoveOperationDelegate::CopyOrMoveImpl {
367  public:
368   StreamCopyOrMoveImpl(
369       FileSystemOperationRunner* operation_runner,
370       FileSystemContext* file_system_context,
371       CopyOrMoveOperationDelegate::OperationType operation_type,
372       const FileSystemURL& src_url,
373       const FileSystemURL& dest_url,
374       CopyOrMoveOperationDelegate::CopyOrMoveOption option,
375       scoped_ptr<storage::FileStreamReader> reader,
376       scoped_ptr<FileStreamWriter> writer,
377       const FileSystemOperation::CopyFileProgressCallback&
378           file_progress_callback)
379       : operation_runner_(operation_runner),
380         file_system_context_(file_system_context),
381         operation_type_(operation_type),
382         src_url_(src_url),
383         dest_url_(dest_url),
384         option_(option),
385         reader_(reader.Pass()),
386         writer_(writer.Pass()),
387         file_progress_callback_(file_progress_callback),
388         cancel_requested_(false),
389         weak_factory_(this) {}
390
391   virtual void Run(
392       const CopyOrMoveOperationDelegate::StatusCallback& callback) OVERRIDE {
393     // Reader can be created even if the entry does not exist or the entry is
394     // a directory. To check errors before destination file creation,
395     // check metadata first.
396     operation_runner_->GetMetadata(
397         src_url_,
398         base::Bind(&StreamCopyOrMoveImpl::RunAfterGetMetadataForSource,
399                    weak_factory_.GetWeakPtr(), callback));
400   }
401
402   virtual void Cancel() OVERRIDE {
403     cancel_requested_ = true;
404     if (copy_helper_)
405       copy_helper_->Cancel();
406   }
407
408  private:
409   void NotifyOnStartUpdate(const FileSystemURL& url) {
410     if (file_system_context_->GetUpdateObservers(url.type())) {
411       file_system_context_->GetUpdateObservers(url.type())
412           ->Notify(&FileUpdateObserver::OnStartUpdate, MakeTuple(url));
413     }
414   }
415
416   void NotifyOnModifyFile(const FileSystemURL& url) {
417     if (file_system_context_->GetChangeObservers(url.type())) {
418       file_system_context_->GetChangeObservers(url.type())
419           ->Notify(&FileChangeObserver::OnModifyFile, MakeTuple(url));
420     }
421   }
422
423   void NotifyOnEndUpdate(const FileSystemURL& url) {
424     if (file_system_context_->GetUpdateObservers(url.type())) {
425       file_system_context_->GetUpdateObservers(url.type())
426           ->Notify(&FileUpdateObserver::OnEndUpdate, MakeTuple(url));
427     }
428   }
429
430   void RunAfterGetMetadataForSource(
431       const CopyOrMoveOperationDelegate::StatusCallback& callback,
432       base::File::Error error,
433       const base::File::Info& file_info) {
434     if (cancel_requested_)
435       error = base::File::FILE_ERROR_ABORT;
436
437     if (error != base::File::FILE_OK) {
438       callback.Run(error);
439       return;
440     }
441
442     if (file_info.is_directory) {
443       // If not a directory, failed with appropriate error code.
444       callback.Run(base::File::FILE_ERROR_NOT_A_FILE);
445       return;
446     }
447
448     // To use FileStreamWriter, we need to ensure the destination file exists.
449     operation_runner_->CreateFile(
450         dest_url_,
451         true /* exclusive */,
452         base::Bind(&StreamCopyOrMoveImpl::RunAfterCreateFileForDestination,
453                    weak_factory_.GetWeakPtr(),
454                    callback,
455                    file_info.last_modified));
456   }
457
458   void RunAfterCreateFileForDestination(
459       const CopyOrMoveOperationDelegate::StatusCallback& callback,
460       const base::Time& last_modified,
461       base::File::Error error) {
462     if (cancel_requested_)
463       error = base::File::FILE_ERROR_ABORT;
464     // This conversion is to return the consistent status code with
465     // FileSystemFileUtil::Copy.
466     if (error == base::File::FILE_ERROR_NOT_A_FILE)
467       error = base::File::FILE_ERROR_INVALID_OPERATION;
468
469     if (error != base::File::FILE_OK &&
470         error != base::File::FILE_ERROR_EXISTS) {
471       callback.Run(error);
472       return;
473     }
474
475     if (error == base::File::FILE_ERROR_EXISTS) {
476       operation_runner_->Truncate(
477           dest_url_,
478           0 /* length */,
479           base::Bind(&StreamCopyOrMoveImpl::RunAfterTruncateForDestination,
480                      weak_factory_.GetWeakPtr(),
481                      callback,
482                      last_modified));
483       return;
484     }
485     RunAfterTruncateForDestination(
486         callback, last_modified, base::File::FILE_OK);
487   }
488
489   void RunAfterTruncateForDestination(
490       const CopyOrMoveOperationDelegate::StatusCallback& callback,
491       const base::Time& last_modified,
492       base::File::Error error) {
493     if (cancel_requested_)
494       error = base::File::FILE_ERROR_ABORT;
495
496     if (error != base::File::FILE_OK) {
497       callback.Run(error);
498       return;
499     }
500
501     const bool need_flush = dest_url_.mount_option().copy_sync_option() ==
502                             storage::COPY_SYNC_OPTION_SYNC;
503
504     NotifyOnStartUpdate(dest_url_);
505     DCHECK(!copy_helper_);
506     copy_helper_.reset(
507         new CopyOrMoveOperationDelegate::StreamCopyHelper(
508             reader_.Pass(), writer_.Pass(),
509             need_flush,
510             kReadBufferSize,
511             file_progress_callback_,
512             base::TimeDelta::FromMilliseconds(
513                 kMinProgressCallbackInvocationSpanInMilliseconds)));
514     copy_helper_->Run(
515         base::Bind(&StreamCopyOrMoveImpl::RunAfterStreamCopy,
516                    weak_factory_.GetWeakPtr(), callback, last_modified));
517   }
518
519   void RunAfterStreamCopy(
520       const CopyOrMoveOperationDelegate::StatusCallback& callback,
521       const base::Time& last_modified,
522       base::File::Error error) {
523     NotifyOnModifyFile(dest_url_);
524     NotifyOnEndUpdate(dest_url_);
525     if (cancel_requested_)
526       error = base::File::FILE_ERROR_ABORT;
527
528     if (error != base::File::FILE_OK) {
529       callback.Run(error);
530       return;
531     }
532
533     if (option_ == FileSystemOperation::OPTION_NONE) {
534       RunAfterTouchFile(callback, base::File::FILE_OK);
535       return;
536     }
537
538     operation_runner_->TouchFile(
539         dest_url_, base::Time::Now() /* last_access */, last_modified,
540         base::Bind(&StreamCopyOrMoveImpl::RunAfterTouchFile,
541                    weak_factory_.GetWeakPtr(), callback));
542   }
543
544   void RunAfterTouchFile(
545       const CopyOrMoveOperationDelegate::StatusCallback& callback,
546       base::File::Error error) {
547     // Even if TouchFile is failed, just ignore it.
548     if (cancel_requested_) {
549       callback.Run(base::File::FILE_ERROR_ABORT);
550       return;
551     }
552
553     if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_COPY) {
554       callback.Run(base::File::FILE_OK);
555       return;
556     }
557
558     DCHECK_EQ(CopyOrMoveOperationDelegate::OPERATION_MOVE, operation_type_);
559
560     // Remove the source for finalizing move operation.
561     operation_runner_->Remove(
562         src_url_, false /* recursive */,
563         base::Bind(&StreamCopyOrMoveImpl::RunAfterRemoveForMove,
564                    weak_factory_.GetWeakPtr(), callback));
565   }
566
567   void RunAfterRemoveForMove(
568       const CopyOrMoveOperationDelegate::StatusCallback& callback,
569       base::File::Error error) {
570     if (cancel_requested_)
571       error = base::File::FILE_ERROR_ABORT;
572     if (error == base::File::FILE_ERROR_NOT_FOUND)
573       error = base::File::FILE_OK;
574     callback.Run(error);
575   }
576
577   FileSystemOperationRunner* operation_runner_;
578   scoped_refptr<FileSystemContext> file_system_context_;
579   CopyOrMoveOperationDelegate::OperationType operation_type_;
580   FileSystemURL src_url_;
581   FileSystemURL dest_url_;
582   CopyOrMoveOperationDelegate::CopyOrMoveOption option_;
583   scoped_ptr<storage::FileStreamReader> reader_;
584   scoped_ptr<FileStreamWriter> writer_;
585   FileSystemOperation::CopyFileProgressCallback file_progress_callback_;
586   scoped_ptr<CopyOrMoveOperationDelegate::StreamCopyHelper> copy_helper_;
587   bool cancel_requested_;
588   base::WeakPtrFactory<StreamCopyOrMoveImpl> weak_factory_;
589   DISALLOW_COPY_AND_ASSIGN(StreamCopyOrMoveImpl);
590 };
591
592 }  // namespace
593
594 CopyOrMoveOperationDelegate::StreamCopyHelper::StreamCopyHelper(
595     scoped_ptr<storage::FileStreamReader> reader,
596     scoped_ptr<FileStreamWriter> writer,
597     bool need_flush,
598     int buffer_size,
599     const FileSystemOperation::CopyFileProgressCallback& file_progress_callback,
600     const base::TimeDelta& min_progress_callback_invocation_span)
601     : reader_(reader.Pass()),
602       writer_(writer.Pass()),
603       need_flush_(need_flush),
604       file_progress_callback_(file_progress_callback),
605       io_buffer_(new net::IOBufferWithSize(buffer_size)),
606       num_copied_bytes_(0),
607       previous_flush_offset_(0),
608       min_progress_callback_invocation_span_(
609           min_progress_callback_invocation_span),
610       cancel_requested_(false),
611       weak_factory_(this) {
612 }
613
614 CopyOrMoveOperationDelegate::StreamCopyHelper::~StreamCopyHelper() {
615 }
616
617 void CopyOrMoveOperationDelegate::StreamCopyHelper::Run(
618     const StatusCallback& callback) {
619   file_progress_callback_.Run(0);
620   last_progress_callback_invocation_time_ = base::Time::Now();
621   Read(callback);
622 }
623
624 void CopyOrMoveOperationDelegate::StreamCopyHelper::Cancel() {
625   cancel_requested_ = true;
626 }
627
628 void CopyOrMoveOperationDelegate::StreamCopyHelper::Read(
629     const StatusCallback& callback) {
630   int result = reader_->Read(
631       io_buffer_.get(), io_buffer_->size(),
632       base::Bind(&StreamCopyHelper::DidRead,
633                  weak_factory_.GetWeakPtr(), callback));
634   if (result != net::ERR_IO_PENDING)
635     DidRead(callback, result);
636 }
637
638 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidRead(
639     const StatusCallback& callback, int result) {
640   if (cancel_requested_) {
641     callback.Run(base::File::FILE_ERROR_ABORT);
642     return;
643   }
644
645   if (result < 0) {
646     callback.Run(NetErrorToFileError(result));
647     return;
648   }
649
650   if (result == 0) {
651     // Here is the EOF.
652     if (need_flush_)
653       Flush(callback, true /* is_eof */);
654     else
655       callback.Run(base::File::FILE_OK);
656     return;
657   }
658
659   Write(callback, new net::DrainableIOBuffer(io_buffer_.get(), result));
660 }
661
662 void CopyOrMoveOperationDelegate::StreamCopyHelper::Write(
663     const StatusCallback& callback,
664     scoped_refptr<net::DrainableIOBuffer> buffer) {
665   DCHECK_GT(buffer->BytesRemaining(), 0);
666
667   int result = writer_->Write(
668       buffer.get(), buffer->BytesRemaining(),
669       base::Bind(&StreamCopyHelper::DidWrite,
670                  weak_factory_.GetWeakPtr(), callback, buffer));
671   if (result != net::ERR_IO_PENDING)
672     DidWrite(callback, buffer, result);
673 }
674
675 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidWrite(
676     const StatusCallback& callback,
677     scoped_refptr<net::DrainableIOBuffer> buffer,
678     int result) {
679   if (cancel_requested_) {
680     callback.Run(base::File::FILE_ERROR_ABORT);
681     return;
682   }
683
684   if (result < 0) {
685     callback.Run(NetErrorToFileError(result));
686     return;
687   }
688
689   buffer->DidConsume(result);
690   num_copied_bytes_ += result;
691
692   // Check the elapsed time since last |file_progress_callback_| invocation.
693   base::Time now = base::Time::Now();
694   if (now - last_progress_callback_invocation_time_ >=
695       min_progress_callback_invocation_span_) {
696     file_progress_callback_.Run(num_copied_bytes_);
697     last_progress_callback_invocation_time_ = now;
698   }
699
700   if (buffer->BytesRemaining() > 0) {
701     Write(callback, buffer);
702     return;
703   }
704
705   if (need_flush_ &&
706       (num_copied_bytes_ - previous_flush_offset_) > kFlushIntervalInBytes) {
707     Flush(callback, false /* not is_eof */);
708   } else {
709     Read(callback);
710   }
711 }
712
713 void CopyOrMoveOperationDelegate::StreamCopyHelper::Flush(
714     const StatusCallback& callback, bool is_eof) {
715   int result = writer_->Flush(
716       base::Bind(&StreamCopyHelper::DidFlush,
717                  weak_factory_.GetWeakPtr(), callback, is_eof));
718   if (result != net::ERR_IO_PENDING)
719     DidFlush(callback, is_eof, result);
720 }
721
722 void CopyOrMoveOperationDelegate::StreamCopyHelper::DidFlush(
723     const StatusCallback& callback, bool is_eof, int result) {
724   if (cancel_requested_) {
725     callback.Run(base::File::FILE_ERROR_ABORT);
726     return;
727   }
728
729   previous_flush_offset_ = num_copied_bytes_;
730   if (is_eof)
731     callback.Run(NetErrorToFileError(result));
732   else
733     Read(callback);
734 }
735
736 CopyOrMoveOperationDelegate::CopyOrMoveOperationDelegate(
737     FileSystemContext* file_system_context,
738     const FileSystemURL& src_root,
739     const FileSystemURL& dest_root,
740     OperationType operation_type,
741     CopyOrMoveOption option,
742     const CopyProgressCallback& progress_callback,
743     const StatusCallback& callback)
744     : RecursiveOperationDelegate(file_system_context),
745       src_root_(src_root),
746       dest_root_(dest_root),
747       operation_type_(operation_type),
748       option_(option),
749       progress_callback_(progress_callback),
750       callback_(callback),
751       weak_factory_(this) {
752   same_file_system_ = src_root_.IsInSameFileSystem(dest_root_);
753 }
754
755 CopyOrMoveOperationDelegate::~CopyOrMoveOperationDelegate() {
756   STLDeleteElements(&running_copy_set_);
757 }
758
759 void CopyOrMoveOperationDelegate::Run() {
760   // Not supported; this should never be called.
761   NOTREACHED();
762 }
763
764 void CopyOrMoveOperationDelegate::RunRecursively() {
765   // Perform light-weight checks first.
766
767   // It is an error to try to copy/move an entry into its child.
768   if (same_file_system_ && src_root_.path().IsParent(dest_root_.path())) {
769     callback_.Run(base::File::FILE_ERROR_INVALID_OPERATION);
770     return;
771   }
772
773   if (same_file_system_ && src_root_.path() == dest_root_.path()) {
774     // In JS API this should return error, but we return success because Pepper
775     // wants to return success and we have a code path that returns error in
776     // Blink for JS (http://crbug.com/329517).
777     callback_.Run(base::File::FILE_OK);
778     return;
779   }
780
781   // Start to process the source directory recursively.
782   // TODO(kinuko): This could be too expensive for same_file_system_==true
783   // and operation==MOVE case, probably we can just rename the root directory.
784   // http://crbug.com/172187
785   StartRecursiveOperation(src_root_, callback_);
786 }
787
788 void CopyOrMoveOperationDelegate::ProcessFile(
789     const FileSystemURL& src_url,
790     const StatusCallback& callback) {
791   if (!progress_callback_.is_null()) {
792     progress_callback_.Run(
793         FileSystemOperation::BEGIN_COPY_ENTRY, src_url, FileSystemURL(), 0);
794   }
795
796   FileSystemURL dest_url = CreateDestURL(src_url);
797   CopyOrMoveImpl* impl = NULL;
798   if (same_file_system_ &&
799       (file_system_context()
800            ->GetFileSystemBackend(src_url.type())
801            ->HasInplaceCopyImplementation(src_url.type()) ||
802        operation_type_ == OPERATION_MOVE)) {
803     impl = new CopyOrMoveOnSameFileSystemImpl(
804         operation_runner(), operation_type_, src_url, dest_url, option_,
805         base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress,
806                    weak_factory_.GetWeakPtr(), src_url));
807   } else {
808     // Cross filesystem case.
809     base::File::Error error = base::File::FILE_ERROR_FAILED;
810     CopyOrMoveFileValidatorFactory* validator_factory =
811         file_system_context()->GetCopyOrMoveFileValidatorFactory(
812             dest_root_.type(), &error);
813     if (error != base::File::FILE_OK) {
814       callback.Run(error);
815       return;
816     }
817
818     if (!validator_factory) {
819       scoped_ptr<storage::FileStreamReader> reader =
820           file_system_context()->CreateFileStreamReader(
821               src_url, 0 /* offset */, storage::kMaximumLength, base::Time());
822       scoped_ptr<FileStreamWriter> writer =
823           file_system_context()->CreateFileStreamWriter(dest_url, 0);
824       if (reader && writer) {
825         impl = new StreamCopyOrMoveImpl(
826             operation_runner(),
827             file_system_context(),
828             operation_type_,
829             src_url,
830             dest_url,
831             option_,
832             reader.Pass(),
833             writer.Pass(),
834             base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress,
835                        weak_factory_.GetWeakPtr(),
836                        src_url));
837       }
838     }
839
840     if (!impl) {
841       impl = new SnapshotCopyOrMoveImpl(
842           operation_runner(), operation_type_, src_url, dest_url, option_,
843           validator_factory,
844           base::Bind(&CopyOrMoveOperationDelegate::OnCopyFileProgress,
845                      weak_factory_.GetWeakPtr(), src_url));
846     }
847   }
848
849   // Register the running task.
850   running_copy_set_.insert(impl);
851   impl->Run(base::Bind(
852       &CopyOrMoveOperationDelegate::DidCopyOrMoveFile,
853       weak_factory_.GetWeakPtr(), src_url, dest_url, callback, impl));
854 }
855
856 void CopyOrMoveOperationDelegate::ProcessDirectory(
857     const FileSystemURL& src_url,
858     const StatusCallback& callback) {
859   if (src_url == src_root_) {
860     // The src_root_ looks to be a directory.
861     // Try removing the dest_root_ to see if it exists and/or it is an
862     // empty directory.
863     // We do not invoke |progress_callback_| for source root, because it is
864     // already called in ProcessFile().
865     operation_runner()->RemoveDirectory(
866         dest_root_,
867         base::Bind(&CopyOrMoveOperationDelegate::DidTryRemoveDestRoot,
868                    weak_factory_.GetWeakPtr(), callback));
869     return;
870   }
871
872   if (!progress_callback_.is_null()) {
873     progress_callback_.Run(
874         FileSystemOperation::BEGIN_COPY_ENTRY, src_url, FileSystemURL(), 0);
875   }
876
877   ProcessDirectoryInternal(src_url, CreateDestURL(src_url), callback);
878 }
879
880 void CopyOrMoveOperationDelegate::PostProcessDirectory(
881     const FileSystemURL& src_url,
882     const StatusCallback& callback) {
883   if (option_ == FileSystemOperation::OPTION_NONE) {
884     PostProcessDirectoryAfterTouchFile(
885         src_url, callback, base::File::FILE_OK);
886     return;
887   }
888
889   operation_runner()->GetMetadata(
890       src_url,
891       base::Bind(
892           &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterGetMetadata,
893           weak_factory_.GetWeakPtr(), src_url, callback));
894 }
895
896 void CopyOrMoveOperationDelegate::OnCancel() {
897   // Request to cancel all running Copy/Move file.
898   for (std::set<CopyOrMoveImpl*>::iterator iter = running_copy_set_.begin();
899        iter != running_copy_set_.end(); ++iter)
900     (*iter)->Cancel();
901 }
902
903 void CopyOrMoveOperationDelegate::DidCopyOrMoveFile(
904     const FileSystemURL& src_url,
905     const FileSystemURL& dest_url,
906     const StatusCallback& callback,
907     CopyOrMoveImpl* impl,
908     base::File::Error error) {
909   running_copy_set_.erase(impl);
910   delete impl;
911
912   if (!progress_callback_.is_null() && error == base::File::FILE_OK) {
913     progress_callback_.Run(
914         FileSystemOperation::END_COPY_ENTRY, src_url, dest_url, 0);
915   }
916
917   callback.Run(error);
918 }
919
920 void CopyOrMoveOperationDelegate::DidTryRemoveDestRoot(
921     const StatusCallback& callback,
922     base::File::Error error) {
923   if (error == base::File::FILE_ERROR_NOT_A_DIRECTORY) {
924     callback_.Run(base::File::FILE_ERROR_INVALID_OPERATION);
925     return;
926   }
927   if (error != base::File::FILE_OK &&
928       error != base::File::FILE_ERROR_NOT_FOUND) {
929     callback_.Run(error);
930     return;
931   }
932
933   ProcessDirectoryInternal(src_root_, dest_root_, callback);
934 }
935
936 void CopyOrMoveOperationDelegate::ProcessDirectoryInternal(
937     const FileSystemURL& src_url,
938     const FileSystemURL& dest_url,
939     const StatusCallback& callback) {
940   // If operation_type == Move we may need to record directories and
941   // restore directory timestamps in the end, though it may have
942   // negative performance impact.
943   // See http://crbug.com/171284 for more details.
944   operation_runner()->CreateDirectory(
945       dest_url, false /* exclusive */, false /* recursive */,
946       base::Bind(&CopyOrMoveOperationDelegate::DidCreateDirectory,
947                  weak_factory_.GetWeakPtr(), src_url, dest_url, callback));
948 }
949
950 void CopyOrMoveOperationDelegate::DidCreateDirectory(
951     const FileSystemURL& src_url,
952     const FileSystemURL& dest_url,
953     const StatusCallback& callback,
954     base::File::Error error) {
955   if (!progress_callback_.is_null() && error == base::File::FILE_OK) {
956     progress_callback_.Run(
957         FileSystemOperation::END_COPY_ENTRY, src_url, dest_url, 0);
958   }
959
960   callback.Run(error);
961 }
962
963 void CopyOrMoveOperationDelegate::PostProcessDirectoryAfterGetMetadata(
964     const FileSystemURL& src_url,
965     const StatusCallback& callback,
966     base::File::Error error,
967     const base::File::Info& file_info) {
968   if (error != base::File::FILE_OK) {
969     // Ignore the error, and run post process which should run after TouchFile.
970     PostProcessDirectoryAfterTouchFile(
971         src_url, callback, base::File::FILE_OK);
972     return;
973   }
974
975   operation_runner()->TouchFile(
976       CreateDestURL(src_url), base::Time::Now() /* last access */,
977       file_info.last_modified,
978       base::Bind(
979           &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterTouchFile,
980           weak_factory_.GetWeakPtr(), src_url, callback));
981 }
982
983 void CopyOrMoveOperationDelegate::PostProcessDirectoryAfterTouchFile(
984     const FileSystemURL& src_url,
985     const StatusCallback& callback,
986     base::File::Error error) {
987   // Even if the TouchFile is failed, just ignore it.
988
989   if (operation_type_ == OPERATION_COPY) {
990     callback.Run(base::File::FILE_OK);
991     return;
992   }
993
994   DCHECK_EQ(OPERATION_MOVE, operation_type_);
995
996   // All files and subdirectories in the directory should be moved here,
997   // so remove the source directory for finalizing move operation.
998   operation_runner()->Remove(
999       src_url, false /* recursive */,
1000       base::Bind(&CopyOrMoveOperationDelegate::DidRemoveSourceForMove,
1001                  weak_factory_.GetWeakPtr(), callback));
1002 }
1003
1004 void CopyOrMoveOperationDelegate::DidRemoveSourceForMove(
1005     const StatusCallback& callback,
1006     base::File::Error error) {
1007   if (error == base::File::FILE_ERROR_NOT_FOUND)
1008     error = base::File::FILE_OK;
1009   callback.Run(error);
1010 }
1011
1012 void CopyOrMoveOperationDelegate::OnCopyFileProgress(
1013     const FileSystemURL& src_url, int64 size) {
1014   if (!progress_callback_.is_null()) {
1015     progress_callback_.Run(
1016         FileSystemOperation::PROGRESS, src_url, FileSystemURL(), size);
1017   }
1018 }
1019
1020 FileSystemURL CopyOrMoveOperationDelegate::CreateDestURL(
1021     const FileSystemURL& src_url) const {
1022   DCHECK_EQ(src_root_.type(), src_url.type());
1023   DCHECK_EQ(src_root_.origin(), src_url.origin());
1024
1025   base::FilePath relative = dest_root_.virtual_path();
1026   src_root_.virtual_path().AppendRelativePath(src_url.virtual_path(),
1027                                               &relative);
1028   return file_system_context()->CreateCrackedFileSystemURL(
1029       dest_root_.origin(),
1030       dest_root_.mount_type(),
1031       relative);
1032 }
1033
1034 }  // namespace storage