Home | History | Annotate | Download | only in fileapi
      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
   1035