// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "webkit/browser/fileapi/file_system_context.h" #include "base/bind.h" #include "base/single_thread_task_runner.h" #include "base/stl_util.h" #include "base/task_runner_util.h" #include "url/gurl.h" #include "webkit/browser/blob/file_stream_reader.h" #include "webkit/browser/fileapi/copy_or_move_file_validator.h" #include "webkit/browser/fileapi/external_mount_points.h" #include "webkit/browser/fileapi/file_permission_policy.h" #include "webkit/browser/fileapi/file_stream_writer.h" #include "webkit/browser/fileapi/file_system_file_util.h" #include "webkit/browser/fileapi/file_system_operation.h" #include "webkit/browser/fileapi/file_system_operation_runner.h" #include "webkit/browser/fileapi/file_system_options.h" #include "webkit/browser/fileapi/file_system_quota_client.h" #include "webkit/browser/fileapi/file_system_url.h" #include "webkit/browser/fileapi/isolated_context.h" #include "webkit/browser/fileapi/isolated_file_system_backend.h" #include "webkit/browser/fileapi/mount_points.h" #include "webkit/browser/fileapi/quota/quota_reservation.h" #include "webkit/browser/fileapi/sandbox_file_system_backend.h" #include "webkit/browser/quota/quota_manager.h" #include "webkit/browser/quota/special_storage_policy.h" #include "webkit/common/fileapi/file_system_info.h" #include "webkit/common/fileapi/file_system_util.h" using quota::QuotaClient; namespace fileapi { namespace { QuotaClient* CreateQuotaClient( FileSystemContext* context, bool is_incognito) { return new FileSystemQuotaClient(context, is_incognito); } void DidGetMetadataForResolveURL( const base::FilePath& path, const FileSystemContext::ResolveURLCallback& callback, const FileSystemInfo& info, base::PlatformFileError error, const base::PlatformFileInfo& file_info) { if (error != base::PLATFORM_FILE_OK) { callback.Run(error, FileSystemInfo(), base::FilePath(), false); return; } callback.Run(error, info, path, file_info.is_directory); } } // namespace // static int FileSystemContext::GetPermissionPolicy(FileSystemType type) { switch (type) { case kFileSystemTypeTemporary: case kFileSystemTypePersistent: case kFileSystemTypeSyncable: return FILE_PERMISSION_SANDBOX; case kFileSystemTypeDrive: case kFileSystemTypeNativeForPlatformApp: case kFileSystemTypeNativeLocal: return FILE_PERMISSION_USE_FILE_PERMISSION; case kFileSystemTypeRestrictedNativeLocal: return FILE_PERMISSION_READ_ONLY | FILE_PERMISSION_USE_FILE_PERMISSION; // Following types are only accessed via IsolatedFileSystem, and // don't have their own permission policies. case kFileSystemTypeDeviceMedia: case kFileSystemTypeDragged: case kFileSystemTypeForTransientFile: case kFileSystemTypeIphoto: case kFileSystemTypeItunes: case kFileSystemTypeNativeMedia: case kFileSystemTypePicasa: case kFileSystemTypePluginPrivate: return FILE_PERMISSION_ALWAYS_DENY; // Following types only appear as mount_type, and will not be // queried for their permission policies. case kFileSystemTypeIsolated: case kFileSystemTypeExternal: return FILE_PERMISSION_ALWAYS_DENY; // Following types should not be used to access files by FileAPI clients. case kFileSystemTypeTest: case kFileSystemTypeSyncableForInternalSync: case kFileSystemInternalTypeEnumEnd: case kFileSystemInternalTypeEnumStart: case kFileSystemTypeUnknown: return FILE_PERMISSION_ALWAYS_DENY; } NOTREACHED(); return FILE_PERMISSION_ALWAYS_DENY; } FileSystemContext::FileSystemContext( base::SingleThreadTaskRunner* io_task_runner, base::SequencedTaskRunner* file_task_runner, ExternalMountPoints* external_mount_points, quota::SpecialStoragePolicy* special_storage_policy, quota::QuotaManagerProxy* quota_manager_proxy, ScopedVector<FileSystemBackend> additional_backends, const base::FilePath& partition_path, const FileSystemOptions& options) : io_task_runner_(io_task_runner), default_file_task_runner_(file_task_runner), quota_manager_proxy_(quota_manager_proxy), sandbox_delegate_(new SandboxFileSystemBackendDelegate( quota_manager_proxy, file_task_runner, partition_path, special_storage_policy, options)), sandbox_backend_(new SandboxFileSystemBackend( sandbox_delegate_.get())), isolated_backend_(new IsolatedFileSystemBackend()), plugin_private_backend_(new PluginPrivateFileSystemBackend( file_task_runner, partition_path, special_storage_policy, options)), additional_backends_(additional_backends.Pass()), external_mount_points_(external_mount_points), partition_path_(partition_path), is_incognito_(options.is_incognito()), operation_runner_(new FileSystemOperationRunner(this)) { RegisterBackend(sandbox_backend_.get()); RegisterBackend(isolated_backend_.get()); RegisterBackend(plugin_private_backend_.get()); for (ScopedVector<FileSystemBackend>::const_iterator iter = additional_backends_.begin(); iter != additional_backends_.end(); ++iter) { RegisterBackend(*iter); } if (quota_manager_proxy) { // Quota client assumes all backends have registered. quota_manager_proxy->RegisterClient(CreateQuotaClient( this, options.is_incognito())); } sandbox_backend_->Initialize(this); isolated_backend_->Initialize(this); plugin_private_backend_->Initialize(this); for (ScopedVector<FileSystemBackend>::const_iterator iter = additional_backends_.begin(); iter != additional_backends_.end(); ++iter) { (*iter)->Initialize(this); } // Additional mount points must be added before regular system-wide // mount points. if (external_mount_points) url_crackers_.push_back(external_mount_points); url_crackers_.push_back(ExternalMountPoints::GetSystemInstance()); url_crackers_.push_back(IsolatedContext::GetInstance()); } bool FileSystemContext::DeleteDataForOriginOnFileThread( const GURL& origin_url) { DCHECK(default_file_task_runner()->RunsTasksOnCurrentThread()); DCHECK(origin_url == origin_url.GetOrigin()); bool success = true; for (FileSystemBackendMap::iterator iter = backend_map_.begin(); iter != backend_map_.end(); ++iter) { FileSystemBackend* backend = iter->second; if (!backend->GetQuotaUtil()) continue; if (backend->GetQuotaUtil()->DeleteOriginDataOnFileThread( this, quota_manager_proxy(), origin_url, iter->first) != base::PLATFORM_FILE_OK) { // Continue the loop, but record the failure. success = false; } } return success; } scoped_refptr<QuotaReservation> FileSystemContext::CreateQuotaReservationOnFileTaskRunner( const GURL& origin_url, FileSystemType type) { DCHECK(default_file_task_runner()->RunsTasksOnCurrentThread()); FileSystemBackend* backend = GetFileSystemBackend(type); if (!backend || !backend->GetQuotaUtil()) return scoped_refptr<QuotaReservation>(); return backend->GetQuotaUtil()->CreateQuotaReservationOnFileTaskRunner( origin_url, type); } void FileSystemContext::Shutdown() { if (!io_task_runner_->RunsTasksOnCurrentThread()) { io_task_runner_->PostTask( FROM_HERE, base::Bind(&FileSystemContext::Shutdown, make_scoped_refptr(this))); return; } operation_runner_->Shutdown(); } FileSystemQuotaUtil* FileSystemContext::GetQuotaUtil(FileSystemType type) const { FileSystemBackend* backend = GetFileSystemBackend(type); if (!backend) return NULL; return backend->GetQuotaUtil(); } AsyncFileUtil* FileSystemContext::GetAsyncFileUtil( FileSystemType type) const { FileSystemBackend* backend = GetFileSystemBackend(type); if (!backend) return NULL; return backend->GetAsyncFileUtil(type); } CopyOrMoveFileValidatorFactory* FileSystemContext::GetCopyOrMoveFileValidatorFactory( FileSystemType type, base::PlatformFileError* error_code) const { DCHECK(error_code); *error_code = base::PLATFORM_FILE_OK; FileSystemBackend* backend = GetFileSystemBackend(type); if (!backend) return NULL; return backend->GetCopyOrMoveFileValidatorFactory( type, error_code); } FileSystemBackend* FileSystemContext::GetFileSystemBackend( FileSystemType type) const { FileSystemBackendMap::const_iterator found = backend_map_.find(type); if (found != backend_map_.end()) return found->second; NOTREACHED() << "Unknown filesystem type: " << type; return NULL; } bool FileSystemContext::IsSandboxFileSystem(FileSystemType type) const { FileSystemBackendMap::const_iterator found = backend_map_.find(type); return found != backend_map_.end() && found->second->GetQuotaUtil(); } const UpdateObserverList* FileSystemContext::GetUpdateObservers( FileSystemType type) const { FileSystemBackend* backend = GetFileSystemBackend(type); if (backend->GetQuotaUtil()) return backend->GetQuotaUtil()->GetUpdateObservers(type); return NULL; } const AccessObserverList* FileSystemContext::GetAccessObservers( FileSystemType type) const { FileSystemBackend* backend = GetFileSystemBackend(type); if (backend->GetQuotaUtil()) return backend->GetQuotaUtil()->GetAccessObservers(type); return NULL; } void FileSystemContext::GetFileSystemTypes( std::vector<FileSystemType>* types) const { types->clear(); for (FileSystemBackendMap::const_iterator iter = backend_map_.begin(); iter != backend_map_.end(); ++iter) types->push_back(iter->first); } ExternalFileSystemBackend* FileSystemContext::external_backend() const { return static_cast<ExternalFileSystemBackend*>( GetFileSystemBackend(kFileSystemTypeExternal)); } void FileSystemContext::OpenFileSystem( const GURL& origin_url, FileSystemType type, OpenFileSystemMode mode, const OpenFileSystemCallback& callback) { DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); DCHECK(!callback.is_null()); if (!FileSystemContext::IsSandboxFileSystem(type)) { // Disallow opening a non-sandboxed filesystem. callback.Run(GURL(), std::string(), base::PLATFORM_FILE_ERROR_SECURITY); return; } FileSystemBackend* backend = GetFileSystemBackend(type); if (!backend) { callback.Run(GURL(), std::string(), base::PLATFORM_FILE_ERROR_SECURITY); return; } backend->OpenFileSystem(origin_url, type, mode, callback); } void FileSystemContext::ResolveURL( const FileSystemURL& url, const ResolveURLCallback& callback) { DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); DCHECK(!callback.is_null()); if (!FileSystemContext::IsSandboxFileSystem(url.type())) { #ifdef OS_CHROMEOS // Do not have to open a non-sandboxed filesystem. // TODO(nhiroki): For now we assume this path is called only on ChromeOS, // but this assumption may be broken in the future and we should handle // more generally. http://crbug.com/304062. FileSystemInfo info = GetFileSystemInfoForChromeOS(url.origin()); DidOpenFileSystemForResolveURL( url, callback, info.root_url, info.name, base::PLATFORM_FILE_OK); return; #endif callback.Run(base::PLATFORM_FILE_ERROR_SECURITY, FileSystemInfo(), base::FilePath(), false); return; } FileSystemBackend* backend = GetFileSystemBackend(url.type()); if (!backend) { callback.Run(base::PLATFORM_FILE_ERROR_SECURITY, FileSystemInfo(), base::FilePath(), false); return; } backend->OpenFileSystem( url.origin(), url.type(), OPEN_FILE_SYSTEM_FAIL_IF_NONEXISTENT, base::Bind(&FileSystemContext::DidOpenFileSystemForResolveURL, this, url, callback)); } void FileSystemContext::DeleteFileSystem( const GURL& origin_url, FileSystemType type, const StatusCallback& callback) { DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); DCHECK(origin_url == origin_url.GetOrigin()); DCHECK(!callback.is_null()); FileSystemBackend* backend = GetFileSystemBackend(type); if (!backend) { callback.Run(base::PLATFORM_FILE_ERROR_SECURITY); return; } if (!backend->GetQuotaUtil()) { callback.Run(base::PLATFORM_FILE_ERROR_INVALID_OPERATION); return; } base::PostTaskAndReplyWithResult( default_file_task_runner(), FROM_HERE, // It is safe to pass Unretained(quota_util) since context owns it. base::Bind(&FileSystemQuotaUtil::DeleteOriginDataOnFileThread, base::Unretained(backend->GetQuotaUtil()), make_scoped_refptr(this), base::Unretained(quota_manager_proxy()), origin_url, type), callback); } scoped_ptr<webkit_blob::FileStreamReader> FileSystemContext::CreateFileStreamReader( const FileSystemURL& url, int64 offset, const base::Time& expected_modification_time) { if (!url.is_valid()) return scoped_ptr<webkit_blob::FileStreamReader>(); FileSystemBackend* backend = GetFileSystemBackend(url.type()); if (!backend) return scoped_ptr<webkit_blob::FileStreamReader>(); return backend->CreateFileStreamReader( url, offset, expected_modification_time, this); } scoped_ptr<FileStreamWriter> FileSystemContext::CreateFileStreamWriter( const FileSystemURL& url, int64 offset) { if (!url.is_valid()) return scoped_ptr<FileStreamWriter>(); FileSystemBackend* backend = GetFileSystemBackend(url.type()); if (!backend) return scoped_ptr<FileStreamWriter>(); return backend->CreateFileStreamWriter(url, offset, this); } scoped_ptr<FileSystemOperationRunner> FileSystemContext::CreateFileSystemOperationRunner() { return make_scoped_ptr(new FileSystemOperationRunner(this)); } FileSystemURL FileSystemContext::CrackURL(const GURL& url) const { return CrackFileSystemURL(FileSystemURL(url)); } FileSystemURL FileSystemContext::CreateCrackedFileSystemURL( const GURL& origin, FileSystemType type, const base::FilePath& path) const { return CrackFileSystemURL(FileSystemURL(origin, type, path)); } #if defined(OS_CHROMEOS) void FileSystemContext::EnableTemporaryFileSystemInIncognito() { sandbox_backend_->set_enable_temporary_file_system_in_incognito(true); } #endif bool FileSystemContext::CanServeURLRequest(const FileSystemURL& url) const { // We never support accessing files in isolated filesystems via an URL. if (url.mount_type() == kFileSystemTypeIsolated) return false; #if defined(OS_CHROMEOS) if (url.type() == kFileSystemTypeTemporary && sandbox_backend_->enable_temporary_file_system_in_incognito()) { return true; } #endif return !is_incognito_ || !FileSystemContext::IsSandboxFileSystem(url.type()); } void FileSystemContext::OpenPluginPrivateFileSystem( const GURL& origin_url, FileSystemType type, const std::string& filesystem_id, const std::string& plugin_id, OpenFileSystemMode mode, const StatusCallback& callback) { DCHECK(plugin_private_backend_); plugin_private_backend_->OpenPrivateFileSystem( origin_url, type, filesystem_id, plugin_id, mode, callback); } FileSystemContext::~FileSystemContext() { } void FileSystemContext::DeleteOnCorrectThread() const { if (!io_task_runner_->RunsTasksOnCurrentThread() && io_task_runner_->DeleteSoon(FROM_HERE, this)) { return; } delete this; } FileSystemOperation* FileSystemContext::CreateFileSystemOperation( const FileSystemURL& url, base::PlatformFileError* error_code) { if (!url.is_valid()) { if (error_code) *error_code = base::PLATFORM_FILE_ERROR_INVALID_URL; return NULL; } FileSystemBackend* backend = GetFileSystemBackend(url.type()); if (!backend) { if (error_code) *error_code = base::PLATFORM_FILE_ERROR_FAILED; return NULL; } base::PlatformFileError fs_error = base::PLATFORM_FILE_OK; FileSystemOperation* operation = backend->CreateFileSystemOperation(url, this, &fs_error); if (error_code) *error_code = fs_error; return operation; } FileSystemURL FileSystemContext::CrackFileSystemURL( const FileSystemURL& url) const { if (!url.is_valid()) return FileSystemURL(); // The returned value in case there is no crackers which can crack the url. // This is valid situation for non isolated/external file systems. FileSystemURL current = url; // File system may be mounted multiple times (e.g., an isolated filesystem on // top of an external filesystem). Hence cracking needs to be iterated. for (;;) { FileSystemURL cracked = current; for (size_t i = 0; i < url_crackers_.size(); ++i) { if (!url_crackers_[i]->HandlesFileSystemMountType(current.type())) continue; cracked = url_crackers_[i]->CrackFileSystemURL(current); if (cracked.is_valid()) break; } if (cracked == current) break; current = cracked; } return current; } void FileSystemContext::RegisterBackend(FileSystemBackend* backend) { const FileSystemType mount_types[] = { kFileSystemTypeTemporary, kFileSystemTypePersistent, kFileSystemTypeIsolated, kFileSystemTypeExternal, }; // Register file system backends for public mount types. for (size_t j = 0; j < ARRAYSIZE_UNSAFE(mount_types); ++j) { if (backend->CanHandleType(mount_types[j])) { const bool inserted = backend_map_.insert( std::make_pair(mount_types[j], backend)).second; DCHECK(inserted); } } // Register file system backends for internal types. for (int t = kFileSystemInternalTypeEnumStart + 1; t < kFileSystemInternalTypeEnumEnd; ++t) { FileSystemType type = static_cast<FileSystemType>(t); if (backend->CanHandleType(type)) { const bool inserted = backend_map_.insert( std::make_pair(type, backend)).second; DCHECK(inserted); } } } void FileSystemContext::DidOpenFileSystemForResolveURL( const FileSystemURL& url, const FileSystemContext::ResolveURLCallback& callback, const GURL& filesystem_root, const std::string& filesystem_name, base::PlatformFileError error) { DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); if (error != base::PLATFORM_FILE_OK) { callback.Run(error, FileSystemInfo(), base::FilePath(), false); return; } fileapi::FileSystemInfo info( filesystem_name, filesystem_root, url.mount_type()); // Extract the virtual path not containing a filesystem type part from |url|. base::FilePath parent = CrackURL(filesystem_root).virtual_path(); base::FilePath child = url.virtual_path(); base::FilePath path; if (parent.empty()) { path = child; } else if (parent != child) { bool result = parent.AppendRelativePath(child, &path); DCHECK(result); } operation_runner()->GetMetadata( url, base::Bind(&DidGetMetadataForResolveURL, path, callback, info)); } } // namespace fileapi