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