Home | History | Annotate | Download | only in fileapi
      1 // Copyright 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/file_system_operation_impl.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/single_thread_task_runner.h"
      9 #include "base/strings/utf_string_conversions.h"
     10 #include "base/time/time.h"
     11 #include "net/base/escape.h"
     12 #include "net/url_request/url_request.h"
     13 #include "webkit/browser/fileapi/async_file_util.h"
     14 #include "webkit/browser/fileapi/copy_or_move_operation_delegate.h"
     15 #include "webkit/browser/fileapi/file_observers.h"
     16 #include "webkit/browser/fileapi/file_system_backend.h"
     17 #include "webkit/browser/fileapi/file_system_context.h"
     18 #include "webkit/browser/fileapi/file_system_file_util.h"
     19 #include "webkit/browser/fileapi/file_system_operation_context.h"
     20 #include "webkit/browser/fileapi/file_system_url.h"
     21 #include "webkit/browser/fileapi/file_writer_delegate.h"
     22 #include "webkit/browser/fileapi/remove_operation_delegate.h"
     23 #include "webkit/browser/fileapi/sandbox_file_stream_writer.h"
     24 #include "webkit/browser/quota/quota_manager.h"
     25 #include "webkit/common/blob/shareable_file_reference.h"
     26 #include "webkit/common/fileapi/file_system_types.h"
     27 #include "webkit/common/fileapi/file_system_util.h"
     28 #include "webkit/common/quota/quota_types.h"
     29 
     30 using webkit_blob::ScopedFile;
     31 
     32 namespace fileapi {
     33 
     34 FileSystemOperationImpl::FileSystemOperationImpl(
     35     const FileSystemURL& url,
     36     FileSystemContext* file_system_context,
     37     scoped_ptr<FileSystemOperationContext> operation_context)
     38     : file_system_context_(file_system_context),
     39       operation_context_(operation_context.Pass()),
     40       async_file_util_(NULL),
     41       peer_handle_(base::kNullProcessHandle),
     42       pending_operation_(kOperationNone) {
     43   DCHECK(operation_context_.get());
     44   operation_context_->DetachUserDataThread();
     45   async_file_util_ = file_system_context_->GetAsyncFileUtil(url.type());
     46   DCHECK(async_file_util_);
     47 }
     48 
     49 FileSystemOperationImpl::~FileSystemOperationImpl() {
     50 }
     51 
     52 void FileSystemOperationImpl::CreateFile(const FileSystemURL& url,
     53                                          bool exclusive,
     54                                          const StatusCallback& callback) {
     55   DCHECK(SetPendingOperationType(kOperationCreateFile));
     56   GetUsageAndQuotaThenRunTask(
     57       url,
     58       base::Bind(&FileSystemOperationImpl::DoCreateFile,
     59                  AsWeakPtr(), url, callback, exclusive),
     60       base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
     61 }
     62 
     63 void FileSystemOperationImpl::CreateDirectory(const FileSystemURL& url,
     64                                               bool exclusive,
     65                                               bool recursive,
     66                                               const StatusCallback& callback) {
     67   DCHECK(SetPendingOperationType(kOperationCreateDirectory));
     68   GetUsageAndQuotaThenRunTask(
     69       url,
     70       base::Bind(&FileSystemOperationImpl::DoCreateDirectory,
     71                  AsWeakPtr(), url, callback, exclusive, recursive),
     72       base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
     73 }
     74 
     75 void FileSystemOperationImpl::Copy(const FileSystemURL& src_url,
     76                                    const FileSystemURL& dest_url,
     77                                    const StatusCallback& callback) {
     78   DCHECK(SetPendingOperationType(kOperationCopy));
     79   DCHECK(!recursive_operation_delegate_);
     80   recursive_operation_delegate_.reset(
     81       new CopyOrMoveOperationDelegate(
     82           file_system_context(),
     83           src_url, dest_url,
     84           CopyOrMoveOperationDelegate::OPERATION_COPY,
     85           base::Bind(&FileSystemOperationImpl::DidFinishOperation,
     86                      AsWeakPtr(), callback)));
     87   recursive_operation_delegate_->RunRecursively();
     88 }
     89 
     90 void FileSystemOperationImpl::Move(const FileSystemURL& src_url,
     91                                    const FileSystemURL& dest_url,
     92                                    const StatusCallback& callback) {
     93   DCHECK(SetPendingOperationType(kOperationMove));
     94   DCHECK(!recursive_operation_delegate_);
     95   recursive_operation_delegate_.reset(
     96       new CopyOrMoveOperationDelegate(
     97           file_system_context(),
     98           src_url, dest_url,
     99           CopyOrMoveOperationDelegate::OPERATION_MOVE,
    100           base::Bind(&FileSystemOperationImpl::DidFinishOperation,
    101                      AsWeakPtr(), callback)));
    102   recursive_operation_delegate_->RunRecursively();
    103 }
    104 
    105 void FileSystemOperationImpl::DirectoryExists(const FileSystemURL& url,
    106                                               const StatusCallback& callback) {
    107   DCHECK(SetPendingOperationType(kOperationDirectoryExists));
    108   async_file_util_->GetFileInfo(
    109       operation_context_.Pass(), url,
    110       base::Bind(&FileSystemOperationImpl::DidDirectoryExists,
    111                  AsWeakPtr(), callback));
    112 }
    113 
    114 void FileSystemOperationImpl::FileExists(const FileSystemURL& url,
    115                                          const StatusCallback& callback) {
    116   DCHECK(SetPendingOperationType(kOperationFileExists));
    117   async_file_util_->GetFileInfo(
    118       operation_context_.Pass(), url,
    119       base::Bind(&FileSystemOperationImpl::DidFileExists,
    120                  AsWeakPtr(), callback));
    121 }
    122 
    123 void FileSystemOperationImpl::GetMetadata(
    124     const FileSystemURL& url, const GetMetadataCallback& callback) {
    125   DCHECK(SetPendingOperationType(kOperationGetMetadata));
    126   async_file_util_->GetFileInfo(operation_context_.Pass(), url, callback);
    127 }
    128 
    129 void FileSystemOperationImpl::ReadDirectory(
    130     const FileSystemURL& url, const ReadDirectoryCallback& callback) {
    131   DCHECK(SetPendingOperationType(kOperationReadDirectory));
    132   async_file_util_->ReadDirectory(
    133       operation_context_.Pass(), url, callback);
    134 }
    135 
    136 void FileSystemOperationImpl::Remove(const FileSystemURL& url,
    137                                      bool recursive,
    138                                      const StatusCallback& callback) {
    139   DCHECK(SetPendingOperationType(kOperationRemove));
    140   DCHECK(!recursive_operation_delegate_);
    141 
    142   if (recursive) {
    143     // For recursive removal, try to delegate the operation to AsyncFileUtil
    144     // first. If not supported, it is delegated to RemoveOperationDelegate
    145     // in DidDeleteRecursively.
    146     async_file_util_->DeleteRecursively(
    147         operation_context_.Pass(), url,
    148         base::Bind(&FileSystemOperationImpl::DidDeleteRecursively,
    149                    AsWeakPtr(), url, callback));
    150     return;
    151   }
    152 
    153   recursive_operation_delegate_.reset(
    154       new RemoveOperationDelegate(
    155           file_system_context(), url,
    156           base::Bind(&FileSystemOperationImpl::DidFinishOperation,
    157                      AsWeakPtr(), callback)));
    158   recursive_operation_delegate_->Run();
    159 }
    160 
    161 void FileSystemOperationImpl::Write(
    162     const FileSystemURL& url,
    163     scoped_ptr<FileWriterDelegate> writer_delegate,
    164     scoped_ptr<net::URLRequest> blob_request,
    165     const WriteCallback& callback) {
    166   DCHECK(SetPendingOperationType(kOperationWrite));
    167   file_writer_delegate_ = writer_delegate.Pass();
    168   file_writer_delegate_->Start(
    169       blob_request.Pass(),
    170       base::Bind(&FileSystemOperationImpl::DidWrite, AsWeakPtr(),
    171                  url, callback));
    172 }
    173 
    174 void FileSystemOperationImpl::Truncate(const FileSystemURL& url, int64 length,
    175                                        const StatusCallback& callback) {
    176   DCHECK(SetPendingOperationType(kOperationTruncate));
    177   GetUsageAndQuotaThenRunTask(
    178       url,
    179       base::Bind(&FileSystemOperationImpl::DoTruncate,
    180                  AsWeakPtr(), url, callback, length),
    181       base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
    182 }
    183 
    184 void FileSystemOperationImpl::TouchFile(const FileSystemURL& url,
    185                                         const base::Time& last_access_time,
    186                                         const base::Time& last_modified_time,
    187                                         const StatusCallback& callback) {
    188   DCHECK(SetPendingOperationType(kOperationTouchFile));
    189   async_file_util_->Touch(
    190       operation_context_.Pass(), url,
    191       last_access_time, last_modified_time,
    192       base::Bind(&FileSystemOperationImpl::DidFinishOperation,
    193                  AsWeakPtr(), callback));
    194 }
    195 
    196 void FileSystemOperationImpl::OpenFile(const FileSystemURL& url,
    197                                        int file_flags,
    198                                        base::ProcessHandle peer_handle,
    199                                        const OpenFileCallback& callback) {
    200   DCHECK(SetPendingOperationType(kOperationOpenFile));
    201   peer_handle_ = peer_handle;
    202 
    203   if (file_flags & (
    204       (base::PLATFORM_FILE_ENUMERATE | base::PLATFORM_FILE_TEMPORARY |
    205        base::PLATFORM_FILE_HIDDEN))) {
    206     callback.Run(base::PLATFORM_FILE_ERROR_FAILED,
    207                  base::kInvalidPlatformFileValue,
    208                  base::Closure(),
    209                  base::kNullProcessHandle);
    210     return;
    211   }
    212   GetUsageAndQuotaThenRunTask(
    213       url,
    214       base::Bind(&FileSystemOperationImpl::DoOpenFile,
    215                  AsWeakPtr(),
    216                  url, callback, file_flags),
    217       base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED,
    218                  base::kInvalidPlatformFileValue,
    219                  base::Closure(),
    220                  base::kNullProcessHandle));
    221 }
    222 
    223 // We can only get here on a write or truncate that's not yet completed.
    224 // We don't support cancelling any other operation at this time.
    225 void FileSystemOperationImpl::Cancel(const StatusCallback& cancel_callback) {
    226   DCHECK(cancel_callback_.is_null());
    227   cancel_callback_ = cancel_callback;
    228 
    229   if (file_writer_delegate_.get()) {
    230     DCHECK_EQ(kOperationWrite, pending_operation_);
    231     // This will call DidWrite() with ABORT status code.
    232     file_writer_delegate_->Cancel();
    233   } else {
    234     // For truncate we have no way to cancel the inflight operation (for now).
    235     // Let it just run and dispatch cancel callback later.
    236     DCHECK_EQ(kOperationTruncate, pending_operation_);
    237   }
    238 }
    239 
    240 FileSystemOperationImpl* FileSystemOperationImpl::AsFileSystemOperationImpl() {
    241   return this;
    242 }
    243 
    244 base::PlatformFileError FileSystemOperationImpl::SyncGetPlatformPath(
    245     const FileSystemURL& url,
    246     base::FilePath* platform_path) {
    247   DCHECK(SetPendingOperationType(kOperationGetLocalPath));
    248   FileSystemFileUtil* file_util = file_system_context()->GetFileUtil(
    249       url.type());
    250   if (!file_util)
    251     return base::PLATFORM_FILE_ERROR_INVALID_OPERATION;
    252   file_util->GetLocalFilePath(operation_context_.get(), url, platform_path);
    253   return base::PLATFORM_FILE_OK;
    254 }
    255 
    256 void FileSystemOperationImpl::CreateSnapshotFile(
    257     const FileSystemURL& url,
    258     const SnapshotFileCallback& callback) {
    259   DCHECK(SetPendingOperationType(kOperationCreateSnapshotFile));
    260   async_file_util_->CreateSnapshotFile(
    261       operation_context_.Pass(), url, callback);
    262 }
    263 
    264 void FileSystemOperationImpl::CopyInForeignFile(
    265     const base::FilePath& src_local_disk_file_path,
    266     const FileSystemURL& dest_url,
    267     const StatusCallback& callback) {
    268   DCHECK(SetPendingOperationType(kOperationCopyInForeignFile));
    269   GetUsageAndQuotaThenRunTask(
    270       dest_url,
    271       base::Bind(&FileSystemOperationImpl::DoCopyInForeignFile,
    272                  AsWeakPtr(), src_local_disk_file_path, dest_url,
    273                  callback),
    274       base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
    275 }
    276 
    277 void FileSystemOperationImpl::RemoveFile(
    278     const FileSystemURL& url,
    279     const StatusCallback& callback) {
    280   DCHECK(SetPendingOperationType(kOperationRemove));
    281   async_file_util_->DeleteFile(
    282       operation_context_.Pass(), url,
    283       base::Bind(&FileSystemOperationImpl::DidFinishOperation,
    284                  AsWeakPtr(), callback));
    285 }
    286 
    287 void FileSystemOperationImpl::RemoveDirectory(
    288     const FileSystemURL& url,
    289     const StatusCallback& callback) {
    290   DCHECK(SetPendingOperationType(kOperationRemove));
    291   async_file_util_->DeleteDirectory(
    292       operation_context_.Pass(), url,
    293       base::Bind(&FileSystemOperationImpl::DidFinishOperation,
    294                  AsWeakPtr(), callback));
    295 }
    296 
    297 void FileSystemOperationImpl::CopyFileLocal(
    298     const FileSystemURL& src_url,
    299     const FileSystemURL& dest_url,
    300     const StatusCallback& callback) {
    301   DCHECK(SetPendingOperationType(kOperationCopy));
    302   DCHECK(src_url.IsInSameFileSystem(dest_url));
    303   GetUsageAndQuotaThenRunTask(
    304       dest_url,
    305       base::Bind(&FileSystemOperationImpl::DoCopyFileLocal,
    306                  AsWeakPtr(), src_url, dest_url, callback),
    307       base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
    308 }
    309 
    310 void FileSystemOperationImpl::MoveFileLocal(
    311     const FileSystemURL& src_url,
    312     const FileSystemURL& dest_url,
    313     const StatusCallback& callback) {
    314   DCHECK(SetPendingOperationType(kOperationMove));
    315   DCHECK(src_url.IsInSameFileSystem(dest_url));
    316   GetUsageAndQuotaThenRunTask(
    317       dest_url,
    318       base::Bind(&FileSystemOperationImpl::DoMoveFileLocal,
    319                  AsWeakPtr(), src_url, dest_url, callback),
    320       base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED));
    321 }
    322 
    323 void FileSystemOperationImpl::GetUsageAndQuotaThenRunTask(
    324     const FileSystemURL& url,
    325     const base::Closure& task,
    326     const base::Closure& error_callback) {
    327   quota::QuotaManagerProxy* quota_manager_proxy =
    328       file_system_context()->quota_manager_proxy();
    329   if (!quota_manager_proxy ||
    330       !file_system_context()->GetQuotaUtil(url.type())) {
    331     // If we don't have the quota manager or the requested filesystem type
    332     // does not support quota, we should be able to let it go.
    333     operation_context_->set_allowed_bytes_growth(kint64max);
    334     task.Run();
    335     return;
    336   }
    337 
    338   DCHECK(quota_manager_proxy);
    339   DCHECK(quota_manager_proxy->quota_manager());
    340   quota_manager_proxy->quota_manager()->GetUsageAndQuota(
    341       url.origin(),
    342       FileSystemTypeToQuotaStorageType(url.type()),
    343       base::Bind(&FileSystemOperationImpl::DidGetUsageAndQuotaAndRunTask,
    344                  AsWeakPtr(), task, error_callback));
    345 }
    346 
    347 void FileSystemOperationImpl::DidGetUsageAndQuotaAndRunTask(
    348     const base::Closure& task,
    349     const base::Closure& error_callback,
    350     quota::QuotaStatusCode status,
    351     int64 usage, int64 quota) {
    352   if (status != quota::kQuotaStatusOk) {
    353     LOG(WARNING) << "Got unexpected quota error : " << status;
    354     error_callback.Run();
    355     return;
    356   }
    357 
    358   operation_context_->set_allowed_bytes_growth(quota - usage);
    359   task.Run();
    360 }
    361 
    362 void FileSystemOperationImpl::DoCreateFile(
    363     const FileSystemURL& url,
    364     const StatusCallback& callback,
    365     bool exclusive) {
    366   async_file_util_->EnsureFileExists(
    367       operation_context_.Pass(), url,
    368       base::Bind(
    369           exclusive ?
    370               &FileSystemOperationImpl::DidEnsureFileExistsExclusive :
    371               &FileSystemOperationImpl::DidEnsureFileExistsNonExclusive,
    372           AsWeakPtr(), callback));
    373 }
    374 
    375 void FileSystemOperationImpl::DoCreateDirectory(
    376     const FileSystemURL& url,
    377     const StatusCallback& callback,
    378     bool exclusive, bool recursive) {
    379   async_file_util_->CreateDirectory(
    380       operation_context_.Pass(),
    381       url, exclusive, recursive,
    382       base::Bind(&FileSystemOperationImpl::DidFinishOperation,
    383                  AsWeakPtr(), callback));
    384 }
    385 
    386 void FileSystemOperationImpl::DoCopyFileLocal(
    387     const FileSystemURL& src_url,
    388     const FileSystemURL& dest_url,
    389     const StatusCallback& callback) {
    390   async_file_util_->CopyFileLocal(
    391       operation_context_.Pass(), src_url, dest_url,
    392       base::Bind(&FileSystemOperationImpl::DidFinishOperation,
    393                  AsWeakPtr(), callback));
    394 }
    395 
    396 void FileSystemOperationImpl::DoMoveFileLocal(
    397     const FileSystemURL& src_url,
    398     const FileSystemURL& dest_url,
    399     const StatusCallback& callback) {
    400   async_file_util_->MoveFileLocal(
    401       operation_context_.Pass(), src_url, dest_url,
    402       base::Bind(&FileSystemOperationImpl::DidFinishOperation,
    403                  AsWeakPtr(), callback));
    404 }
    405 
    406 void FileSystemOperationImpl::DoCopyInForeignFile(
    407     const base::FilePath& src_local_disk_file_path,
    408     const FileSystemURL& dest_url,
    409     const StatusCallback& callback) {
    410   async_file_util_->CopyInForeignFile(
    411       operation_context_.Pass(),
    412       src_local_disk_file_path, dest_url,
    413       base::Bind(&FileSystemOperationImpl::DidFinishOperation,
    414                  AsWeakPtr(), callback));
    415 }
    416 
    417 void FileSystemOperationImpl::DoTruncate(const FileSystemURL& url,
    418                                          const StatusCallback& callback,
    419                                          int64 length) {
    420   async_file_util_->Truncate(
    421       operation_context_.Pass(), url, length,
    422       base::Bind(&FileSystemOperationImpl::DidFinishOperation,
    423                  AsWeakPtr(), callback));
    424 }
    425 
    426 void FileSystemOperationImpl::DoOpenFile(const FileSystemURL& url,
    427                                          const OpenFileCallback& callback,
    428                                          int file_flags) {
    429   async_file_util_->CreateOrOpen(
    430       operation_context_.Pass(), url, file_flags,
    431       base::Bind(&FileSystemOperationImpl::DidOpenFile,
    432                  AsWeakPtr(), callback));
    433 }
    434 
    435 void FileSystemOperationImpl::DidEnsureFileExistsExclusive(
    436     const StatusCallback& callback,
    437     base::PlatformFileError rv, bool created) {
    438   if (rv == base::PLATFORM_FILE_OK && !created) {
    439     callback.Run(base::PLATFORM_FILE_ERROR_EXISTS);
    440   } else {
    441     DidFinishOperation(callback, rv);
    442   }
    443 }
    444 
    445 void FileSystemOperationImpl::DidEnsureFileExistsNonExclusive(
    446     const StatusCallback& callback,
    447     base::PlatformFileError rv, bool /* created */) {
    448   DidFinishOperation(callback, rv);
    449 }
    450 
    451 void FileSystemOperationImpl::DidFinishOperation(
    452     const StatusCallback& callback,
    453     base::PlatformFileError rv) {
    454   if (!cancel_callback_.is_null()) {
    455     DCHECK_EQ(kOperationTruncate, pending_operation_);
    456 
    457     StatusCallback cancel_callback = cancel_callback_;
    458     callback.Run(base::PLATFORM_FILE_ERROR_ABORT);
    459     cancel_callback.Run(base::PLATFORM_FILE_OK);
    460   } else {
    461     callback.Run(rv);
    462   }
    463 }
    464 
    465 void FileSystemOperationImpl::DidDirectoryExists(
    466     const StatusCallback& callback,
    467     base::PlatformFileError rv,
    468     const base::PlatformFileInfo& file_info) {
    469   if (rv == base::PLATFORM_FILE_OK && !file_info.is_directory)
    470     rv = base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY;
    471   callback.Run(rv);
    472 }
    473 
    474 void FileSystemOperationImpl::DidFileExists(
    475     const StatusCallback& callback,
    476     base::PlatformFileError rv,
    477     const base::PlatformFileInfo& file_info) {
    478   if (rv == base::PLATFORM_FILE_OK && file_info.is_directory)
    479     rv = base::PLATFORM_FILE_ERROR_NOT_A_FILE;
    480   callback.Run(rv);
    481 }
    482 
    483 void FileSystemOperationImpl::DidDeleteRecursively(
    484     const FileSystemURL& url,
    485     const StatusCallback& callback,
    486     base::PlatformFileError rv) {
    487   if (rv == base::PLATFORM_FILE_ERROR_INVALID_OPERATION) {
    488     // Recursive removal is not supported on this platform.
    489     DCHECK(!recursive_operation_delegate_);
    490     recursive_operation_delegate_.reset(
    491         new RemoveOperationDelegate(
    492             file_system_context(), url,
    493             base::Bind(&FileSystemOperationImpl::DidFinishOperation,
    494                        AsWeakPtr(), callback)));
    495     recursive_operation_delegate_->RunRecursively();
    496     return;
    497   }
    498 
    499   callback.Run(rv);
    500 }
    501 
    502 void FileSystemOperationImpl::DidWrite(
    503     const FileSystemURL& url,
    504     const WriteCallback& write_callback,
    505     base::PlatformFileError rv,
    506     int64 bytes,
    507     FileWriterDelegate::WriteProgressStatus write_status) {
    508   const bool complete = (
    509       write_status != FileWriterDelegate::SUCCESS_IO_PENDING);
    510   if (complete && write_status != FileWriterDelegate::ERROR_WRITE_NOT_STARTED) {
    511     DCHECK(operation_context_);
    512     operation_context_->change_observers()->Notify(
    513         &FileChangeObserver::OnModifyFile, MakeTuple(url));
    514   }
    515 
    516   StatusCallback cancel_callback = cancel_callback_;
    517   write_callback.Run(rv, bytes, complete);
    518   if (!cancel_callback.is_null())
    519     cancel_callback.Run(base::PLATFORM_FILE_OK);
    520 }
    521 
    522 void FileSystemOperationImpl::DidOpenFile(
    523     const OpenFileCallback& callback,
    524     base::PlatformFileError rv,
    525     base::PassPlatformFile file,
    526     const base::Closure& on_close_callback) {
    527   if (rv == base::PLATFORM_FILE_OK)
    528     CHECK_NE(base::kNullProcessHandle, peer_handle_);
    529   callback.Run(rv, file.ReleaseValue(), on_close_callback, peer_handle_);
    530 }
    531 
    532 bool FileSystemOperationImpl::SetPendingOperationType(OperationType type) {
    533   if (pending_operation_ != kOperationNone)
    534     return false;
    535   pending_operation_ = type;
    536   return true;
    537 }
    538 
    539 }  // namespace fileapi
    540