1 // Copyright (c) 2012 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/extensions/api/file_handlers/app_file_handler_util.h" 6 7 #include "base/files/file.h" 8 #include "base/files/file_path.h" 9 #include "base/files/file_util.h" 10 #include "content/public/browser/browser_thread.h" 11 #include "content/public/browser/child_process_security_policy.h" 12 #include "content/public/browser/render_process_host.h" 13 #include "extensions/browser/extension_prefs.h" 14 #include "extensions/browser/granted_file_entry.h" 15 #include "extensions/common/permissions/permissions_data.h" 16 #include "net/base/mime_util.h" 17 #include "storage/browser/fileapi/isolated_context.h" 18 #include "storage/common/fileapi/file_system_mount_option.h" 19 #include "storage/common/fileapi/file_system_types.h" 20 21 #if defined(OS_CHROMEOS) 22 #include "chrome/browser/chromeos/file_manager/filesystem_api_util.h" 23 #endif 24 25 namespace extensions { 26 27 namespace app_file_handler_util { 28 29 const char kInvalidParameters[] = "Invalid parameters"; 30 const char kSecurityError[] = "Security error"; 31 32 namespace { 33 34 bool FileHandlerCanHandleFileWithExtension( 35 const FileHandlerInfo& handler, 36 const base::FilePath& path) { 37 for (std::set<std::string>::const_iterator extension = 38 handler.extensions.begin(); 39 extension != handler.extensions.end(); ++extension) { 40 if (*extension == "*") 41 return true; 42 43 if (path.MatchesExtension( 44 base::FilePath::kExtensionSeparator + 45 base::FilePath::FromUTF8Unsafe(*extension).value())) { 46 return true; 47 } 48 49 // Also accept files with no extension for handlers that support an 50 // empty extension, i.e. both "foo" and "foo." match. 51 if (extension->empty() && 52 path.MatchesExtension(base::FilePath::StringType())) { 53 return true; 54 } 55 } 56 return false; 57 } 58 59 bool FileHandlerCanHandleFileWithMimeType( 60 const FileHandlerInfo& handler, 61 const std::string& mime_type) { 62 for (std::set<std::string>::const_iterator type = handler.types.begin(); 63 type != handler.types.end(); ++type) { 64 if (net::MatchesMimeType(*type, mime_type)) 65 return true; 66 } 67 return false; 68 } 69 70 bool DoCheckWritableFile(const base::FilePath& path, bool is_directory) { 71 // Don't allow links. 72 if (base::PathExists(path) && base::IsLink(path)) 73 return false; 74 75 if (is_directory) 76 return base::DirectoryExists(path); 77 78 // Create the file if it doesn't already exist. 79 int creation_flags = base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_READ; 80 base::File file(path, creation_flags); 81 return file.IsValid(); 82 } 83 84 // Checks whether a list of paths are all OK for writing and calls a provided 85 // on_success or on_failure callback when done. A file is OK for writing if it 86 // is not a symlink, is not in a blacklisted path and can be opened for writing; 87 // files are created if they do not exist. 88 class WritableFileChecker 89 : public base::RefCountedThreadSafe<WritableFileChecker> { 90 public: 91 WritableFileChecker( 92 const std::vector<base::FilePath>& paths, 93 Profile* profile, 94 bool is_directory, 95 const base::Closure& on_success, 96 const base::Callback<void(const base::FilePath&)>& on_failure); 97 98 void Check(); 99 100 private: 101 friend class base::RefCountedThreadSafe<WritableFileChecker>; 102 virtual ~WritableFileChecker(); 103 104 // Called when a work item is completed. If all work items are done, this 105 // calls the success or failure callback. 106 void TaskDone(); 107 108 // Reports an error in completing a work item. This may be called more than 109 // once, but only the last message will be retained. 110 void Error(const base::FilePath& error_path); 111 112 void CheckLocalWritableFiles(); 113 114 #if defined(OS_CHROMEOS) 115 void NonNativeLocalPathCheckDone(const base::FilePath& path, bool success); 116 #endif 117 118 const std::vector<base::FilePath> paths_; 119 Profile* profile_; 120 const bool is_directory_; 121 int outstanding_tasks_; 122 base::FilePath error_path_; 123 base::Closure on_success_; 124 base::Callback<void(const base::FilePath&)> on_failure_; 125 }; 126 127 WritableFileChecker::WritableFileChecker( 128 const std::vector<base::FilePath>& paths, 129 Profile* profile, 130 bool is_directory, 131 const base::Closure& on_success, 132 const base::Callback<void(const base::FilePath&)>& on_failure) 133 : paths_(paths), 134 profile_(profile), 135 is_directory_(is_directory), 136 outstanding_tasks_(1), 137 on_success_(on_success), 138 on_failure_(on_failure) {} 139 140 void WritableFileChecker::Check() { 141 #if defined(OS_CHROMEOS) 142 if (file_manager::util::IsUnderNonNativeLocalPath(profile_, paths_[0])) { 143 outstanding_tasks_ = paths_.size(); 144 for (std::vector<base::FilePath>::const_iterator it = paths_.begin(); 145 it != paths_.end(); 146 ++it) { 147 if (is_directory_) { 148 file_manager::util::IsNonNativeLocalPathDirectory( 149 profile_, 150 *it, 151 base::Bind(&WritableFileChecker::NonNativeLocalPathCheckDone, 152 this, *it)); 153 } else { 154 file_manager::util::PrepareNonNativeLocalFileForWritableApp( 155 profile_, 156 *it, 157 base::Bind(&WritableFileChecker::NonNativeLocalPathCheckDone, 158 this, *it)); 159 } 160 } 161 return; 162 } 163 #endif 164 content::BrowserThread::PostTask( 165 content::BrowserThread::FILE, 166 FROM_HERE, 167 base::Bind(&WritableFileChecker::CheckLocalWritableFiles, this)); 168 } 169 170 WritableFileChecker::~WritableFileChecker() {} 171 172 void WritableFileChecker::TaskDone() { 173 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 174 if (--outstanding_tasks_ == 0) { 175 if (error_path_.empty()) 176 on_success_.Run(); 177 else 178 on_failure_.Run(error_path_); 179 } 180 } 181 182 // Reports an error in completing a work item. This may be called more than 183 // once, but only the last message will be retained. 184 void WritableFileChecker::Error(const base::FilePath& error_path) { 185 DCHECK(!error_path.empty()); 186 error_path_ = error_path; 187 TaskDone(); 188 } 189 190 void WritableFileChecker::CheckLocalWritableFiles() { 191 DCHECK_CURRENTLY_ON(content::BrowserThread::FILE); 192 std::string error; 193 for (std::vector<base::FilePath>::const_iterator it = paths_.begin(); 194 it != paths_.end(); 195 ++it) { 196 if (!DoCheckWritableFile(*it, is_directory_)) { 197 content::BrowserThread::PostTask( 198 content::BrowserThread::UI, 199 FROM_HERE, 200 base::Bind(&WritableFileChecker::Error, this, *it)); 201 return; 202 } 203 } 204 content::BrowserThread::PostTask( 205 content::BrowserThread::UI, 206 FROM_HERE, 207 base::Bind(&WritableFileChecker::TaskDone, this)); 208 } 209 210 #if defined(OS_CHROMEOS) 211 void WritableFileChecker::NonNativeLocalPathCheckDone( 212 const base::FilePath& path, 213 bool success) { 214 if (success) 215 TaskDone(); 216 else 217 Error(path); 218 } 219 #endif 220 221 } // namespace 222 223 const FileHandlerInfo* FileHandlerForId(const Extension& app, 224 const std::string& handler_id) { 225 const FileHandlersInfo* file_handlers = FileHandlers::GetFileHandlers(&app); 226 if (!file_handlers) 227 return NULL; 228 229 for (FileHandlersInfo::const_iterator i = file_handlers->begin(); 230 i != file_handlers->end(); i++) { 231 if (i->id == handler_id) 232 return &*i; 233 } 234 return NULL; 235 } 236 237 const FileHandlerInfo* FirstFileHandlerForFile( 238 const Extension& app, 239 const std::string& mime_type, 240 const base::FilePath& path) { 241 const FileHandlersInfo* file_handlers = FileHandlers::GetFileHandlers(&app); 242 if (!file_handlers) 243 return NULL; 244 245 for (FileHandlersInfo::const_iterator i = file_handlers->begin(); 246 i != file_handlers->end(); i++) { 247 if (FileHandlerCanHandleFile(*i, mime_type, path)) 248 return &*i; 249 } 250 return NULL; 251 } 252 253 std::vector<const FileHandlerInfo*> FindFileHandlersForFiles( 254 const Extension& app, const PathAndMimeTypeSet& files) { 255 std::vector<const FileHandlerInfo*> handlers; 256 if (files.empty()) 257 return handlers; 258 259 // Look for file handlers which can handle all the MIME types specified. 260 const FileHandlersInfo* file_handlers = FileHandlers::GetFileHandlers(&app); 261 if (!file_handlers) 262 return handlers; 263 264 for (FileHandlersInfo::const_iterator data = file_handlers->begin(); 265 data != file_handlers->end(); ++data) { 266 bool handles_all_types = true; 267 for (PathAndMimeTypeSet::const_iterator it = files.begin(); 268 it != files.end(); ++it) { 269 if (!FileHandlerCanHandleFile(*data, it->second, it->first)) { 270 handles_all_types = false; 271 break; 272 } 273 } 274 if (handles_all_types) 275 handlers.push_back(&*data); 276 } 277 return handlers; 278 } 279 280 bool FileHandlerCanHandleFile( 281 const FileHandlerInfo& handler, 282 const std::string& mime_type, 283 const base::FilePath& path) { 284 return FileHandlerCanHandleFileWithMimeType(handler, mime_type) || 285 FileHandlerCanHandleFileWithExtension(handler, path); 286 } 287 288 GrantedFileEntry CreateFileEntry( 289 Profile* profile, 290 const Extension* extension, 291 int renderer_id, 292 const base::FilePath& path, 293 bool is_directory) { 294 GrantedFileEntry result; 295 storage::IsolatedContext* isolated_context = 296 storage::IsolatedContext::GetInstance(); 297 DCHECK(isolated_context); 298 299 result.filesystem_id = isolated_context->RegisterFileSystemForPath( 300 storage::kFileSystemTypeNativeForPlatformApp, 301 std::string(), 302 path, 303 &result.registered_name); 304 305 content::ChildProcessSecurityPolicy* policy = 306 content::ChildProcessSecurityPolicy::GetInstance(); 307 policy->GrantReadFileSystem(renderer_id, result.filesystem_id); 308 if (HasFileSystemWritePermission(extension)) { 309 if (is_directory) { 310 policy->GrantCreateReadWriteFileSystem(renderer_id, result.filesystem_id); 311 } else { 312 policy->GrantWriteFileSystem(renderer_id, result.filesystem_id); 313 policy->GrantDeleteFromFileSystem(renderer_id, result.filesystem_id); 314 } 315 } 316 317 result.id = result.filesystem_id + ":" + result.registered_name; 318 return result; 319 } 320 321 void PrepareFilesForWritableApp( 322 const std::vector<base::FilePath>& paths, 323 Profile* profile, 324 bool is_directory, 325 const base::Closure& on_success, 326 const base::Callback<void(const base::FilePath&)>& on_failure) { 327 scoped_refptr<WritableFileChecker> checker(new WritableFileChecker( 328 paths, profile, is_directory, on_success, on_failure)); 329 checker->Check(); 330 } 331 332 bool HasFileSystemWritePermission(const Extension* extension) { 333 return extension->permissions_data()->HasAPIPermission( 334 APIPermission::kFileSystemWrite); 335 } 336 337 bool ValidateFileEntryAndGetPath( 338 const std::string& filesystem_name, 339 const std::string& filesystem_path, 340 const content::RenderViewHost* render_view_host, 341 base::FilePath* file_path, 342 std::string* error) { 343 if (filesystem_path.empty()) { 344 *error = kInvalidParameters; 345 return false; 346 } 347 348 std::string filesystem_id; 349 if (!storage::CrackIsolatedFileSystemName(filesystem_name, &filesystem_id)) { 350 *error = kInvalidParameters; 351 return false; 352 } 353 354 // Only return the display path if the process has read access to the 355 // filesystem. 356 content::ChildProcessSecurityPolicy* policy = 357 content::ChildProcessSecurityPolicy::GetInstance(); 358 if (!policy->CanReadFileSystem(render_view_host->GetProcess()->GetID(), 359 filesystem_id)) { 360 *error = kSecurityError; 361 return false; 362 } 363 364 storage::IsolatedContext* context = storage::IsolatedContext::GetInstance(); 365 base::FilePath relative_path = 366 base::FilePath::FromUTF8Unsafe(filesystem_path); 367 base::FilePath virtual_path = context->CreateVirtualRootPath(filesystem_id) 368 .Append(relative_path); 369 storage::FileSystemType type; 370 storage::FileSystemMountOption mount_option; 371 std::string cracked_id; 372 if (!context->CrackVirtualPath( 373 virtual_path, &filesystem_id, &type, &cracked_id, file_path, 374 &mount_option)) { 375 *error = kInvalidParameters; 376 return false; 377 } 378 379 // The file system API is only intended to operate on file entries that 380 // correspond to a native file, selected by the user so only allow file 381 // systems returned by the file system API or from a drag and drop operation. 382 if (type != storage::kFileSystemTypeNativeForPlatformApp && 383 type != storage::kFileSystemTypeDragged) { 384 *error = kInvalidParameters; 385 return false; 386 } 387 388 return true; 389 } 390 391 } // namespace app_file_handler_util 392 393 } // namespace extensions 394