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/fileapi_worker.h"
      6 
      7 #include "base/files/file_path.h"
      8 #include "base/logging.h"
      9 #include "base/task_runner_util.h"
     10 #include "base/threading/sequenced_worker_pool.h"
     11 #include "chrome/browser/chromeos/drive/drive.pb.h"
     12 #include "chrome/browser/chromeos/drive/file_errors.h"
     13 #include "chrome/browser/chromeos/drive/file_system_interface.h"
     14 #include "chrome/browser/chromeos/drive/file_system_util.h"
     15 #include "chrome/browser/chromeos/drive/resource_entry_conversion.h"
     16 #include "content/public/browser/browser_thread.h"
     17 #include "storage/browser/fileapi/file_system_url.h"
     18 #include "storage/common/fileapi/directory_entry.h"
     19 
     20 using content::BrowserThread;
     21 
     22 namespace drive {
     23 namespace fileapi_internal {
     24 namespace {
     25 
     26 // The summary of opening mode is:
     27 // - File::FLAG_OPEN: Open the existing file. Fail if not exists.
     28 // - File::FLAG_CREATE: Create the file if not exists. Fail if exists.
     29 // - File::FLAG_OPEN_ALWAYS: Open the existing file. Create a new file
     30 //     if not exists.
     31 // - File::FLAG_CREATE_ALWAYS: Create a new file if not exists. If exists
     32 //     open it with truncate.
     33 // - File::FLAG_OPEN_TRUNCATE: Open the existing file with truncate.
     34 //     Fail if not exists.
     35 OpenMode GetOpenMode(int file_flag) {
     36   if (file_flag & (base::File::FLAG_OPEN | base::File::FLAG_OPEN_TRUNCATED))
     37     return OPEN_FILE;
     38 
     39   if (file_flag & base::File::FLAG_CREATE)
     40     return CREATE_FILE;
     41 
     42   DCHECK(file_flag &
     43          (base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_CREATE_ALWAYS));
     44   return OPEN_OR_CREATE_FILE;
     45 }
     46 
     47 // Runs |callback| with the File::Error converted from |error|.
     48 void RunStatusCallbackByFileError(const StatusCallback& callback,
     49                                   FileError error) {
     50   callback.Run(FileErrorToBaseFileError(error));
     51 }
     52 
     53 // Runs |callback| with arguments converted from |error| and |entry|.
     54 void RunGetFileInfoCallback(const GetFileInfoCallback& callback,
     55                             FileError error,
     56                             scoped_ptr<ResourceEntry> entry) {
     57   if (error != FILE_ERROR_OK) {
     58     callback.Run(FileErrorToBaseFileError(error), base::File::Info());
     59     return;
     60   }
     61 
     62   DCHECK(entry);
     63   base::File::Info file_info;
     64   ConvertResourceEntryToFileInfo(*entry, &file_info);
     65   callback.Run(base::File::FILE_OK, file_info);
     66 }
     67 
     68 // Runs |callback| with entries.
     69 void RunReadDirectoryCallbackWithEntries(
     70     const ReadDirectoryCallback& callback,
     71     scoped_ptr<ResourceEntryVector> resource_entries) {
     72   DCHECK(resource_entries);
     73 
     74   std::vector<storage::DirectoryEntry> entries;
     75   // Convert drive files to File API's directory entry.
     76   entries.reserve(resource_entries->size());
     77   for (size_t i = 0; i < resource_entries->size(); ++i) {
     78     const ResourceEntry& resource_entry = (*resource_entries)[i];
     79     storage::DirectoryEntry entry;
     80     entry.name = resource_entry.base_name();
     81 
     82     const PlatformFileInfoProto& file_info = resource_entry.file_info();
     83     entry.is_directory = file_info.is_directory();
     84     entry.size = file_info.size();
     85     entry.last_modified_time =
     86         base::Time::FromInternalValue(file_info.last_modified());
     87     entries.push_back(entry);
     88   }
     89 
     90   callback.Run(base::File::FILE_OK, entries, true /*has_more*/);
     91 }
     92 
     93 // Runs |callback| with |error|.
     94 void RunReadDirectoryCallbackOnCompletion(const ReadDirectoryCallback& callback,
     95                                           FileError error) {
     96   callback.Run(FileErrorToBaseFileError(error),
     97                std::vector<storage::DirectoryEntry>(),
     98                false /*has_more*/);
     99 }
    100 
    101 // Runs |callback| with arguments based on |error|, |local_path| and |entry|.
    102 void RunCreateSnapshotFileCallback(const CreateSnapshotFileCallback& callback,
    103                                    FileError error,
    104                                    const base::FilePath& local_path,
    105                                    scoped_ptr<ResourceEntry> entry) {
    106   if (error != FILE_ERROR_OK) {
    107     callback.Run(FileErrorToBaseFileError(error),
    108                  base::File::Info(),
    109                  base::FilePath(),
    110                  storage::ScopedFile::ScopeOutPolicy());
    111     return;
    112   }
    113 
    114   DCHECK(entry);
    115 
    116   // When reading file, last modified time specified in file info will be
    117   // compared to the last modified time of the local version of the drive file.
    118   // Since those two values don't generally match (last modification time on the
    119   // drive server vs. last modification time of the local, downloaded file), so
    120   // we have to opt out from this check. We do this by unsetting last_modified
    121   // value in the file info passed to the CreateSnapshot caller.
    122   base::File::Info file_info;
    123   ConvertResourceEntryToFileInfo(*entry, &file_info);
    124   file_info.last_modified = base::Time();
    125 
    126   // If the file is a hosted document, a temporary JSON file is created to
    127   // represent the document. The JSON file is not cached and its lifetime
    128   // is managed by ShareableFileReference.
    129   storage::ScopedFile::ScopeOutPolicy scope_out_policy =
    130       entry->file_specific_info().is_hosted_document()
    131           ? storage::ScopedFile::DELETE_ON_SCOPE_OUT
    132           : storage::ScopedFile::DONT_DELETE_ON_SCOPE_OUT;
    133 
    134   callback.Run(base::File::FILE_OK, file_info, local_path, scope_out_policy);
    135 }
    136 
    137 // Runs |callback| with arguments converted from |error| and |local_path|.
    138 void RunCreateWritableSnapshotFileCallback(
    139     const CreateWritableSnapshotFileCallback& callback,
    140     FileError error,
    141     const base::FilePath& local_path,
    142     const base::Closure& close_callback) {
    143   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    144   callback.Run(FileErrorToBaseFileError(error), local_path, close_callback);
    145 }
    146 
    147 // Runs |callback| with |file|.
    148 void RunOpenFileCallback(const OpenFileCallback& callback,
    149                          const base::Closure& close_callback,
    150                          base::File file) {
    151   callback.Run(file.Pass(), close_callback);
    152 }
    153 
    154 base::File OpenFile(const base::FilePath& path, int flags) {
    155   return base::File(path, flags);
    156 }
    157 
    158 // Part of OpenFile(). Called after FileSystem::OpenFile().
    159 void OpenFileAfterFileSystemOpenFile(int file_flags,
    160                                      const OpenFileCallback& callback,
    161                                      FileError error,
    162                                      const base::FilePath& local_path,
    163                                      const base::Closure& close_callback) {
    164   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    165 
    166   if (error != FILE_ERROR_OK) {
    167     callback.Run(base::File(FileErrorToBaseFileError(error)), base::Closure());
    168     return;
    169   }
    170 
    171   // Here, the file should be at |local_path|, but there may be timing issue.
    172   // Because the file is managed by Drive file system, so, in order to avoid
    173   // unexpected file creation, CREATE, OPEN_ALWAYS and CREATE_ALWAYS are
    174   // translated into OPEN or OPEN_TRUNCATED, here. Keep OPEN and OPEN_TRUNCATED
    175   // as is.
    176   if (file_flags & (base::File::FLAG_CREATE |
    177                     base::File::FLAG_OPEN_ALWAYS)) {
    178     file_flags &= ~(base::File::FLAG_CREATE |
    179                     base::File::FLAG_OPEN_ALWAYS);
    180     file_flags |= base::File::FLAG_OPEN;
    181   } else if (file_flags & base::File::FLAG_CREATE_ALWAYS) {
    182     file_flags &= ~base::File::FLAG_CREATE_ALWAYS;
    183     file_flags |= base::File::FLAG_OPEN_TRUNCATED;
    184   }
    185 
    186   // Cache file prepared for modification is available. Open it locally.
    187   bool posted = base::PostTaskAndReplyWithResult(
    188       BrowserThread::GetBlockingPool(), FROM_HERE,
    189       base::Bind(&OpenFile, local_path, file_flags),
    190       base::Bind(&RunOpenFileCallback, callback, close_callback));
    191   DCHECK(posted);
    192 }
    193 
    194 }  // namespace
    195 
    196 FileSystemInterface* GetFileSystemFromUrl(const storage::FileSystemURL& url) {
    197   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    198 
    199   Profile* profile = util::ExtractProfileFromPath(url.path());
    200   return profile ? util::GetFileSystemByProfile(profile) : NULL;
    201 }
    202 
    203 void RunFileSystemCallback(
    204     const FileSystemGetter& file_system_getter,
    205     const base::Callback<void(FileSystemInterface*)>& callback,
    206     const base::Closure& on_error_callback) {
    207   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    208   FileSystemInterface* file_system = file_system_getter.Run();
    209 
    210   if (!file_system) {
    211     if (!on_error_callback.is_null())
    212       on_error_callback.Run();
    213     return;
    214   }
    215 
    216   callback.Run(file_system);
    217 }
    218 
    219 void GetFileInfo(const base::FilePath& file_path,
    220                  const GetFileInfoCallback& callback,
    221                  FileSystemInterface* file_system) {
    222   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    223   file_system->GetResourceEntry(
    224       file_path,
    225       base::Bind(&RunGetFileInfoCallback, callback));
    226 }
    227 
    228 void Copy(const base::FilePath& src_file_path,
    229           const base::FilePath& dest_file_path,
    230           bool preserve_last_modified,
    231           const StatusCallback& callback,
    232           FileSystemInterface* file_system) {
    233   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    234   file_system->Copy(src_file_path, dest_file_path, preserve_last_modified,
    235                     base::Bind(&RunStatusCallbackByFileError, callback));
    236 }
    237 
    238 void Move(const base::FilePath& src_file_path,
    239           const base::FilePath& dest_file_path,
    240           const StatusCallback& callback,
    241           FileSystemInterface* file_system) {
    242   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    243   file_system->Move(src_file_path, dest_file_path,
    244                     base::Bind(&RunStatusCallbackByFileError, callback));
    245 }
    246 
    247 void CopyInForeignFile(const base::FilePath& src_foreign_file_path,
    248                        const base::FilePath& dest_file_path,
    249                        const StatusCallback& callback,
    250                        FileSystemInterface* file_system) {
    251   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    252   file_system->TransferFileFromLocalToRemote(
    253       src_foreign_file_path, dest_file_path,
    254       base::Bind(&RunStatusCallbackByFileError, callback));
    255 }
    256 
    257 void ReadDirectory(const base::FilePath& file_path,
    258                    const ReadDirectoryCallback& callback,
    259                    FileSystemInterface* file_system) {
    260   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    261   file_system->ReadDirectory(
    262       file_path,
    263       base::Bind(&RunReadDirectoryCallbackWithEntries, callback),
    264       base::Bind(&RunReadDirectoryCallbackOnCompletion, callback));
    265 }
    266 
    267 void Remove(const base::FilePath& file_path,
    268             bool is_recursive,
    269             const StatusCallback& callback,
    270             FileSystemInterface* file_system) {
    271   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    272   file_system->Remove(file_path, is_recursive,
    273                       base::Bind(&RunStatusCallbackByFileError, callback));
    274 }
    275 
    276 void CreateDirectory(const base::FilePath& file_path,
    277                      bool is_exclusive,
    278                      bool is_recursive,
    279                      const StatusCallback& callback,
    280                      FileSystemInterface* file_system) {
    281   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    282   file_system->CreateDirectory(
    283       file_path, is_exclusive, is_recursive,
    284       base::Bind(&RunStatusCallbackByFileError, callback));
    285 }
    286 
    287 void CreateFile(const base::FilePath& file_path,
    288                 bool is_exclusive,
    289                 const StatusCallback& callback,
    290                 FileSystemInterface* file_system) {
    291   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    292   file_system->CreateFile(file_path, is_exclusive,
    293                           std::string(),  // no mime type; guess from file_path
    294                           base::Bind(&RunStatusCallbackByFileError, callback));
    295 }
    296 
    297 void Truncate(const base::FilePath& file_path,
    298               int64 length,
    299               const StatusCallback& callback,
    300               FileSystemInterface* file_system) {
    301   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    302   file_system->TruncateFile(
    303       file_path, length,
    304       base::Bind(&RunStatusCallbackByFileError, callback));
    305 }
    306 
    307 void CreateSnapshotFile(const base::FilePath& file_path,
    308                         const CreateSnapshotFileCallback& callback,
    309                         FileSystemInterface* file_system) {
    310   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    311   file_system->GetFile(file_path,
    312                        base::Bind(&RunCreateSnapshotFileCallback, callback));
    313 }
    314 
    315 void CreateWritableSnapshotFile(
    316     const base::FilePath& file_path,
    317     const CreateWritableSnapshotFileCallback& callback,
    318     FileSystemInterface* file_system) {
    319   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    320   file_system->OpenFile(
    321       file_path,
    322       OPEN_FILE,
    323       std::string(),  // no mime type; we never create a new file here.
    324       base::Bind(&RunCreateWritableSnapshotFileCallback, callback));
    325 }
    326 
    327 void OpenFile(const base::FilePath& file_path,
    328               int file_flags,
    329               const OpenFileCallback& callback,
    330               FileSystemInterface* file_system) {
    331   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    332 
    333   // Returns an error if any unsupported flag is found.
    334   if (file_flags & ~(base::File::FLAG_OPEN |
    335                      base::File::FLAG_CREATE |
    336                      base::File::FLAG_OPEN_ALWAYS |
    337                      base::File::FLAG_CREATE_ALWAYS |
    338                      base::File::FLAG_OPEN_TRUNCATED |
    339                      base::File::FLAG_READ |
    340                      base::File::FLAG_WRITE |
    341                      base::File::FLAG_WRITE_ATTRIBUTES |
    342                      base::File::FLAG_APPEND)) {
    343     base::MessageLoopProxy::current()->PostTask(
    344         FROM_HERE,
    345         base::Bind(callback,
    346                    Passed(base::File(base::File::FILE_ERROR_FAILED)),
    347                    base::Closure()));
    348     return;
    349   }
    350 
    351   file_system->OpenFile(
    352       file_path, GetOpenMode(file_flags),
    353       std::string(),  // no mime type; guess from file_path
    354       base::Bind(&OpenFileAfterFileSystemOpenFile, file_flags, callback));
    355 }
    356 
    357 void TouchFile(const base::FilePath& file_path,
    358                const base::Time& last_access_time,
    359                const base::Time& last_modified_time,
    360                const StatusCallback& callback,
    361                FileSystemInterface* file_system) {
    362   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    363   file_system->TouchFile(file_path, last_access_time, last_modified_time,
    364                          base::Bind(&RunStatusCallbackByFileError, callback));
    365 
    366 }
    367 
    368 }  // namespace fileapi_internal
    369 }  // namespace drive
    370