Home | History | Annotate | Download | only in file_manager
      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/file_manager/filesystem_api_util.h"
      6 
      7 #include "base/callback.h"
      8 #include "base/files/file_path.h"
      9 #include "base/memory/scoped_ptr.h"
     10 #include "chrome/browser/chromeos/drive/file_errors.h"
     11 #include "chrome/browser/chromeos/drive/file_system_interface.h"
     12 #include "chrome/browser/chromeos/drive/file_system_util.h"
     13 #include "chrome/browser/chromeos/file_manager/app_id.h"
     14 #include "chrome/browser/chromeos/file_manager/fileapi_util.h"
     15 #include "chrome/browser/extensions/extension_util.h"
     16 #include "chrome/browser/profiles/profile.h"
     17 #include "content/public/browser/browser_thread.h"
     18 #include "content/public/browser/storage_partition.h"
     19 #include "google_apis/drive/task_util.h"
     20 #include "webkit/browser/fileapi/file_system_context.h"
     21 
     22 namespace file_manager {
     23 namespace util {
     24 
     25 namespace {
     26 
     27 // Helper function used to implement GetNonNativeLocalPathMimeType. It extracts
     28 // the mime type from the passed Drive resource entry.
     29 void GetMimeTypeAfterGetResourceEntry(
     30     const base::Callback<void(bool, const std::string&)>& callback,
     31     drive::FileError error,
     32     scoped_ptr<drive::ResourceEntry> entry) {
     33   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
     34 
     35   if (error != drive::FILE_ERROR_OK || !entry->has_file_specific_info()) {
     36     callback.Run(false, std::string());
     37     return;
     38   }
     39   callback.Run(true, entry->file_specific_info().content_mime_type());
     40 }
     41 
     42 // Helper function to converts a callback that takes boolean value to that takes
     43 // File::Error, by regarding FILE_OK as the only successful value.
     44 void BoolCallbackAsFileErrorCallback(
     45     const base::Callback<void(bool)>& callback,
     46     base::File::Error error) {
     47   return callback.Run(error == base::File::FILE_OK);
     48 }
     49 
     50 // Part of PrepareFileOnIOThread. It tries to create a new file if the given
     51 // |url| is not already inhabited.
     52 void PrepareFileAfterCheckExistOnIOThread(
     53     scoped_refptr<fileapi::FileSystemContext> file_system_context,
     54     const fileapi::FileSystemURL& url,
     55     const fileapi::FileSystemOperation::StatusCallback& callback,
     56     base::File::Error error) {
     57   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
     58 
     59   if (error != base::File::FILE_ERROR_NOT_FOUND) {
     60     callback.Run(error);
     61     return;
     62   }
     63 
     64   // Call with the second argument |exclusive| set to false, meaning that it
     65   // is not an error even if the file already exists (it can happen if the file
     66   // is created after the previous FileExists call and before this CreateFile.)
     67   //
     68   // Note that the preceding call to FileExists is necessary for handling
     69   // read only filesystems that blindly rejects handling CreateFile().
     70   file_system_context->operation_runner()->CreateFile(url, false, callback);
     71 }
     72 
     73 // Checks whether a file exists at the given |url|, and try creating it if it
     74 // is not already there.
     75 void PrepareFileOnIOThread(
     76     scoped_refptr<fileapi::FileSystemContext> file_system_context,
     77     const fileapi::FileSystemURL& url,
     78     const base::Callback<void(bool)>& callback) {
     79   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
     80 
     81   file_system_context->operation_runner()->FileExists(
     82       url,
     83       base::Bind(&PrepareFileAfterCheckExistOnIOThread,
     84                  file_system_context,
     85                  url,
     86                  base::Bind(&BoolCallbackAsFileErrorCallback, callback)));
     87 }
     88 
     89 }  // namespace
     90 
     91 bool IsUnderNonNativeLocalPath(Profile* profile,
     92                         const base::FilePath& path) {
     93   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
     94 
     95   GURL url;
     96   if (!util::ConvertAbsoluteFilePathToFileSystemUrl(
     97            profile, path, kFileManagerAppId, &url)) {
     98     return false;
     99   }
    100 
    101   fileapi::FileSystemURL filesystem_url =
    102       GetFileSystemContextForExtensionId(profile,
    103                                          kFileManagerAppId)->CrackURL(url);
    104   if (!filesystem_url.is_valid())
    105     return false;
    106 
    107   switch (filesystem_url.type()) {
    108     case fileapi::kFileSystemTypeNativeLocal:
    109     case fileapi::kFileSystemTypeRestrictedNativeLocal:
    110       return false;
    111     default:
    112       // The path indeed corresponds to a mount point not associated with a
    113       // native local path.
    114       return true;
    115   }
    116 }
    117 
    118 void GetNonNativeLocalPathMimeType(
    119     Profile* profile,
    120     const base::FilePath& path,
    121     const base::Callback<void(bool, const std::string&)>& callback) {
    122   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
    123   DCHECK(IsUnderNonNativeLocalPath(profile, path));
    124 
    125   if (!drive::util::IsUnderDriveMountPoint(path)) {
    126     // Non-drive mount point does not have mime types as metadata. Just return
    127     // success with empty mime type value.
    128     content::BrowserThread::PostTask(
    129         content::BrowserThread::UI,
    130         FROM_HERE,
    131         base::Bind(callback, true /* success */, std::string()));
    132     return;
    133   }
    134 
    135   drive::FileSystemInterface* file_system =
    136       drive::util::GetFileSystemByProfile(profile);
    137   if (!file_system) {
    138     content::BrowserThread::PostTask(
    139         content::BrowserThread::UI,
    140         FROM_HERE,
    141         base::Bind(callback, false, std::string()));
    142     return;
    143   }
    144 
    145   file_system->GetResourceEntry(
    146       drive::util::ExtractDrivePath(path),
    147       base::Bind(&GetMimeTypeAfterGetResourceEntry, callback));
    148 }
    149 
    150 void IsNonNativeLocalPathDirectory(
    151     Profile* profile,
    152     const base::FilePath& path,
    153     const base::Callback<void(bool)>& callback) {
    154   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
    155   DCHECK(IsUnderNonNativeLocalPath(profile, path));
    156 
    157   GURL url;
    158   if (!util::ConvertAbsoluteFilePathToFileSystemUrl(
    159            profile, path, kFileManagerAppId, &url)) {
    160     // Posting to the current thread, so that we always call back asynchronously
    161     // independent from whether or not the operation succeeds.
    162     content::BrowserThread::PostTask(content::BrowserThread::UI,
    163                                      FROM_HERE,
    164                                      base::Bind(callback, false));
    165     return;
    166   }
    167 
    168   util::CheckIfDirectoryExists(
    169       GetFileSystemContextForExtensionId(profile, kFileManagerAppId),
    170       url,
    171       base::Bind(&BoolCallbackAsFileErrorCallback, callback));
    172 }
    173 
    174 void PrepareNonNativeLocalFileForWritableApp(
    175     Profile* profile,
    176     const base::FilePath& path,
    177     const base::Callback<void(bool)>& callback) {
    178   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
    179   DCHECK(IsUnderNonNativeLocalPath(profile, path));
    180 
    181   GURL url;
    182   if (!util::ConvertAbsoluteFilePathToFileSystemUrl(
    183            profile, path, kFileManagerAppId, &url)) {
    184     // Posting to the current thread, so that we always call back asynchronously
    185     // independent from whether or not the operation succeeds.
    186     content::BrowserThread::PostTask(content::BrowserThread::UI,
    187                                      FROM_HERE,
    188                                      base::Bind(callback, false));
    189     return;
    190   }
    191 
    192   fileapi::FileSystemContext* const context =
    193       GetFileSystemContextForExtensionId(profile, kFileManagerAppId);
    194   DCHECK(context);
    195 
    196   // Check the existence of a file using file system API implementation on
    197   // behalf of the file manager app. We need to grant access beforehand.
    198   context->external_backend()->GrantFullAccessToExtension(kFileManagerAppId);
    199 
    200   content::BrowserThread::PostTask(
    201       content::BrowserThread::IO,
    202       FROM_HERE,
    203       base::Bind(&PrepareFileOnIOThread,
    204                  make_scoped_refptr(context),
    205                  context->CrackURL(url),
    206                  google_apis::CreateRelayCallback(callback)));
    207 }
    208 
    209 }  // namespace util
    210 }  // namespace file_manager
    211