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 "webkit/browser/fileapi/file_system_context.h" 6 7 #include "base/bind.h" 8 #include "base/single_thread_task_runner.h" 9 #include "base/stl_util.h" 10 #include "base/task_runner_util.h" 11 #include "url/gurl.h" 12 #include "webkit/browser/blob/file_stream_reader.h" 13 #include "webkit/browser/fileapi/copy_or_move_file_validator.h" 14 #include "webkit/browser/fileapi/external_mount_points.h" 15 #include "webkit/browser/fileapi/file_permission_policy.h" 16 #include "webkit/browser/fileapi/file_stream_writer.h" 17 #include "webkit/browser/fileapi/file_system_file_util.h" 18 #include "webkit/browser/fileapi/file_system_operation.h" 19 #include "webkit/browser/fileapi/file_system_operation_runner.h" 20 #include "webkit/browser/fileapi/file_system_options.h" 21 #include "webkit/browser/fileapi/file_system_quota_client.h" 22 #include "webkit/browser/fileapi/file_system_url.h" 23 #include "webkit/browser/fileapi/isolated_context.h" 24 #include "webkit/browser/fileapi/isolated_file_system_backend.h" 25 #include "webkit/browser/fileapi/mount_points.h" 26 #include "webkit/browser/fileapi/quota/quota_reservation.h" 27 #include "webkit/browser/fileapi/sandbox_file_system_backend.h" 28 #include "webkit/browser/quota/quota_manager.h" 29 #include "webkit/browser/quota/special_storage_policy.h" 30 #include "webkit/common/fileapi/file_system_info.h" 31 #include "webkit/common/fileapi/file_system_util.h" 32 33 using quota::QuotaClient; 34 35 namespace fileapi { 36 37 namespace { 38 39 QuotaClient* CreateQuotaClient( 40 FileSystemContext* context, 41 bool is_incognito) { 42 return new FileSystemQuotaClient(context, is_incognito); 43 } 44 45 46 void DidGetMetadataForResolveURL( 47 const base::FilePath& path, 48 const FileSystemContext::ResolveURLCallback& callback, 49 const FileSystemInfo& info, 50 base::PlatformFileError error, 51 const base::PlatformFileInfo& file_info) { 52 if (error != base::PLATFORM_FILE_OK) { 53 callback.Run(error, FileSystemInfo(), base::FilePath(), false); 54 return; 55 } 56 callback.Run(error, info, path, file_info.is_directory); 57 } 58 59 } // namespace 60 61 // static 62 int FileSystemContext::GetPermissionPolicy(FileSystemType type) { 63 switch (type) { 64 case kFileSystemTypeTemporary: 65 case kFileSystemTypePersistent: 66 case kFileSystemTypeSyncable: 67 return FILE_PERMISSION_SANDBOX; 68 69 case kFileSystemTypeDrive: 70 case kFileSystemTypeNativeForPlatformApp: 71 case kFileSystemTypeNativeLocal: 72 return FILE_PERMISSION_USE_FILE_PERMISSION; 73 74 case kFileSystemTypeRestrictedNativeLocal: 75 return FILE_PERMISSION_READ_ONLY | 76 FILE_PERMISSION_USE_FILE_PERMISSION; 77 78 // Following types are only accessed via IsolatedFileSystem, and 79 // don't have their own permission policies. 80 case kFileSystemTypeDeviceMedia: 81 case kFileSystemTypeDragged: 82 case kFileSystemTypeForTransientFile: 83 case kFileSystemTypeIphoto: 84 case kFileSystemTypeItunes: 85 case kFileSystemTypeNativeMedia: 86 case kFileSystemTypePicasa: 87 case kFileSystemTypePluginPrivate: 88 return FILE_PERMISSION_ALWAYS_DENY; 89 90 // Following types only appear as mount_type, and will not be 91 // queried for their permission policies. 92 case kFileSystemTypeIsolated: 93 case kFileSystemTypeExternal: 94 return FILE_PERMISSION_ALWAYS_DENY; 95 96 // Following types should not be used to access files by FileAPI clients. 97 case kFileSystemTypeTest: 98 case kFileSystemTypeSyncableForInternalSync: 99 case kFileSystemInternalTypeEnumEnd: 100 case kFileSystemInternalTypeEnumStart: 101 case kFileSystemTypeUnknown: 102 return FILE_PERMISSION_ALWAYS_DENY; 103 } 104 NOTREACHED(); 105 return FILE_PERMISSION_ALWAYS_DENY; 106 } 107 108 FileSystemContext::FileSystemContext( 109 base::SingleThreadTaskRunner* io_task_runner, 110 base::SequencedTaskRunner* file_task_runner, 111 ExternalMountPoints* external_mount_points, 112 quota::SpecialStoragePolicy* special_storage_policy, 113 quota::QuotaManagerProxy* quota_manager_proxy, 114 ScopedVector<FileSystemBackend> additional_backends, 115 const base::FilePath& partition_path, 116 const FileSystemOptions& options) 117 : io_task_runner_(io_task_runner), 118 default_file_task_runner_(file_task_runner), 119 quota_manager_proxy_(quota_manager_proxy), 120 sandbox_delegate_(new SandboxFileSystemBackendDelegate( 121 quota_manager_proxy, 122 file_task_runner, 123 partition_path, 124 special_storage_policy, 125 options)), 126 sandbox_backend_(new SandboxFileSystemBackend( 127 sandbox_delegate_.get())), 128 isolated_backend_(new IsolatedFileSystemBackend()), 129 plugin_private_backend_(new PluginPrivateFileSystemBackend( 130 file_task_runner, 131 partition_path, 132 special_storage_policy, 133 options)), 134 additional_backends_(additional_backends.Pass()), 135 external_mount_points_(external_mount_points), 136 partition_path_(partition_path), 137 is_incognito_(options.is_incognito()), 138 operation_runner_(new FileSystemOperationRunner(this)) { 139 RegisterBackend(sandbox_backend_.get()); 140 RegisterBackend(isolated_backend_.get()); 141 RegisterBackend(plugin_private_backend_.get()); 142 143 for (ScopedVector<FileSystemBackend>::const_iterator iter = 144 additional_backends_.begin(); 145 iter != additional_backends_.end(); ++iter) { 146 RegisterBackend(*iter); 147 } 148 149 if (quota_manager_proxy) { 150 // Quota client assumes all backends have registered. 151 quota_manager_proxy->RegisterClient(CreateQuotaClient( 152 this, options.is_incognito())); 153 } 154 155 sandbox_backend_->Initialize(this); 156 isolated_backend_->Initialize(this); 157 plugin_private_backend_->Initialize(this); 158 for (ScopedVector<FileSystemBackend>::const_iterator iter = 159 additional_backends_.begin(); 160 iter != additional_backends_.end(); ++iter) { 161 (*iter)->Initialize(this); 162 } 163 164 // Additional mount points must be added before regular system-wide 165 // mount points. 166 if (external_mount_points) 167 url_crackers_.push_back(external_mount_points); 168 url_crackers_.push_back(ExternalMountPoints::GetSystemInstance()); 169 url_crackers_.push_back(IsolatedContext::GetInstance()); 170 } 171 172 bool FileSystemContext::DeleteDataForOriginOnFileThread( 173 const GURL& origin_url) { 174 DCHECK(default_file_task_runner()->RunsTasksOnCurrentThread()); 175 DCHECK(origin_url == origin_url.GetOrigin()); 176 177 bool success = true; 178 for (FileSystemBackendMap::iterator iter = backend_map_.begin(); 179 iter != backend_map_.end(); 180 ++iter) { 181 FileSystemBackend* backend = iter->second; 182 if (!backend->GetQuotaUtil()) 183 continue; 184 if (backend->GetQuotaUtil()->DeleteOriginDataOnFileThread( 185 this, quota_manager_proxy(), origin_url, iter->first) 186 != base::PLATFORM_FILE_OK) { 187 // Continue the loop, but record the failure. 188 success = false; 189 } 190 } 191 192 return success; 193 } 194 195 scoped_refptr<QuotaReservation> 196 FileSystemContext::CreateQuotaReservationOnFileTaskRunner( 197 const GURL& origin_url, 198 FileSystemType type) { 199 DCHECK(default_file_task_runner()->RunsTasksOnCurrentThread()); 200 FileSystemBackend* backend = GetFileSystemBackend(type); 201 if (!backend || !backend->GetQuotaUtil()) 202 return scoped_refptr<QuotaReservation>(); 203 return backend->GetQuotaUtil()->CreateQuotaReservationOnFileTaskRunner( 204 origin_url, type); 205 } 206 207 void FileSystemContext::Shutdown() { 208 if (!io_task_runner_->RunsTasksOnCurrentThread()) { 209 io_task_runner_->PostTask( 210 FROM_HERE, base::Bind(&FileSystemContext::Shutdown, 211 make_scoped_refptr(this))); 212 return; 213 } 214 operation_runner_->Shutdown(); 215 } 216 217 FileSystemQuotaUtil* 218 FileSystemContext::GetQuotaUtil(FileSystemType type) const { 219 FileSystemBackend* backend = GetFileSystemBackend(type); 220 if (!backend) 221 return NULL; 222 return backend->GetQuotaUtil(); 223 } 224 225 AsyncFileUtil* FileSystemContext::GetAsyncFileUtil( 226 FileSystemType type) const { 227 FileSystemBackend* backend = GetFileSystemBackend(type); 228 if (!backend) 229 return NULL; 230 return backend->GetAsyncFileUtil(type); 231 } 232 233 CopyOrMoveFileValidatorFactory* 234 FileSystemContext::GetCopyOrMoveFileValidatorFactory( 235 FileSystemType type, base::PlatformFileError* error_code) const { 236 DCHECK(error_code); 237 *error_code = base::PLATFORM_FILE_OK; 238 FileSystemBackend* backend = GetFileSystemBackend(type); 239 if (!backend) 240 return NULL; 241 return backend->GetCopyOrMoveFileValidatorFactory( 242 type, error_code); 243 } 244 245 FileSystemBackend* FileSystemContext::GetFileSystemBackend( 246 FileSystemType type) const { 247 FileSystemBackendMap::const_iterator found = backend_map_.find(type); 248 if (found != backend_map_.end()) 249 return found->second; 250 NOTREACHED() << "Unknown filesystem type: " << type; 251 return NULL; 252 } 253 254 bool FileSystemContext::IsSandboxFileSystem(FileSystemType type) const { 255 FileSystemBackendMap::const_iterator found = backend_map_.find(type); 256 return found != backend_map_.end() && found->second->GetQuotaUtil(); 257 } 258 259 const UpdateObserverList* FileSystemContext::GetUpdateObservers( 260 FileSystemType type) const { 261 FileSystemBackend* backend = GetFileSystemBackend(type); 262 if (backend->GetQuotaUtil()) 263 return backend->GetQuotaUtil()->GetUpdateObservers(type); 264 return NULL; 265 } 266 267 const AccessObserverList* FileSystemContext::GetAccessObservers( 268 FileSystemType type) const { 269 FileSystemBackend* backend = GetFileSystemBackend(type); 270 if (backend->GetQuotaUtil()) 271 return backend->GetQuotaUtil()->GetAccessObservers(type); 272 return NULL; 273 } 274 275 void FileSystemContext::GetFileSystemTypes( 276 std::vector<FileSystemType>* types) const { 277 types->clear(); 278 for (FileSystemBackendMap::const_iterator iter = backend_map_.begin(); 279 iter != backend_map_.end(); ++iter) 280 types->push_back(iter->first); 281 } 282 283 ExternalFileSystemBackend* 284 FileSystemContext::external_backend() const { 285 return static_cast<ExternalFileSystemBackend*>( 286 GetFileSystemBackend(kFileSystemTypeExternal)); 287 } 288 289 void FileSystemContext::OpenFileSystem( 290 const GURL& origin_url, 291 FileSystemType type, 292 OpenFileSystemMode mode, 293 const OpenFileSystemCallback& callback) { 294 DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); 295 DCHECK(!callback.is_null()); 296 297 if (!FileSystemContext::IsSandboxFileSystem(type)) { 298 // Disallow opening a non-sandboxed filesystem. 299 callback.Run(GURL(), std::string(), base::PLATFORM_FILE_ERROR_SECURITY); 300 return; 301 } 302 303 FileSystemBackend* backend = GetFileSystemBackend(type); 304 if (!backend) { 305 callback.Run(GURL(), std::string(), base::PLATFORM_FILE_ERROR_SECURITY); 306 return; 307 } 308 309 backend->OpenFileSystem(origin_url, type, mode, callback); 310 } 311 312 void FileSystemContext::ResolveURL( 313 const FileSystemURL& url, 314 const ResolveURLCallback& callback) { 315 DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); 316 DCHECK(!callback.is_null()); 317 318 if (!FileSystemContext::IsSandboxFileSystem(url.type())) { 319 #ifdef OS_CHROMEOS 320 // Do not have to open a non-sandboxed filesystem. 321 // TODO(nhiroki): For now we assume this path is called only on ChromeOS, 322 // but this assumption may be broken in the future and we should handle 323 // more generally. http://crbug.com/304062. 324 FileSystemInfo info = GetFileSystemInfoForChromeOS(url.origin()); 325 DidOpenFileSystemForResolveURL( 326 url, callback, info.root_url, info.name, base::PLATFORM_FILE_OK); 327 return; 328 #endif 329 callback.Run(base::PLATFORM_FILE_ERROR_SECURITY, 330 FileSystemInfo(), base::FilePath(), false); 331 return; 332 } 333 334 FileSystemBackend* backend = GetFileSystemBackend(url.type()); 335 if (!backend) { 336 callback.Run(base::PLATFORM_FILE_ERROR_SECURITY, 337 FileSystemInfo(), base::FilePath(), false); 338 return; 339 } 340 341 backend->OpenFileSystem( 342 url.origin(), url.type(), 343 OPEN_FILE_SYSTEM_FAIL_IF_NONEXISTENT, 344 base::Bind(&FileSystemContext::DidOpenFileSystemForResolveURL, 345 this, url, callback)); 346 } 347 348 void FileSystemContext::DeleteFileSystem( 349 const GURL& origin_url, 350 FileSystemType type, 351 const StatusCallback& callback) { 352 DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); 353 DCHECK(origin_url == origin_url.GetOrigin()); 354 DCHECK(!callback.is_null()); 355 356 FileSystemBackend* backend = GetFileSystemBackend(type); 357 if (!backend) { 358 callback.Run(base::PLATFORM_FILE_ERROR_SECURITY); 359 return; 360 } 361 if (!backend->GetQuotaUtil()) { 362 callback.Run(base::PLATFORM_FILE_ERROR_INVALID_OPERATION); 363 return; 364 } 365 366 base::PostTaskAndReplyWithResult( 367 default_file_task_runner(), 368 FROM_HERE, 369 // It is safe to pass Unretained(quota_util) since context owns it. 370 base::Bind(&FileSystemQuotaUtil::DeleteOriginDataOnFileThread, 371 base::Unretained(backend->GetQuotaUtil()), 372 make_scoped_refptr(this), 373 base::Unretained(quota_manager_proxy()), 374 origin_url, 375 type), 376 callback); 377 } 378 379 scoped_ptr<webkit_blob::FileStreamReader> 380 FileSystemContext::CreateFileStreamReader( 381 const FileSystemURL& url, 382 int64 offset, 383 const base::Time& expected_modification_time) { 384 if (!url.is_valid()) 385 return scoped_ptr<webkit_blob::FileStreamReader>(); 386 FileSystemBackend* backend = GetFileSystemBackend(url.type()); 387 if (!backend) 388 return scoped_ptr<webkit_blob::FileStreamReader>(); 389 return backend->CreateFileStreamReader( 390 url, offset, expected_modification_time, this); 391 } 392 393 scoped_ptr<FileStreamWriter> FileSystemContext::CreateFileStreamWriter( 394 const FileSystemURL& url, 395 int64 offset) { 396 if (!url.is_valid()) 397 return scoped_ptr<FileStreamWriter>(); 398 FileSystemBackend* backend = GetFileSystemBackend(url.type()); 399 if (!backend) 400 return scoped_ptr<FileStreamWriter>(); 401 return backend->CreateFileStreamWriter(url, offset, this); 402 } 403 404 scoped_ptr<FileSystemOperationRunner> 405 FileSystemContext::CreateFileSystemOperationRunner() { 406 return make_scoped_ptr(new FileSystemOperationRunner(this)); 407 } 408 409 FileSystemURL FileSystemContext::CrackURL(const GURL& url) const { 410 return CrackFileSystemURL(FileSystemURL(url)); 411 } 412 413 FileSystemURL FileSystemContext::CreateCrackedFileSystemURL( 414 const GURL& origin, 415 FileSystemType type, 416 const base::FilePath& path) const { 417 return CrackFileSystemURL(FileSystemURL(origin, type, path)); 418 } 419 420 #if defined(OS_CHROMEOS) 421 void FileSystemContext::EnableTemporaryFileSystemInIncognito() { 422 sandbox_backend_->set_enable_temporary_file_system_in_incognito(true); 423 } 424 #endif 425 426 bool FileSystemContext::CanServeURLRequest(const FileSystemURL& url) const { 427 // We never support accessing files in isolated filesystems via an URL. 428 if (url.mount_type() == kFileSystemTypeIsolated) 429 return false; 430 #if defined(OS_CHROMEOS) 431 if (url.type() == kFileSystemTypeTemporary && 432 sandbox_backend_->enable_temporary_file_system_in_incognito()) { 433 return true; 434 } 435 #endif 436 return !is_incognito_ || !FileSystemContext::IsSandboxFileSystem(url.type()); 437 } 438 439 void FileSystemContext::OpenPluginPrivateFileSystem( 440 const GURL& origin_url, 441 FileSystemType type, 442 const std::string& filesystem_id, 443 const std::string& plugin_id, 444 OpenFileSystemMode mode, 445 const StatusCallback& callback) { 446 DCHECK(plugin_private_backend_); 447 plugin_private_backend_->OpenPrivateFileSystem( 448 origin_url, type, filesystem_id, plugin_id, mode, callback); 449 } 450 451 FileSystemContext::~FileSystemContext() { 452 } 453 454 void FileSystemContext::DeleteOnCorrectThread() const { 455 if (!io_task_runner_->RunsTasksOnCurrentThread() && 456 io_task_runner_->DeleteSoon(FROM_HERE, this)) { 457 return; 458 } 459 delete this; 460 } 461 462 FileSystemOperation* FileSystemContext::CreateFileSystemOperation( 463 const FileSystemURL& url, base::PlatformFileError* error_code) { 464 if (!url.is_valid()) { 465 if (error_code) 466 *error_code = base::PLATFORM_FILE_ERROR_INVALID_URL; 467 return NULL; 468 } 469 470 FileSystemBackend* backend = GetFileSystemBackend(url.type()); 471 if (!backend) { 472 if (error_code) 473 *error_code = base::PLATFORM_FILE_ERROR_FAILED; 474 return NULL; 475 } 476 477 base::PlatformFileError fs_error = base::PLATFORM_FILE_OK; 478 FileSystemOperation* operation = 479 backend->CreateFileSystemOperation(url, this, &fs_error); 480 481 if (error_code) 482 *error_code = fs_error; 483 return operation; 484 } 485 486 FileSystemURL FileSystemContext::CrackFileSystemURL( 487 const FileSystemURL& url) const { 488 if (!url.is_valid()) 489 return FileSystemURL(); 490 491 // The returned value in case there is no crackers which can crack the url. 492 // This is valid situation for non isolated/external file systems. 493 FileSystemURL current = url; 494 495 // File system may be mounted multiple times (e.g., an isolated filesystem on 496 // top of an external filesystem). Hence cracking needs to be iterated. 497 for (;;) { 498 FileSystemURL cracked = current; 499 for (size_t i = 0; i < url_crackers_.size(); ++i) { 500 if (!url_crackers_[i]->HandlesFileSystemMountType(current.type())) 501 continue; 502 cracked = url_crackers_[i]->CrackFileSystemURL(current); 503 if (cracked.is_valid()) 504 break; 505 } 506 if (cracked == current) 507 break; 508 current = cracked; 509 } 510 return current; 511 } 512 513 void FileSystemContext::RegisterBackend(FileSystemBackend* backend) { 514 const FileSystemType mount_types[] = { 515 kFileSystemTypeTemporary, 516 kFileSystemTypePersistent, 517 kFileSystemTypeIsolated, 518 kFileSystemTypeExternal, 519 }; 520 // Register file system backends for public mount types. 521 for (size_t j = 0; j < ARRAYSIZE_UNSAFE(mount_types); ++j) { 522 if (backend->CanHandleType(mount_types[j])) { 523 const bool inserted = backend_map_.insert( 524 std::make_pair(mount_types[j], backend)).second; 525 DCHECK(inserted); 526 } 527 } 528 // Register file system backends for internal types. 529 for (int t = kFileSystemInternalTypeEnumStart + 1; 530 t < kFileSystemInternalTypeEnumEnd; ++t) { 531 FileSystemType type = static_cast<FileSystemType>(t); 532 if (backend->CanHandleType(type)) { 533 const bool inserted = backend_map_.insert( 534 std::make_pair(type, backend)).second; 535 DCHECK(inserted); 536 } 537 } 538 } 539 540 void FileSystemContext::DidOpenFileSystemForResolveURL( 541 const FileSystemURL& url, 542 const FileSystemContext::ResolveURLCallback& callback, 543 const GURL& filesystem_root, 544 const std::string& filesystem_name, 545 base::PlatformFileError error) { 546 DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); 547 548 if (error != base::PLATFORM_FILE_OK) { 549 callback.Run(error, FileSystemInfo(), base::FilePath(), false); 550 return; 551 } 552 553 fileapi::FileSystemInfo info( 554 filesystem_name, filesystem_root, url.mount_type()); 555 556 // Extract the virtual path not containing a filesystem type part from |url|. 557 base::FilePath parent = CrackURL(filesystem_root).virtual_path(); 558 base::FilePath child = url.virtual_path(); 559 base::FilePath path; 560 561 if (parent.empty()) { 562 path = child; 563 } else if (parent != child) { 564 bool result = parent.AppendRelativePath(child, &path); 565 DCHECK(result); 566 } 567 568 operation_runner()->GetMetadata( 569 url, base::Bind(&DidGetMetadataForResolveURL, path, callback, info)); 570 } 571 572 } // namespace fileapi 573