Update To 11.40.268.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   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   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   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   void Cancel() override { cancel_requested_ = true; }
126
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;
136
137     if (error != base::File::FILE_OK) {
138       callback.Run(error);
139       return;
140     }
141
142     // For now we assume CreateSnapshotFile always return a valid local file
143     // path.
144     DCHECK(!platform_path.empty());
145
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;
151     }
152
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));
159   }
160
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;
169
170     if (error != base::File::FILE_OK) {
171       callback.Run(error);
172       return;
173     }
174
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));
181   }
182
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;
190
191     if (error != base::File::FILE_OK) {
192       callback.Run(error);
193       return;
194     }
195
196     file_progress_callback_.Run(file_info.size);
197
198     if (option_ == FileSystemOperation::OPTION_NONE) {
199       RunAfterTouchFile(callback, base::File::FILE_OK);
200       return;
201     }
202
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));
208   }
209
210   void RunAfterTouchFile(
211       const CopyOrMoveOperationDelegate::StatusCallback& callback,
212       base::File::Error error) {
213     // Even if TouchFile is failed, just ignore it.
214
215     if (cancel_requested_) {
216       callback.Run(base::File::FILE_ERROR_ABORT);
217       return;
218     }
219
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;
226     }
227
228     PostWriteValidation(
229         base::Bind(&SnapshotCopyOrMoveImpl::RunAfterPostWriteValidation,
230                    weak_factory_.GetWeakPtr(), callback));
231   }
232
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;
239     }
240
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;
248     }
249
250     if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_COPY) {
251       callback.Run(base::File::FILE_OK);
252       return;
253     }
254
255     DCHECK_EQ(CopyOrMoveOperationDelegate::OPERATION_MOVE, operation_type_);
256
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));
262   }
263
264   void RunAfterRemoveSourceForMove(
265       const CopyOrMoveOperationDelegate::StatusCallback& callback,
266       base::File::Error error) {
267     if (cancel_requested_)
268       error = base::File::FILE_ERROR_ABORT;
269
270     if (error == base::File::FILE_ERROR_NOT_FOUND)
271       error = base::File::FILE_OK;
272     callback.Run(error);
273   }
274
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;
282     }
283     callback.Run(prior_error);
284   }
285
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);
295   }
296
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));
305   }
306
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;
315
316     if (error != base::File::FILE_OK) {
317       callback.Run(error);
318       return;
319     }
320
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));
328   }
329
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);
337   }
338
339   FileSystemOperationRunner* operation_runner_;
340   CopyOrMoveOperationDelegate::OperationType operation_type_;
341   FileSystemURL src_url_;
342   FileSystemURL dest_url_;
343
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);
351 };
352
353 // The size of buffer for StreamCopyHelper.
354 const int kReadBufferSize = 32768;
355
356 // To avoid too many progress callbacks, it should be called less
357 // frequently than 50ms.
358 const int kMinProgressCallbackInvocationSpanInMilliseconds = 50;
359
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) {}
388
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));
398   }
399
400   void Cancel() override {
401     cancel_requested_ = true;
402     if (copy_helper_)
403       copy_helper_->Cancel();
404   }
405
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));
411     }
412   }
413
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));
418     }
419   }
420
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));
425     }
426   }
427
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;
434
435     if (error != base::File::FILE_OK) {
436       callback.Run(error);
437       return;
438     }
439
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;
444     }
445
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));
454   }
455
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;
466
467     if (error != base::File::FILE_OK &&
468         error != base::File::FILE_ERROR_EXISTS) {
469       callback.Run(error);
470       return;
471     }
472
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;
482     }
483     RunAfterTruncateForDestination(
484         callback, last_modified, base::File::FILE_OK);
485   }
486
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;
493
494     if (error != base::File::FILE_OK) {
495       callback.Run(error);
496       return;
497     }
498
499     const bool need_flush = dest_url_.mount_option().copy_sync_option() ==
500                             storage::COPY_SYNC_OPTION_SYNC;
501
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));
515   }
516
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;
525
526     if (error != base::File::FILE_OK) {
527       callback.Run(error);
528       return;
529     }
530
531     if (option_ == FileSystemOperation::OPTION_NONE) {
532       RunAfterTouchFile(callback, base::File::FILE_OK);
533       return;
534     }
535
536     operation_runner_->TouchFile(
537         dest_url_, base::Time::Now() /* last_access */, last_modified,
538         base::Bind(&StreamCopyOrMoveImpl::RunAfterTouchFile,
539                    weak_factory_.GetWeakPtr(), callback));
540   }
541
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;
549     }
550
551     if (operation_type_ == CopyOrMoveOperationDelegate::OPERATION_COPY) {
552       callback.Run(base::File::FILE_OK);
553       return;
554     }
555
556     DCHECK_EQ(CopyOrMoveOperationDelegate::OPERATION_MOVE, operation_type_);
557
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));
563   }
564
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);
573   }
574
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);
588 };
589
590 }  // namespace
591
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) {
610 }
611
612 CopyOrMoveOperationDelegate::StreamCopyHelper::~StreamCopyHelper() {
613 }
614
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);
620 }
621
622 void CopyOrMoveOperationDelegate::StreamCopyHelper::Cancel() {
623   cancel_requested_ = true;
624 }
625
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);
634 }
635
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;
641   }
642
643   if (result < 0) {
644     callback.Run(NetErrorToFileError(result));
645     return;
646   }
647
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;
655   }
656
657   Write(callback, new net::DrainableIOBuffer(io_buffer_.get(), result));
658 }
659
660 void CopyOrMoveOperationDelegate::StreamCopyHelper::Write(
661     const StatusCallback& callback,
662     scoped_refptr<net::DrainableIOBuffer> buffer) {
663   DCHECK_GT(buffer->BytesRemaining(), 0);
664
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);
671 }
672
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;
680   }
681
682   if (result < 0) {
683     callback.Run(NetErrorToFileError(result));
684     return;
685   }
686
687   buffer->DidConsume(result);
688   num_copied_bytes_ += result;
689
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;
696   }
697
698   if (buffer->BytesRemaining() > 0) {
699     Write(callback, buffer);
700     return;
701   }
702
703   if (need_flush_ &&
704       (num_copied_bytes_ - previous_flush_offset_) > kFlushIntervalInBytes) {
705     Flush(callback, false /* not is_eof */);
706   } else {
707     Read(callback);
708   }
709 }
710
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);
718 }
719
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;
725   }
726
727   previous_flush_offset_ = num_copied_bytes_;
728   if (is_eof)
729     callback.Run(NetErrorToFileError(result));
730   else
731     Read(callback);
732 }
733
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_);
751 }
752
753 CopyOrMoveOperationDelegate::~CopyOrMoveOperationDelegate() {
754   STLDeleteElements(&running_copy_set_);
755 }
756
757 void CopyOrMoveOperationDelegate::Run() {
758   // Not supported; this should never be called.
759   NOTREACHED();
760 }
761
762 void CopyOrMoveOperationDelegate::RunRecursively() {
763   // Perform light-weight checks first.
764
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;
769   }
770
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;
777   }
778
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_);
784 }
785
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);
792   }
793
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;
814     }
815
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));
835       }
836     }
837
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));
844     }
845   }
846
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));
852 }
853
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;
868   }
869
870   if (!progress_callback_.is_null()) {
871     progress_callback_.Run(
872         FileSystemOperation::BEGIN_COPY_ENTRY, src_url, FileSystemURL(), 0);
873   }
874
875   ProcessDirectoryInternal(src_url, CreateDestURL(src_url), callback);
876 }
877
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;
885   }
886
887   operation_runner()->GetMetadata(
888       src_url,
889       base::Bind(
890           &CopyOrMoveOperationDelegate::PostProcessDirectoryAfterGetMetadata,
891           weak_factory_.GetWeakPtr(), src_url, callback));
892 }
893
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();
899 }
900
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;
909
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);
913   }
914
915   callback.Run(error);
916 }
917
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;
924   }
925   if (error != base::File::FILE_OK &&
926       error != base::File::FILE_ERROR_NOT_FOUND) {
927     callback_.Run(error);
928     return;
929   }
930
931   ProcessDirectoryInternal(src_root_, dest_root_, callback);
932 }
933
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));
946 }
947
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);
956   }
957
958   callback.Run(error);
959 }
960
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;
971   }
972
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));
979 }
980
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.
986
987   if (operation_type_ == OPERATION_COPY) {
988     callback.Run(base::File::FILE_OK);
989     return;
990   }
991
992   DCHECK_EQ(OPERATION_MOVE, operation_type_);
993
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));
1000 }
1001
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);
1008 }
1009
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);
1015   }
1016 }
1017
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());
1022
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);
1030 }
1031
1032 }  // namespace storage