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