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