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 "webkit/browser/fileapi/file_system_url.h"
     18 #include "webkit/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<fileapi::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     fileapi::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<fileapi::DirectoryEntry>(), false /*has_more*/);
     98 }
     99 
    100 // Runs |callback| with arguments based on |error|, |local_path| and |entry|.
    101 void RunCreateSnapshotFileCallback(const CreateSnapshotFileCallback& callback,
    102                                    FileError error,
    103                                    const base::FilePath& local_path,
    104                                    scoped_ptr<ResourceEntry> entry) {
    105   if (error != FILE_ERROR_OK) {
    106     callback.Run(
    107         FileErrorToBaseFileError(error),
    108         base::File::Info(), base::FilePath(),
    109         webkit_blob::ScopedFile::ScopeOutPolicy());
    110     return;
    111   }
    112 
    113   DCHECK(entry);
    114 
    115   // When reading file, last modified time specified in file info will be
    116   // compared to the last modified time of the local version of the drive file.
    117   // Since those two values don't generally match (last modification time on the
    118   // drive server vs. last modification time of the local, downloaded file), so
    119   // we have to opt out from this check. We do this by unsetting last_modified
    120   // value in the file info passed to the CreateSnapshot caller.
    121   base::File::Info file_info;
    122   ConvertResourceEntryToFileInfo(*entry, &file_info);
    123   file_info.last_modified = base::Time();
    124 
    125   // If the file is a hosted document, a temporary JSON file is created to
    126   // represent the document. The JSON file is not cached and its lifetime
    127   // is managed by ShareableFileReference.
    128   webkit_blob::ScopedFile::ScopeOutPolicy scope_out_policy =
    129       entry->file_specific_info().is_hosted_document() ?
    130       webkit_blob::ScopedFile::DELETE_ON_SCOPE_OUT :
    131       webkit_blob::ScopedFile::DONT_DELETE_ON_SCOPE_OUT;
    132 
    133   callback.Run(base::File::FILE_OK, file_info, local_path, scope_out_policy);
    134 }
    135 
    136 // Runs |callback| with arguments converted from |error| and |local_path|.
    137 void RunCreateWritableSnapshotFileCallback(
    138     const CreateWritableSnapshotFileCallback& callback,
    139     FileError error,
    140     const base::FilePath& local_path,
    141     const base::Closure& close_callback) {
    142   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    143   callback.Run(FileErrorToBaseFileError(error), local_path, close_callback);
    144 }
    145 
    146 // Runs |callback| with |file|.
    147 void RunOpenFileCallback(const OpenFileCallback& callback,
    148                          const base::Closure& close_callback,
    149                          base::File file) {
    150   callback.Run(file.Pass(), close_callback);
    151 }
    152 
    153 base::File OpenFile(const base::FilePath& path, int flags) {
    154   return base::File(path, flags);
    155 }
    156 
    157 // Part of OpenFile(). Called after FileSystem::OpenFile().
    158 void OpenFileAfterFileSystemOpenFile(int file_flags,
    159                                      const OpenFileCallback& callback,
    160                                      FileError error,
    161                                      const base::FilePath& local_path,
    162                                      const base::Closure& close_callback) {
    163   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    164 
    165   if (error != FILE_ERROR_OK) {
    166     callback.Run(base::File(FileErrorToBaseFileError(error)), base::Closure());
    167     return;
    168   }
    169 
    170   // Here, the file should be at |local_path|, but there may be timing issue.
    171   // Because the file is managed by Drive file system, so, in order to avoid
    172   // unexpected file creation, CREATE, OPEN_ALWAYS and CREATE_ALWAYS are
    173   // translated into OPEN or OPEN_TRUNCATED, here. Keep OPEN and OPEN_TRUNCATED
    174   // as is.
    175   if (file_flags & (base::File::FLAG_CREATE |
    176                     base::File::FLAG_OPEN_ALWAYS)) {
    177     file_flags &= ~(base::File::FLAG_CREATE |
    178                     base::File::FLAG_OPEN_ALWAYS);
    179     file_flags |= base::File::FLAG_OPEN;
    180   } else if (file_flags & base::File::FLAG_CREATE_ALWAYS) {
    181     file_flags &= ~base::File::FLAG_CREATE_ALWAYS;
    182     file_flags |= base::File::FLAG_OPEN_TRUNCATED;
    183   }
    184 
    185   // Cache file prepared for modification is available. Open it locally.
    186   bool posted = base::PostTaskAndReplyWithResult(
    187       BrowserThread::GetBlockingPool(), FROM_HERE,
    188       base::Bind(&OpenFile, local_path, file_flags),
    189       base::Bind(&RunOpenFileCallback, callback, close_callback));
    190   DCHECK(posted);
    191 }
    192 
    193 }  // namespace
    194 
    195 FileSystemInterface* GetFileSystemFromUrl(const fileapi::FileSystemURL& url) {
    196   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    197 
    198   Profile* profile = util::ExtractProfileFromPath(url.path());
    199   return profile ? util::GetFileSystemByProfile(profile) : NULL;
    200 }
    201 
    202 void RunFileSystemCallback(
    203     const FileSystemGetter& file_system_getter,
    204     const base::Callback<void(FileSystemInterface*)>& callback,
    205     const base::Closure& on_error_callback) {
    206   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    207   FileSystemInterface* file_system = file_system_getter.Run();
    208 
    209   if (!file_system) {
    210     if (!on_error_callback.is_null())
    211       on_error_callback.Run();
    212     return;
    213   }
    214 
    215   callback.Run(file_system);
    216 }
    217 
    218 void GetFileInfo(const base::FilePath& file_path,
    219                  const GetFileInfoCallback& callback,
    220                  FileSystemInterface* file_system) {
    221   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    222   file_system->GetResourceEntry(
    223       file_path,
    224       base::Bind(&RunGetFileInfoCallback, callback));
    225 }
    226 
    227 void Copy(const base::FilePath& src_file_path,
    228           const base::FilePath& dest_file_path,
    229           bool preserve_last_modified,
    230           const StatusCallback& callback,
    231           FileSystemInterface* file_system) {
    232   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    233   file_system->Copy(src_file_path, dest_file_path, preserve_last_modified,
    234                     base::Bind(&RunStatusCallbackByFileError, callback));
    235 }
    236 
    237 void Move(const base::FilePath& src_file_path,
    238           const base::FilePath& dest_file_path,
    239           const StatusCallback& callback,
    240           FileSystemInterface* file_system) {
    241   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    242   file_system->Move(src_file_path, dest_file_path,
    243                     base::Bind(&RunStatusCallbackByFileError, callback));
    244 }
    245 
    246 void CopyInForeignFile(const base::FilePath& src_foreign_file_path,
    247                        const base::FilePath& dest_file_path,
    248                        const StatusCallback& callback,
    249                        FileSystemInterface* file_system) {
    250   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    251   file_system->TransferFileFromLocalToRemote(
    252       src_foreign_file_path, dest_file_path,
    253       base::Bind(&RunStatusCallbackByFileError, callback));
    254 }
    255 
    256 void ReadDirectory(const base::FilePath& file_path,
    257                    const ReadDirectoryCallback& callback,
    258                    FileSystemInterface* file_system) {
    259   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    260   file_system->ReadDirectory(
    261       file_path,
    262       base::Bind(&RunReadDirectoryCallbackWithEntries, callback),
    263       base::Bind(&RunReadDirectoryCallbackOnCompletion, callback));
    264 }
    265 
    266 void Remove(const base::FilePath& file_path,
    267             bool is_recursive,
    268             const StatusCallback& callback,
    269             FileSystemInterface* file_system) {
    270   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    271   file_system->Remove(file_path, is_recursive,
    272                       base::Bind(&RunStatusCallbackByFileError, callback));
    273 }
    274 
    275 void CreateDirectory(const base::FilePath& file_path,
    276                      bool is_exclusive,
    277                      bool is_recursive,
    278                      const StatusCallback& callback,
    279                      FileSystemInterface* file_system) {
    280   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    281   file_system->CreateDirectory(
    282       file_path, is_exclusive, is_recursive,
    283       base::Bind(&RunStatusCallbackByFileError, callback));
    284 }
    285 
    286 void CreateFile(const base::FilePath& file_path,
    287                 bool is_exclusive,
    288                 const StatusCallback& callback,
    289                 FileSystemInterface* file_system) {
    290   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    291   file_system->CreateFile(file_path, is_exclusive,
    292                           std::string(),  // no mime type; guess from file_path
    293                           base::Bind(&RunStatusCallbackByFileError, callback));
    294 }
    295 
    296 void Truncate(const base::FilePath& file_path,
    297               int64 length,
    298               const StatusCallback& callback,
    299               FileSystemInterface* file_system) {
    300   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    301   file_system->TruncateFile(
    302       file_path, length,
    303       base::Bind(&RunStatusCallbackByFileError, callback));
    304 }
    305 
    306 void CreateSnapshotFile(const base::FilePath& file_path,
    307                         const CreateSnapshotFileCallback& callback,
    308                         FileSystemInterface* file_system) {
    309   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    310   file_system->GetFile(file_path,
    311                        base::Bind(&RunCreateSnapshotFileCallback, callback));
    312 }
    313 
    314 void CreateWritableSnapshotFile(
    315     const base::FilePath& file_path,
    316     const CreateWritableSnapshotFileCallback& callback,
    317     FileSystemInterface* file_system) {
    318   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    319   file_system->OpenFile(
    320       file_path,
    321       OPEN_FILE,
    322       std::string(),  // no mime type; we never create a new file here.
    323       base::Bind(&RunCreateWritableSnapshotFileCallback, callback));
    324 }
    325 
    326 void OpenFile(const base::FilePath& file_path,
    327               int file_flags,
    328               const OpenFileCallback& callback,
    329               FileSystemInterface* file_system) {
    330   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    331 
    332   // Returns an error if any unsupported flag is found.
    333   if (file_flags & ~(base::File::FLAG_OPEN |
    334                      base::File::FLAG_CREATE |
    335                      base::File::FLAG_OPEN_ALWAYS |
    336                      base::File::FLAG_CREATE_ALWAYS |
    337                      base::File::FLAG_OPEN_TRUNCATED |
    338                      base::File::FLAG_READ |
    339                      base::File::FLAG_WRITE |
    340                      base::File::FLAG_WRITE_ATTRIBUTES |
    341                      base::File::FLAG_APPEND)) {
    342     base::MessageLoopProxy::current()->PostTask(
    343         FROM_HERE,
    344         base::Bind(callback,
    345                    Passed(base::File(base::File::FILE_ERROR_FAILED)),
    346                    base::Closure()));
    347     return;
    348   }
    349 
    350   file_system->OpenFile(
    351       file_path, GetOpenMode(file_flags),
    352       std::string(),  // no mime type; guess from file_path
    353       base::Bind(&OpenFileAfterFileSystemOpenFile, file_flags, callback));
    354 }
    355 
    356 void TouchFile(const base::FilePath& file_path,
    357                const base::Time& last_access_time,
    358                const base::Time& last_modified_time,
    359                const StatusCallback& callback,
    360                FileSystemInterface* file_system) {
    361   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    362   file_system->TouchFile(file_path, last_access_time, last_modified_time,
    363                          base::Bind(&RunStatusCallbackByFileError, callback));
    364 
    365 }
    366 
    367 }  // namespace fileapi_internal
    368 }  // namespace drive
    369