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