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 "storage/browser/fileapi/file_system_operation_context.h"
     17 #include "storage/browser/fileapi/file_system_url.h"
     18 #include "storage/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::RunTaskWithTaskRunner,
     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::RunTaskWithTaskRunner,
     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     storage::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<storage::ShareableFileReference> file_reference =
    118       storage::ShareableFileReference::GetOrCreate(storage::ScopedFile(
    119           local_path, scope_out_policy, BrowserThread::GetBlockingPool()));
    120   callback.Run(error, file_info, local_path, file_reference);
    121 }
    122 
    123 }  // namespace
    124 
    125 AsyncFileUtil::AsyncFileUtil() {
    126 }
    127 
    128 AsyncFileUtil::~AsyncFileUtil() {
    129 }
    130 
    131 void AsyncFileUtil::CreateOrOpen(
    132     scoped_ptr<storage::FileSystemOperationContext> context,
    133     const storage::FileSystemURL& url,
    134     int file_flags,
    135     const CreateOrOpenCallback& callback) {
    136   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    137 
    138   base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
    139   if (file_path.empty()) {
    140     callback.Run(base::File(base::File::FILE_ERROR_NOT_FOUND), base::Closure());
    141     return;
    142   }
    143 
    144   const fileapi_internal::FileSystemGetter getter =
    145       base::Bind(&fileapi_internal::GetFileSystemFromUrl, url);
    146   PostFileSystemCallback(
    147       getter,
    148       base::Bind(&fileapi_internal::OpenFile,
    149                  file_path, file_flags,
    150                  google_apis::CreateRelayCallback(
    151                      base::Bind(&RunCreateOrOpenFileCallback, callback))),
    152       base::Bind(&RunCreateOrOpenFileCallbackOnError,
    153                  callback, base::File::FILE_ERROR_FAILED));
    154 }
    155 
    156 void AsyncFileUtil::EnsureFileExists(
    157     scoped_ptr<storage::FileSystemOperationContext> context,
    158     const storage::FileSystemURL& url,
    159     const EnsureFileExistsCallback& callback) {
    160   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    161 
    162   base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
    163   if (file_path.empty()) {
    164     callback.Run(base::File::FILE_ERROR_NOT_FOUND, false);
    165     return;
    166   }
    167 
    168   PostFileSystemCallback(
    169       base::Bind(&fileapi_internal::GetFileSystemFromUrl, url),
    170       base::Bind(&fileapi_internal::CreateFile,
    171                  file_path, true /* is_exlusive */,
    172                  google_apis::CreateRelayCallback(
    173                      base::Bind(&RunEnsureFileExistsCallback, callback))),
    174       base::Bind(callback, base::File::FILE_ERROR_FAILED, false));
    175 }
    176 
    177 void AsyncFileUtil::CreateDirectory(
    178     scoped_ptr<storage::FileSystemOperationContext> context,
    179     const storage::FileSystemURL& url,
    180     bool exclusive,
    181     bool recursive,
    182     const StatusCallback& callback) {
    183   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    184 
    185   base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
    186   if (file_path.empty()) {
    187     callback.Run(base::File::FILE_ERROR_NOT_FOUND);
    188     return;
    189   }
    190 
    191   PostFileSystemCallback(
    192       base::Bind(&fileapi_internal::GetFileSystemFromUrl, url),
    193       base::Bind(&fileapi_internal::CreateDirectory,
    194                  file_path, exclusive, recursive,
    195                  google_apis::CreateRelayCallback(callback)),
    196       base::Bind(callback, base::File::FILE_ERROR_FAILED));
    197 }
    198 
    199 void AsyncFileUtil::GetFileInfo(
    200     scoped_ptr<storage::FileSystemOperationContext> context,
    201     const storage::FileSystemURL& url,
    202     const GetFileInfoCallback& callback) {
    203   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    204 
    205   base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
    206   if (file_path.empty()) {
    207     callback.Run(base::File::FILE_ERROR_NOT_FOUND, base::File::Info());
    208     return;
    209   }
    210 
    211   PostFileSystemCallback(
    212       base::Bind(&fileapi_internal::GetFileSystemFromUrl, url),
    213       base::Bind(&fileapi_internal::GetFileInfo,
    214                  file_path, google_apis::CreateRelayCallback(callback)),
    215       base::Bind(callback, base::File::FILE_ERROR_FAILED,
    216                  base::File::Info()));
    217 }
    218 
    219 void AsyncFileUtil::ReadDirectory(
    220     scoped_ptr<storage::FileSystemOperationContext> context,
    221     const storage::FileSystemURL& url,
    222     const ReadDirectoryCallback& callback) {
    223   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    224 
    225   base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
    226   if (file_path.empty()) {
    227     callback.Run(base::File::FILE_ERROR_NOT_FOUND, EntryList(), false);
    228     return;
    229   }
    230 
    231   PostFileSystemCallback(
    232       base::Bind(&fileapi_internal::GetFileSystemFromUrl, url),
    233       base::Bind(&fileapi_internal::ReadDirectory,
    234                  file_path, google_apis::CreateRelayCallback(callback)),
    235       base::Bind(callback, base::File::FILE_ERROR_FAILED,
    236                  EntryList(), false));
    237 }
    238 
    239 void AsyncFileUtil::Touch(
    240     scoped_ptr<storage::FileSystemOperationContext> context,
    241     const storage::FileSystemURL& url,
    242     const base::Time& last_access_time,
    243     const base::Time& last_modified_time,
    244     const StatusCallback& callback) {
    245   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    246 
    247   base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
    248   if (file_path.empty()) {
    249     callback.Run(base::File::FILE_ERROR_NOT_FOUND);
    250     return;
    251   }
    252 
    253   PostFileSystemCallback(
    254       base::Bind(&fileapi_internal::GetFileSystemFromUrl, url),
    255       base::Bind(&fileapi_internal::TouchFile,
    256                  file_path, last_access_time, last_modified_time,
    257                  google_apis::CreateRelayCallback(callback)),
    258       base::Bind(callback, base::File::FILE_ERROR_FAILED));
    259 }
    260 
    261 void AsyncFileUtil::Truncate(
    262     scoped_ptr<storage::FileSystemOperationContext> context,
    263     const storage::FileSystemURL& url,
    264     int64 length,
    265     const StatusCallback& callback) {
    266   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    267 
    268   base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
    269   if (file_path.empty()) {
    270     callback.Run(base::File::FILE_ERROR_NOT_FOUND);
    271     return;
    272   }
    273 
    274   PostFileSystemCallback(
    275       base::Bind(&fileapi_internal::GetFileSystemFromUrl, url),
    276       base::Bind(&fileapi_internal::Truncate,
    277                  file_path, length, google_apis::CreateRelayCallback(callback)),
    278       base::Bind(callback, base::File::FILE_ERROR_FAILED));
    279 }
    280 
    281 void AsyncFileUtil::CopyFileLocal(
    282     scoped_ptr<storage::FileSystemOperationContext> context,
    283     const storage::FileSystemURL& src_url,
    284     const storage::FileSystemURL& dest_url,
    285     CopyOrMoveOption option,
    286     const CopyFileProgressCallback& progress_callback,
    287     const StatusCallback& callback) {
    288   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    289 
    290   base::FilePath src_path = util::ExtractDrivePathFromFileSystemUrl(src_url);
    291   base::FilePath dest_path = util::ExtractDrivePathFromFileSystemUrl(dest_url);
    292   if (src_path.empty() || dest_path.empty()) {
    293     callback.Run(base::File::FILE_ERROR_NOT_FOUND);
    294     return;
    295   }
    296 
    297   // TODO(kinaba): crbug.com/339794.
    298   // Assumption here is that |src_url| and |dest_url| are always from the same
    299   // profile. This indeed holds as long as we mount different profiles onto
    300   // different mount point. Hence, using GetFileSystemFromUrl(dest_url) is safe.
    301   // This will change after we introduce cross-profile sharing etc., and we
    302   // need to deal with files from different profiles here.
    303   PostFileSystemCallback(
    304       base::Bind(&fileapi_internal::GetFileSystemFromUrl, dest_url),
    305       base::Bind(
    306           &fileapi_internal::Copy,
    307           src_path,
    308           dest_path,
    309           option == storage::FileSystemOperation::OPTION_PRESERVE_LAST_MODIFIED,
    310           google_apis::CreateRelayCallback(callback)),
    311       base::Bind(callback, base::File::FILE_ERROR_FAILED));
    312 }
    313 
    314 void AsyncFileUtil::MoveFileLocal(
    315     scoped_ptr<storage::FileSystemOperationContext> context,
    316     const storage::FileSystemURL& src_url,
    317     const storage::FileSystemURL& dest_url,
    318     CopyOrMoveOption option,
    319     const StatusCallback& callback) {
    320   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    321 
    322   base::FilePath src_path = util::ExtractDrivePathFromFileSystemUrl(src_url);
    323   base::FilePath dest_path = util::ExtractDrivePathFromFileSystemUrl(dest_url);
    324   if (src_path.empty() || dest_path.empty()) {
    325     callback.Run(base::File::FILE_ERROR_NOT_FOUND);
    326     return;
    327   }
    328 
    329   // TODO(kinaba): see the comment in CopyFileLocal(). |src_url| and |dest_url|
    330   // always return the same FileSystem by GetFileSystemFromUrl, but we need to
    331   // change it in order to support cross-profile file sharing etc.
    332   PostFileSystemCallback(
    333       base::Bind(&fileapi_internal::GetFileSystemFromUrl, dest_url),
    334       base::Bind(&fileapi_internal::Move,
    335                  src_path, dest_path,
    336                  google_apis::CreateRelayCallback(callback)),
    337       base::Bind(callback, base::File::FILE_ERROR_FAILED));
    338 }
    339 
    340 void AsyncFileUtil::CopyInForeignFile(
    341     scoped_ptr<storage::FileSystemOperationContext> context,
    342     const base::FilePath& src_file_path,
    343     const storage::FileSystemURL& dest_url,
    344     const StatusCallback& callback) {
    345   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    346 
    347   base::FilePath dest_path = util::ExtractDrivePathFromFileSystemUrl(dest_url);
    348   if (dest_path.empty()) {
    349     callback.Run(base::File::FILE_ERROR_NOT_FOUND);
    350     return;
    351   }
    352 
    353   PostFileSystemCallback(
    354       base::Bind(&fileapi_internal::GetFileSystemFromUrl, dest_url),
    355       base::Bind(&fileapi_internal::CopyInForeignFile,
    356                  src_file_path, dest_path,
    357                  google_apis::CreateRelayCallback(callback)),
    358       base::Bind(callback, base::File::FILE_ERROR_FAILED));
    359 }
    360 
    361 void AsyncFileUtil::DeleteFile(
    362     scoped_ptr<storage::FileSystemOperationContext> context,
    363     const storage::FileSystemURL& url,
    364     const StatusCallback& callback) {
    365   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    366 
    367   base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
    368   if (file_path.empty()) {
    369     callback.Run(base::File::FILE_ERROR_NOT_FOUND);
    370     return;
    371   }
    372 
    373   PostFileSystemCallback(
    374       base::Bind(&fileapi_internal::GetFileSystemFromUrl, url),
    375       base::Bind(&fileapi_internal::Remove,
    376                  file_path, false /* not recursive */,
    377                  google_apis::CreateRelayCallback(callback)),
    378       base::Bind(callback, base::File::FILE_ERROR_FAILED));
    379 }
    380 
    381 void AsyncFileUtil::DeleteDirectory(
    382     scoped_ptr<storage::FileSystemOperationContext> context,
    383     const storage::FileSystemURL& url,
    384     const StatusCallback& callback) {
    385   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    386 
    387   base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
    388   if (file_path.empty()) {
    389     callback.Run(base::File::FILE_ERROR_NOT_FOUND);
    390     return;
    391   }
    392 
    393   PostFileSystemCallback(
    394       base::Bind(&fileapi_internal::GetFileSystemFromUrl, url),
    395       base::Bind(&fileapi_internal::Remove,
    396                  file_path, false /* not recursive */,
    397                  google_apis::CreateRelayCallback(callback)),
    398       base::Bind(callback, base::File::FILE_ERROR_FAILED));
    399 }
    400 
    401 void AsyncFileUtil::DeleteRecursively(
    402     scoped_ptr<storage::FileSystemOperationContext> context,
    403     const storage::FileSystemURL& url,
    404     const StatusCallback& callback) {
    405   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    406 
    407   base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
    408   if (file_path.empty()) {
    409     callback.Run(base::File::FILE_ERROR_NOT_FOUND);
    410     return;
    411   }
    412 
    413   PostFileSystemCallback(
    414       base::Bind(&fileapi_internal::GetFileSystemFromUrl, url),
    415       base::Bind(&fileapi_internal::Remove,
    416                  file_path, true /* recursive */,
    417                  google_apis::CreateRelayCallback(callback)),
    418       base::Bind(callback, base::File::FILE_ERROR_FAILED));
    419 }
    420 
    421 void AsyncFileUtil::CreateSnapshotFile(
    422     scoped_ptr<storage::FileSystemOperationContext> context,
    423     const storage::FileSystemURL& url,
    424     const CreateSnapshotFileCallback& callback) {
    425   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    426 
    427   base::FilePath file_path = util::ExtractDrivePathFromFileSystemUrl(url);
    428   if (file_path.empty()) {
    429     callback.Run(base::File::FILE_ERROR_NOT_FOUND,
    430                  base::File::Info(),
    431                  base::FilePath(),
    432                  scoped_refptr<storage::ShareableFileReference>());
    433     return;
    434   }
    435 
    436   PostFileSystemCallback(
    437       base::Bind(&fileapi_internal::GetFileSystemFromUrl, url),
    438       base::Bind(&fileapi_internal::CreateSnapshotFile,
    439                  file_path,
    440                  google_apis::CreateRelayCallback(
    441                      base::Bind(&RunCreateSnapshotFileCallback, callback))),
    442       base::Bind(callback,
    443                  base::File::FILE_ERROR_FAILED,
    444                  base::File::Info(),
    445                  base::FilePath(),
    446                  scoped_refptr<storage::ShareableFileReference>()));
    447 }
    448 
    449 }  // namespace internal
    450 }  // namespace drive
    451