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/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/common/fileapi/directory_entry.h"
     18 
     19 using content::BrowserThread;
     20 
     21 namespace drive {
     22 namespace fileapi_internal {
     23 namespace {
     24 
     25 // The summary of opening mode is:
     26 // - PLATFORM_FILE_OPEN: Open the existing file. Fail if not exists.
     27 // - PLATFORM_FILE_CREATE: Create the file if not exists. Fail if exists.
     28 // - PLATFORM_FILE_OPEN_ALWAYS: Open the existing file. Create a new file
     29 //     if not exists.
     30 // - PLATFORM_FILE_CREATE_ALWAYS: Create a new file if not exists. If exists
     31 //     open it with truncate.
     32 // - PLATFORM_FILE_OPEN_TRUNCATE: Open the existing file with truncate.
     33 //     Fail if not exists.
     34 OpenMode GetOpenMode(int file_flag) {
     35   if (file_flag & (base::PLATFORM_FILE_OPEN |
     36                    base::PLATFORM_FILE_OPEN_TRUNCATED))
     37     return OPEN_FILE;
     38 
     39   if (file_flag & base::PLATFORM_FILE_CREATE)
     40     return CREATE_FILE;
     41 
     42   DCHECK(file_flag & (base::PLATFORM_FILE_OPEN_ALWAYS |
     43                       base::PLATFORM_FILE_CREATE_ALWAYS));
     44   return OPEN_OR_CREATE_FILE;
     45 }
     46 
     47 // Runs |callback| with the PlatformFileError converted from |error|.
     48 void RunStatusCallbackByFileError(const StatusCallback& callback,
     49                                   FileError error) {
     50   callback.Run(FileErrorToPlatformError(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(FileErrorToPlatformError(error), base::PlatformFileInfo());
     59     return;
     60   }
     61 
     62   DCHECK(entry);
     63   base::PlatformFileInfo file_info;
     64   ConvertResourceEntryToPlatformFileInfo(*entry, &file_info);
     65   callback.Run(base::PLATFORM_FILE_OK, file_info);
     66 }
     67 
     68 // Runs |callback| with arguments converted from |error| and |resource_entries|.
     69 void RunReadDirectoryCallback(
     70     const ReadDirectoryCallback& callback,
     71     FileError error,
     72     scoped_ptr<ResourceEntryVector> resource_entries) {
     73   if (error != FILE_ERROR_OK) {
     74     callback.Run(FileErrorToPlatformError(error),
     75                  std::vector<fileapi::DirectoryEntry>(), false);
     76     return;
     77   }
     78 
     79   DCHECK(resource_entries);
     80 
     81   std::vector<fileapi::DirectoryEntry> entries;
     82   // Convert drive files to File API's directory entry.
     83   entries.reserve(resource_entries->size());
     84   for (size_t i = 0; i < resource_entries->size(); ++i) {
     85     const ResourceEntry& resource_entry = (*resource_entries)[i];
     86     fileapi::DirectoryEntry entry;
     87     entry.name = resource_entry.base_name();
     88 
     89     const PlatformFileInfoProto& file_info = resource_entry.file_info();
     90     entry.is_directory = file_info.is_directory();
     91     entry.size = file_info.size();
     92     entry.last_modified_time =
     93         base::Time::FromInternalValue(file_info.last_modified());
     94     entries.push_back(entry);
     95   }
     96 
     97   callback.Run(base::PLATFORM_FILE_OK, entries, false);
     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         FileErrorToPlatformError(error),
    108         base::PlatformFileInfo(), 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::PlatformFileInfo file_info;
    122   ConvertResourceEntryToPlatformFileInfo(*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::PLATFORM_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(FileErrorToPlatformError(error), local_path, close_callback);
    144 }
    145 
    146 // Runs |callback| with |error| and |platform_file|.
    147 void RunOpenFileCallback(const OpenFileCallback& callback,
    148                          const base::Closure& close_callback,
    149                          base::PlatformFileError* error,
    150                          base::PlatformFile platform_file) {
    151   callback.Run(*error, platform_file, close_callback);
    152 }
    153 
    154 // Part of OpenFile(). Called after FileSystem::OpenFile().
    155 void OpenFileAfterFileSystemOpenFile(int file_flags,
    156                                      const OpenFileCallback& callback,
    157                                      FileError error,
    158                                      const base::FilePath& local_path,
    159                                      const base::Closure& close_callback) {
    160   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    161 
    162   if (error != FILE_ERROR_OK) {
    163     callback.Run(FileErrorToPlatformError(error),
    164                  base::kInvalidPlatformFileValue,
    165                  base::Closure());
    166     return;
    167   }
    168 
    169   // Here, the file should be at |local_path|, but there may be timing issue.
    170   // Because the file is managed by Drive file system, so, in order to avoid
    171   // unexpected file creation, CREATE, OPEN_ALWAYS and CREATE_ALWAYS are
    172   // translated into OPEN or OPEN_TRUNCATED, here. Keep OPEN and OPEN_TRUNCATED
    173   // as is.
    174   if (file_flags & (base::PLATFORM_FILE_CREATE |
    175                     base::PLATFORM_FILE_OPEN_ALWAYS)) {
    176     file_flags &= ~(base::PLATFORM_FILE_CREATE |
    177                     base::PLATFORM_FILE_OPEN_ALWAYS);
    178     file_flags |= base::PLATFORM_FILE_OPEN;
    179   } else if (file_flags & base::PLATFORM_FILE_CREATE_ALWAYS) {
    180     file_flags &= ~base::PLATFORM_FILE_CREATE_ALWAYS;
    181     file_flags |= base::PLATFORM_FILE_OPEN_TRUNCATED;
    182   }
    183 
    184   // Cache file prepared for modification is available. Open it locally.
    185   base::PlatformFileError* result =
    186       new base::PlatformFileError(base::PLATFORM_FILE_ERROR_FAILED);
    187   bool posted = base::PostTaskAndReplyWithResult(
    188       BrowserThread::GetBlockingPool(), FROM_HERE,
    189       base::Bind(&base::CreatePlatformFile,
    190                  local_path, file_flags, static_cast<bool*>(NULL), result),
    191       base::Bind(&RunOpenFileCallback,
    192                  callback, close_callback, base::Owned(result)));
    193   DCHECK(posted);
    194 }
    195 
    196 }  // namespace
    197 
    198 void RunFileSystemCallback(
    199     const FileSystemGetter& file_system_getter,
    200     const base::Callback<void(FileSystemInterface*)>& callback,
    201     const base::Closure& on_error_callback) {
    202   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    203   FileSystemInterface* file_system = file_system_getter.Run();
    204 
    205   if (!file_system) {
    206     if (!on_error_callback.is_null())
    207       on_error_callback.Run();
    208     return;
    209   }
    210 
    211   callback.Run(file_system);
    212 }
    213 
    214 void GetFileInfo(const base::FilePath& file_path,
    215                  const GetFileInfoCallback& callback,
    216                  FileSystemInterface* file_system) {
    217   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    218   file_system->GetResourceEntryByPath(
    219       file_path,
    220       base::Bind(&RunGetFileInfoCallback, callback));
    221 }
    222 
    223 void Copy(const base::FilePath& src_file_path,
    224           const base::FilePath& dest_file_path,
    225           const StatusCallback& callback,
    226           FileSystemInterface* file_system) {
    227   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    228   file_system->Copy(src_file_path, dest_file_path,
    229                     base::Bind(&RunStatusCallbackByFileError, callback));
    230 }
    231 
    232 void Move(const base::FilePath& src_file_path,
    233           const base::FilePath& dest_file_path,
    234           const StatusCallback& callback,
    235           FileSystemInterface* file_system) {
    236   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    237   file_system->Move(src_file_path, dest_file_path,
    238                     base::Bind(&RunStatusCallbackByFileError, callback));
    239 }
    240 
    241 void CopyInForeignFile(const base::FilePath& src_foreign_file_path,
    242                        const base::FilePath& dest_file_path,
    243                        const StatusCallback& callback,
    244                        FileSystemInterface* file_system) {
    245   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    246   file_system->TransferFileFromLocalToRemote(
    247       src_foreign_file_path, dest_file_path,
    248       base::Bind(&RunStatusCallbackByFileError, callback));
    249 }
    250 
    251 void ReadDirectory(const base::FilePath& file_path,
    252                    const ReadDirectoryCallback& callback,
    253                    FileSystemInterface* file_system) {
    254   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    255   file_system->ReadDirectoryByPath(
    256       file_path,
    257       base::Bind(&RunReadDirectoryCallback, callback));
    258 }
    259 
    260 void Remove(const base::FilePath& file_path,
    261             bool is_recursive,
    262             const StatusCallback& callback,
    263             FileSystemInterface* file_system) {
    264   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    265   file_system->Remove(file_path, is_recursive,
    266                       base::Bind(&RunStatusCallbackByFileError, callback));
    267 }
    268 
    269 void CreateDirectory(const base::FilePath& file_path,
    270                      bool is_exclusive,
    271                      bool is_recursive,
    272                      const StatusCallback& callback,
    273                      FileSystemInterface* file_system) {
    274   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    275   file_system->CreateDirectory(
    276       file_path, is_exclusive, is_recursive,
    277       base::Bind(&RunStatusCallbackByFileError, callback));
    278 }
    279 
    280 void CreateFile(const base::FilePath& file_path,
    281                 bool is_exclusive,
    282                 const StatusCallback& callback,
    283                 FileSystemInterface* file_system) {
    284   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    285   file_system->CreateFile(file_path, is_exclusive,
    286                           base::Bind(&RunStatusCallbackByFileError, callback));
    287 }
    288 
    289 void Truncate(const base::FilePath& file_path,
    290               int64 length,
    291               const StatusCallback& callback,
    292               FileSystemInterface* file_system) {
    293   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    294   file_system->TruncateFile(
    295       file_path, length,
    296       base::Bind(&RunStatusCallbackByFileError, callback));
    297 }
    298 
    299 void CreateSnapshotFile(const base::FilePath& file_path,
    300                         const CreateSnapshotFileCallback& callback,
    301                         FileSystemInterface* file_system) {
    302   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    303   file_system->GetFileByPath(
    304       file_path,
    305       base::Bind(&RunCreateSnapshotFileCallback, callback));
    306 }
    307 
    308 void CreateWritableSnapshotFile(
    309     const base::FilePath& file_path,
    310     const CreateWritableSnapshotFileCallback& callback,
    311     FileSystemInterface* file_system) {
    312   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    313   file_system->OpenFile(
    314       file_path,
    315       OPEN_FILE,
    316       base::Bind(&RunCreateWritableSnapshotFileCallback, callback));
    317 }
    318 
    319 void OpenFile(const base::FilePath& file_path,
    320               int file_flags,
    321               const OpenFileCallback& callback,
    322               FileSystemInterface* file_system) {
    323   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    324 
    325   // Returns an error if any unsupported flag is found.
    326   if (file_flags & ~(base::PLATFORM_FILE_OPEN |
    327                      base::PLATFORM_FILE_CREATE |
    328                      base::PLATFORM_FILE_OPEN_ALWAYS |
    329                      base::PLATFORM_FILE_CREATE_ALWAYS |
    330                      base::PLATFORM_FILE_OPEN_TRUNCATED |
    331                      base::PLATFORM_FILE_READ |
    332                      base::PLATFORM_FILE_WRITE |
    333                      base::PLATFORM_FILE_WRITE_ATTRIBUTES |
    334                      base::PLATFORM_FILE_APPEND)) {
    335     base::MessageLoopProxy::current()->PostTask(
    336         FROM_HERE,
    337         base::Bind(callback,
    338                    base::PLATFORM_FILE_ERROR_FAILED,
    339                    base::kInvalidPlatformFileValue,
    340                    base::Closure()));
    341     return;
    342   }
    343 
    344   file_system->OpenFile(
    345       file_path, GetOpenMode(file_flags),
    346       base::Bind(&OpenFileAfterFileSystemOpenFile, file_flags, callback));
    347 }
    348 
    349 void TouchFile(const base::FilePath& file_path,
    350                const base::Time& last_access_time,
    351                const base::Time& last_modified_time,
    352                const StatusCallback& callback,
    353                FileSystemInterface* file_system) {
    354   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    355   file_system->TouchFile(file_path, last_access_time, last_modified_time,
    356                          base::Bind(&RunStatusCallbackByFileError, callback));
    357 
    358 }
    359 
    360 }  // namespace fileapi_internal
    361 }  // namespace drive
    362