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