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