Home | History | Annotate | Download | only in fileapi
      1 // Copyright 2014 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 "chrome/browser/chromeos/drive/fileapi/async_file_util.h"
      6 
      7 #include "base/callback.h"
      8 #include "base/files/file_path.h"
      9 #include "base/logging.h"
     10 #include "base/threading/sequenced_worker_pool.h"
     11 #include "chrome/browser/chromeos/drive/drive_integration_service.h"
     12 #include "chrome/browser/chromeos/drive/file_system_util.h"
     13 #include "chrome/browser/chromeos/drive/fileapi/fileapi_worker.h"
     14 #include "content/public/browser/browser_thread.h"
     15 #include "google_apis/drive/task_util.h"
     16 #include "webkit/browser/fileapi/file_system_operation_context.h"
     17 #include "webkit/browser/fileapi/file_system_url.h"
     18 #include "webkit/common/blob/shareable_file_reference.h"
     19 
     20 using content::BrowserThread;
     21 
     22 namespace google_apis {
     23 namespace internal {
     24 
     25 // Partial specialization of helper template from google_apis/drive/task_util.h
     26 // to enable google_apis::CreateRelayCallback to work with CreateOrOpenCallback.
     27 template<typename T2>
     28 struct ComposedCallback<void(base::File, T2)> {
     29   static void Run(
     30       const base::Callback<void(const base::Closure&)>& runner,
     31       const base::Callback<void(base::File, T2)>& callback,
     32       base::File arg1, T2 arg2) {
     33     runner.Run(base::Bind(callback, Passed(&arg1), arg2));
     34   }
     35 };
     36 
     37 }  // namespace internal
     38 }  // namespace google_apis
     39 
     40 namespace drive {
     41 namespace internal {
     42 namespace {
     43 
     44 // Posts fileapi_internal::RunFileSystemCallback to UI thread.
     45 // This function must be called on IO thread.
     46 // The |on_error_callback| will be called (on error case) on IO thread.
     47 void PostFileSystemCallback(
     48     const fileapi_internal::FileSystemGetter& file_system_getter,
     49     const base::Callback<void(FileSystemInterface*)>& function,
     50     const base::Closure& on_error_callback) {
     51   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     52 
     53   BrowserThread::PostTask(
     54       BrowserThread::UI,
     55       FROM_HERE,
     56       base::Bind(&fileapi_internal::RunFileSystemCallback,
     57                  file_system_getter, function,
     58                  on_error_callback.is_null() ?
     59                  base::Closure() :
     60                  base::Bind(&google_apis::RunTaskOnThread,
     61                             base::MessageLoopProxy::current(),
     62                             on_error_callback)));
     63 }
     64 
     65 // Runs CreateOrOpenFile callback based on the given |error| and |file|.
     66 void RunCreateOrOpenFileCallback(
     67     const AsyncFileUtil::CreateOrOpenCallback& callback,
     68     base::File file,
     69     const base::Closure& close_callback_on_ui_thread) {
     70   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     71 
     72   // It is necessary to make a closure, which runs on file closing here.
     73   // It will be provided as a FileSystem::OpenFileCallback's argument later.
     74   // (crbug.com/259184).
     75   callback.Run(
     76       file.Pass(),
     77       base::Bind(&google_apis::RunTaskOnThread,
     78                  BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI),
     79                  close_callback_on_ui_thread));
     80 }
     81 
     82 // Runs CreateOrOpenFile when the error happens.
     83 void RunCreateOrOpenFileCallbackOnError(
     84     const AsyncFileUtil::CreateOrOpenCallback& callback,
     85     base::File::Error error) {
     86   callback.Run(base::File(error), base::Closure());
     87 }
     88 
     89 // Runs EnsureFileExistsCallback based on the given |error|.
     90 void RunEnsureFileExistsCallback(
     91     const AsyncFileUtil::EnsureFileExistsCallback& callback,
     92     base::File::Error error) {
     93   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     94 
     95   // Remember if the file is actually created or not.
     96   bool created = (error == base::File::FILE_OK);
     97 
     98   // File::FILE_ERROR_EXISTS is not an actual error here.
     99   if (error == base::File::FILE_ERROR_EXISTS)
    100     error = base::File::FILE_OK;
    101 
    102   callback.Run(error, created);
    103 }
    104 
    105 // Runs |callback| with the arguments based on the given arguments.
    106 void RunCreateSnapshotFileCallback(
    107     const AsyncFileUtil::CreateSnapshotFileCallback& callback,
    108     base::File::Error error,
    109     const base::File::Info& file_info,
    110     const base::FilePath& local_path,
    111     webkit_blob::ScopedFile::ScopeOutPolicy scope_out_policy) {
    112   // ShareableFileReference is thread *unsafe* class. So it is necessary to
    113   // create the instance (by invoking GetOrCreate) on IO thread, though
    114   // most drive file system related operations run on UI thread.
    115   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    116 
    117   scoped_refptr<webkit_blob::ShareableFileReference> file_reference =
    118       webkit_blob::ShareableFileReference::GetOrCreate(webkit_blob::ScopedFile(
    119           local_path,
    120           scope_out_policy,
    121           BrowserThread::GetBlockingPool()));
    122   callback.Run(error, file_info, local_path, file_reference);
    123 }
    124 
    125 }  // namespace
    126 
    127 AsyncFileUtil::AsyncFileUtil() {
    128 }
    129 
    130 AsyncFileUtil::~AsyncFileUtil() {
    131 }
    132 
    133 void AsyncFileUtil::CreateOrOpen(
    134     scoped_ptr<fileapi::FileSystemOperationContext> context,
    135     const fileapi::FileSystemURL& url,
    136     int file_flags,
    137     const CreateOrOpenCallback& callback) {
    138   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    139 
    140   base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
    141   if (file_path.empty()) {
    142     callback.Run(base::File(base::File::FILE_ERROR_NOT_FOUND), base::Closure());
    143     return;
    144   }
    145 
    146   const fileapi_internal::FileSystemGetter getter =
    147       base::Bind(&fileapi_internal::GetFileSystemFromUrl, url);
    148   PostFileSystemCallback(
    149       getter,
    150       base::Bind(&fileapi_internal::OpenFile,
    151                  file_path, file_flags,
    152                  google_apis::CreateRelayCallback(
    153                      base::Bind(&RunCreateOrOpenFileCallback, callback))),
    154       base::Bind(&RunCreateOrOpenFileCallbackOnError,
    155                  callback, base::File::FILE_ERROR_FAILED));
    156 }
    157 
    158 void AsyncFileUtil::EnsureFileExists(
    159     scoped_ptr<fileapi::FileSystemOperationContext> context,
    160     const fileapi::FileSystemURL& url,
    161     const EnsureFileExistsCallback& callback) {
    162   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    163 
    164   base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
    165   if (file_path.empty()) {
    166     callback.Run(base::File::FILE_ERROR_NOT_FOUND, false);
    167     return;
    168   }
    169 
    170   PostFileSystemCallback(
    171       base::Bind(&fileapi_internal::GetFileSystemFromUrl, url),
    172       base::Bind(&fileapi_internal::CreateFile,
    173                  file_path, true /* is_exlusive */,
    174                  google_apis::CreateRelayCallback(
    175                      base::Bind(&RunEnsureFileExistsCallback, callback))),
    176       base::Bind(callback, base::File::FILE_ERROR_FAILED, false));
    177 }
    178 
    179 void AsyncFileUtil::CreateDirectory(
    180     scoped_ptr<fileapi::FileSystemOperationContext> context,
    181     const fileapi::FileSystemURL& url,
    182     bool exclusive,
    183     bool recursive,
    184     const StatusCallback& callback) {
    185   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    186 
    187   base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
    188   if (file_path.empty()) {
    189     callback.Run(base::File::FILE_ERROR_NOT_FOUND);
    190     return;
    191   }
    192 
    193   PostFileSystemCallback(
    194       base::Bind(&fileapi_internal::GetFileSystemFromUrl, url),
    195       base::Bind(&fileapi_internal::CreateDirectory,
    196                  file_path, exclusive, recursive,
    197                  google_apis::CreateRelayCallback(callback)),
    198       base::Bind(callback, base::File::FILE_ERROR_FAILED));
    199 }
    200 
    201 void AsyncFileUtil::GetFileInfo(
    202     scoped_ptr<fileapi::FileSystemOperationContext> context,
    203     const fileapi::FileSystemURL& url,
    204     const GetFileInfoCallback& callback) {
    205   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    206 
    207   base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
    208   if (file_path.empty()) {
    209     callback.Run(base::File::FILE_ERROR_NOT_FOUND, base::File::Info());
    210     return;
    211   }
    212 
    213   PostFileSystemCallback(
    214       base::Bind(&fileapi_internal::GetFileSystemFromUrl, url),
    215       base::Bind(&fileapi_internal::GetFileInfo,
    216                  file_path, google_apis::CreateRelayCallback(callback)),
    217       base::Bind(callback, base::File::FILE_ERROR_FAILED,
    218                  base::File::Info()));
    219 }
    220 
    221 void AsyncFileUtil::ReadDirectory(
    222     scoped_ptr<fileapi::FileSystemOperationContext> context,
    223     const fileapi::FileSystemURL& url,
    224     const ReadDirectoryCallback& callback) {
    225   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    226 
    227   base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
    228   if (file_path.empty()) {
    229     callback.Run(base::File::FILE_ERROR_NOT_FOUND, EntryList(), false);
    230     return;
    231   }
    232 
    233   PostFileSystemCallback(
    234       base::Bind(&fileapi_internal::GetFileSystemFromUrl, url),
    235       base::Bind(&fileapi_internal::ReadDirectory,
    236                  file_path, google_apis::CreateRelayCallback(callback)),
    237       base::Bind(callback, base::File::FILE_ERROR_FAILED,
    238                  EntryList(), false));
    239 }
    240 
    241 void AsyncFileUtil::Touch(
    242     scoped_ptr<fileapi::FileSystemOperationContext> context,
    243     const fileapi::FileSystemURL& url,
    244     const base::Time& last_access_time,
    245     const base::Time& last_modified_time,
    246     const StatusCallback& callback) {
    247   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    248 
    249   base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
    250   if (file_path.empty()) {
    251     callback.Run(base::File::FILE_ERROR_NOT_FOUND);
    252     return;
    253   }
    254 
    255   PostFileSystemCallback(
    256       base::Bind(&fileapi_internal::GetFileSystemFromUrl, url),
    257       base::Bind(&fileapi_internal::TouchFile,
    258                  file_path, last_access_time, last_modified_time,
    259                  google_apis::CreateRelayCallback(callback)),
    260       base::Bind(callback, base::File::FILE_ERROR_FAILED));
    261 }
    262 
    263 void AsyncFileUtil::Truncate(
    264     scoped_ptr<fileapi::FileSystemOperationContext> context,
    265     const fileapi::FileSystemURL& url,
    266     int64 length,
    267     const StatusCallback& callback) {
    268   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    269 
    270   base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
    271   if (file_path.empty()) {
    272     callback.Run(base::File::FILE_ERROR_NOT_FOUND);
    273     return;
    274   }
    275 
    276   PostFileSystemCallback(
    277       base::Bind(&fileapi_internal::GetFileSystemFromUrl, url),
    278       base::Bind(&fileapi_internal::Truncate,
    279                  file_path, length, google_apis::CreateRelayCallback(callback)),
    280       base::Bind(callback, base::File::FILE_ERROR_FAILED));
    281 }
    282 
    283 void AsyncFileUtil::CopyFileLocal(
    284     scoped_ptr<fileapi::FileSystemOperationContext> context,
    285     const fileapi::FileSystemURL& src_url,
    286     const fileapi::FileSystemURL& dest_url,
    287     CopyOrMoveOption option,
    288     const CopyFileProgressCallback& progress_callback,
    289     const StatusCallback& callback) {
    290   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    291 
    292   base::FilePath src_path = util::ExtractDrivePathFromFileSystemUrl(src_url);
    293   base::FilePath dest_path = util::ExtractDrivePathFromFileSystemUrl(dest_url);
    294   if (src_path.empty() || dest_path.empty()) {
    295     callback.Run(base::File::FILE_ERROR_NOT_FOUND);
    296     return;
    297   }
    298 
    299   // TODO(kinaba): crbug.com/339794.
    300   // Assumption here is that |src_url| and |dest_url| are always from the same
    301   // profile. This indeed holds as long as we mount different profiles onto
    302   // different mount point. Hence, using GetFileSystemFromUrl(dest_url) is safe.
    303   // This will change after we introduce cross-profile sharing etc., and we
    304   // need to deal with files from different profiles here.
    305   PostFileSystemCallback(
    306       base::Bind(&fileapi_internal::GetFileSystemFromUrl, dest_url),
    307       base::Bind(
    308           &fileapi_internal::Copy,
    309           src_path, dest_path,
    310           option == fileapi::FileSystemOperation::OPTION_PRESERVE_LAST_MODIFIED,
    311           google_apis::CreateRelayCallback(callback)),
    312       base::Bind(callback, base::File::FILE_ERROR_FAILED));
    313 }
    314 
    315 void AsyncFileUtil::MoveFileLocal(
    316     scoped_ptr<fileapi::FileSystemOperationContext> context,
    317     const fileapi::FileSystemURL& src_url,
    318     const fileapi::FileSystemURL& dest_url,
    319     CopyOrMoveOption option,
    320     const StatusCallback& callback) {
    321   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    322 
    323   base::FilePath src_path = util::ExtractDrivePathFromFileSystemUrl(src_url);
    324   base::FilePath dest_path = util::ExtractDrivePathFromFileSystemUrl(dest_url);
    325   if (src_path.empty() || dest_path.empty()) {
    326     callback.Run(base::File::FILE_ERROR_NOT_FOUND);
    327     return;
    328   }
    329 
    330   // TODO(kinaba): see the comment in CopyFileLocal(). |src_url| and |dest_url|
    331   // always return the same FileSystem by GetFileSystemFromUrl, but we need to
    332   // change it in order to support cross-profile file sharing etc.
    333   PostFileSystemCallback(
    334       base::Bind(&fileapi_internal::GetFileSystemFromUrl, dest_url),
    335       base::Bind(&fileapi_internal::Move,
    336                  src_path, dest_path,
    337                  google_apis::CreateRelayCallback(callback)),
    338       base::Bind(callback, base::File::FILE_ERROR_FAILED));
    339 }
    340 
    341 void AsyncFileUtil::CopyInForeignFile(
    342     scoped_ptr<fileapi::FileSystemOperationContext> context,
    343     const base::FilePath& src_file_path,
    344     const fileapi::FileSystemURL& dest_url,
    345     const StatusCallback& callback) {
    346   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    347 
    348   base::FilePath dest_path = util::ExtractDrivePathFromFileSystemUrl(dest_url);
    349   if (dest_path.empty()) {
    350     callback.Run(base::File::FILE_ERROR_NOT_FOUND);
    351     return;
    352   }
    353 
    354   PostFileSystemCallback(
    355       base::Bind(&fileapi_internal::GetFileSystemFromUrl, dest_url),
    356       base::Bind(&fileapi_internal::CopyInForeignFile,
    357                  src_file_path, dest_path,
    358                  google_apis::CreateRelayCallback(callback)),
    359       base::Bind(callback, base::File::FILE_ERROR_FAILED));
    360 }
    361 
    362 void AsyncFileUtil::DeleteFile(
    363     scoped_ptr<fileapi::FileSystemOperationContext> context,
    364     const fileapi::FileSystemURL& url,
    365     const StatusCallback& callback) {
    366   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    367 
    368   base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
    369   if (file_path.empty()) {
    370     callback.Run(base::File::FILE_ERROR_NOT_FOUND);
    371     return;
    372   }
    373 
    374   PostFileSystemCallback(
    375       base::Bind(&fileapi_internal::GetFileSystemFromUrl, url),
    376       base::Bind(&fileapi_internal::Remove,
    377                  file_path, false /* not recursive */,
    378                  google_apis::CreateRelayCallback(callback)),
    379       base::Bind(callback, base::File::FILE_ERROR_FAILED));
    380 }
    381 
    382 void AsyncFileUtil::DeleteDirectory(
    383     scoped_ptr<fileapi::FileSystemOperationContext> context,
    384     const fileapi::FileSystemURL& url,
    385     const StatusCallback& callback) {
    386   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    387 
    388   base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
    389   if (file_path.empty()) {
    390     callback.Run(base::File::FILE_ERROR_NOT_FOUND);
    391     return;
    392   }
    393 
    394   PostFileSystemCallback(
    395       base::Bind(&fileapi_internal::GetFileSystemFromUrl, url),
    396       base::Bind(&fileapi_internal::Remove,
    397                  file_path, false /* not recursive */,
    398                  google_apis::CreateRelayCallback(callback)),
    399       base::Bind(callback, base::File::FILE_ERROR_FAILED));
    400 }
    401 
    402 void AsyncFileUtil::DeleteRecursively(
    403     scoped_ptr<fileapi::FileSystemOperationContext> context,
    404     const fileapi::FileSystemURL& url,
    405     const StatusCallback& callback) {
    406   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    407 
    408   base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
    409   if (file_path.empty()) {
    410     callback.Run(base::File::FILE_ERROR_NOT_FOUND);
    411     return;
    412   }
    413 
    414   PostFileSystemCallback(
    415       base::Bind(&fileapi_internal::GetFileSystemFromUrl, url),
    416       base::Bind(&fileapi_internal::Remove,
    417                  file_path, true /* recursive */,
    418                  google_apis::CreateRelayCallback(callback)),
    419       base::Bind(callback, base::File::FILE_ERROR_FAILED));
    420 }
    421 
    422 void AsyncFileUtil::CreateSnapshotFile(
    423     scoped_ptr<fileapi::FileSystemOperationContext> context,
    424     const fileapi::FileSystemURL& url,
    425     const CreateSnapshotFileCallback& callback) {
    426   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    427 
    428   base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
    429   if (file_path.empty()) {
    430     callback.Run(base::File::FILE_ERROR_NOT_FOUND,
    431                  base::File::Info(),
    432                  base::FilePath(),
    433                  scoped_refptr<webkit_blob::ShareableFileReference>());
    434     return;
    435   }
    436 
    437   PostFileSystemCallback(
    438       base::Bind(&fileapi_internal::GetFileSystemFromUrl, url),
    439       base::Bind(&fileapi_internal::CreateSnapshotFile,
    440                  file_path,
    441                  google_apis::CreateRelayCallback(
    442                      base::Bind(&RunCreateSnapshotFileCallback, callback))),
    443       base::Bind(callback,
    444                  base::File::FILE_ERROR_FAILED,
    445                  base::File::Info(),
    446                  base::FilePath(),
    447                  scoped_refptr<webkit_blob::ShareableFileReference>()));
    448 }
    449 
    450 }  // namespace internal
    451 }  // namespace drive
    452