// Copyright 2013 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 "chrome/browser/chromeos/fileapi/file_system_backend.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/synchronization/lock.h" #include "chrome/browser/chromeos/fileapi/file_access_permissions.h" #include "chrome/browser/chromeos/fileapi/file_system_backend_delegate.h" #include "chromeos/dbus/cros_disks_client.h" #include "webkit/browser/blob/file_stream_reader.h" #include "webkit/browser/fileapi/async_file_util.h" #include "webkit/browser/fileapi/external_mount_points.h" #include "webkit/browser/fileapi/file_stream_writer.h" #include "webkit/browser/fileapi/file_system_context.h" #include "webkit/browser/fileapi/file_system_operation.h" #include "webkit/browser/fileapi/file_system_operation_context.h" #include "webkit/browser/fileapi/file_system_url.h" #include "webkit/browser/fileapi/isolated_context.h" namespace { const char kChromeUIScheme[] = "chrome"; } // namespace namespace chromeos { // static bool FileSystemBackend::CanHandleURL(const fileapi::FileSystemURL& url) { if (!url.is_valid()) return false; return url.type() == fileapi::kFileSystemTypeNativeLocal || url.type() == fileapi::kFileSystemTypeRestrictedNativeLocal || url.type() == fileapi::kFileSystemTypeDrive; } FileSystemBackend::FileSystemBackend( FileSystemBackendDelegate* drive_delegate, scoped_refptr special_storage_policy, scoped_refptr mount_points, fileapi::ExternalMountPoints* system_mount_points) : special_storage_policy_(special_storage_policy), file_access_permissions_(new FileAccessPermissions()), local_file_util_(fileapi::AsyncFileUtil::CreateForLocalFileSystem()), drive_delegate_(drive_delegate), mount_points_(mount_points), system_mount_points_(system_mount_points) { } FileSystemBackend::~FileSystemBackend() { } void FileSystemBackend::AddSystemMountPoints() { // RegisterFileSystem() is no-op if the mount point with the same name // already exists, hence it's safe to call without checking if a mount // point already exists or not. system_mount_points_->RegisterFileSystem( "archive", fileapi::kFileSystemTypeNativeLocal, fileapi::FileSystemMountOption(), chromeos::CrosDisksClient::GetArchiveMountPoint()); system_mount_points_->RegisterFileSystem( "removable", fileapi::kFileSystemTypeNativeLocal, fileapi::FileSystemMountOption(fileapi::COPY_SYNC_OPTION_SYNC), chromeos::CrosDisksClient::GetRemovableDiskMountPoint()); system_mount_points_->RegisterFileSystem( "oem", fileapi::kFileSystemTypeRestrictedNativeLocal, fileapi::FileSystemMountOption(), base::FilePath(FILE_PATH_LITERAL("/usr/share/oem"))); } bool FileSystemBackend::CanHandleType(fileapi::FileSystemType type) const { switch (type) { case fileapi::kFileSystemTypeExternal: case fileapi::kFileSystemTypeDrive: case fileapi::kFileSystemTypeRestrictedNativeLocal: case fileapi::kFileSystemTypeNativeLocal: case fileapi::kFileSystemTypeNativeForPlatformApp: return true; default: return false; } } void FileSystemBackend::Initialize(fileapi::FileSystemContext* context) { } void FileSystemBackend::ResolveURL(const fileapi::FileSystemURL& url, fileapi::OpenFileSystemMode mode, const OpenFileSystemCallback& callback) { std::string id; fileapi::FileSystemType type; base::FilePath path; fileapi::FileSystemMountOption option; if (!mount_points_->CrackVirtualPath( url.virtual_path(), &id, &type, &path, &option) && !system_mount_points_->CrackVirtualPath( url.virtual_path(), &id, &type, &path, &option)) { // Not under a mount point, so return an error, since the root is not // accessible. GURL root_url = GURL(fileapi::GetExternalFileSystemRootURIString( url.origin(), std::string())); callback.Run(root_url, std::string(), base::File::FILE_ERROR_SECURITY); return; } std::string name; // Construct a URL restricted to the found mount point. std::string root_url = fileapi::GetExternalFileSystemRootURIString(url.origin(), id); // For removable and archives, the file system root is the external mount // point plus the inner mount point. if (id == "archive" || id == "removable") { std::vector components; url.virtual_path().GetComponents(&components); DCHECK_EQ(id, components.at(0)); if (components.size() < 2) { // Unable to access /archive and /removable directories directly. The // inner mount name must be specified. callback.Run( GURL(root_url), std::string(), base::File::FILE_ERROR_SECURITY); return; } std::string inner_mount_name = components[1]; root_url += inner_mount_name + "/"; name = inner_mount_name; } else { name = id; } callback.Run(GURL(root_url), name, base::File::FILE_OK); } fileapi::FileSystemQuotaUtil* FileSystemBackend::GetQuotaUtil() { // No quota support. return NULL; } bool FileSystemBackend::IsAccessAllowed( const fileapi::FileSystemURL& url) const { if (!url.is_valid()) return false; // Permit access to mount points from internal WebUI. const GURL& origin_url = url.origin(); if (origin_url.SchemeIs(kChromeUIScheme)) return true; // No extra check is needed for isolated file systems. if (url.mount_type() == fileapi::kFileSystemTypeIsolated) return true; if (!CanHandleURL(url)) return false; std::string extension_id = origin_url.host(); // TODO(mtomasz): Temporarily whitelist TimeScapes. Remove this in M-31. // See: crbug.com/271946 if (extension_id == "mlbmkoenclnokonejhlfakkeabdlmpek" && url.type() == fileapi::kFileSystemTypeRestrictedNativeLocal) { return true; } // Check first to make sure this extension has fileBrowserHander permissions. if (!special_storage_policy_->IsFileHandler(extension_id)) return false; return file_access_permissions_->HasAccessPermission(extension_id, url.virtual_path()); } void FileSystemBackend::GrantFullAccessToExtension( const std::string& extension_id) { DCHECK(special_storage_policy_->IsFileHandler(extension_id)); if (!special_storage_policy_->IsFileHandler(extension_id)) return; file_access_permissions_->GrantFullAccessPermission(extension_id); } void FileSystemBackend::GrantFileAccessToExtension( const std::string& extension_id, const base::FilePath& virtual_path) { // All we care about here is access from extensions for now. DCHECK(special_storage_policy_->IsFileHandler(extension_id)); if (!special_storage_policy_->IsFileHandler(extension_id)) return; std::string id; fileapi::FileSystemType type; base::FilePath path; fileapi::FileSystemMountOption option; if (!mount_points_->CrackVirtualPath(virtual_path, &id, &type, &path, &option) && !system_mount_points_->CrackVirtualPath(virtual_path, &id, &type, &path, &option)) { return; } if (type == fileapi::kFileSystemTypeRestrictedNativeLocal) { LOG(ERROR) << "Can't grant access for restricted mount point"; return; } file_access_permissions_->GrantAccessPermission(extension_id, virtual_path); } void FileSystemBackend::RevokeAccessForExtension( const std::string& extension_id) { file_access_permissions_->RevokePermissions(extension_id); } std::vector FileSystemBackend::GetRootDirectories() const { std::vector mount_points; mount_points_->AddMountPointInfosTo(&mount_points); system_mount_points_->AddMountPointInfosTo(&mount_points); std::vector root_dirs; for (size_t i = 0; i < mount_points.size(); ++i) root_dirs.push_back(mount_points[i].path); return root_dirs; } fileapi::AsyncFileUtil* FileSystemBackend::GetAsyncFileUtil( fileapi::FileSystemType type) { if (type == fileapi::kFileSystemTypeDrive) return drive_delegate_->GetAsyncFileUtil(type); DCHECK(type == fileapi::kFileSystemTypeNativeLocal || type == fileapi::kFileSystemTypeRestrictedNativeLocal); return local_file_util_.get(); } fileapi::CopyOrMoveFileValidatorFactory* FileSystemBackend::GetCopyOrMoveFileValidatorFactory( fileapi::FileSystemType type, base::File::Error* error_code) { DCHECK(error_code); *error_code = base::File::FILE_OK; return NULL; } fileapi::FileSystemOperation* FileSystemBackend::CreateFileSystemOperation( const fileapi::FileSystemURL& url, fileapi::FileSystemContext* context, base::File::Error* error_code) const { DCHECK(url.is_valid()); if (!IsAccessAllowed(url)) { *error_code = base::File::FILE_ERROR_SECURITY; return NULL; } DCHECK(url.type() == fileapi::kFileSystemTypeNativeLocal || url.type() == fileapi::kFileSystemTypeRestrictedNativeLocal || url.type() == fileapi::kFileSystemTypeDrive); return fileapi::FileSystemOperation::Create( url, context, make_scoped_ptr(new fileapi::FileSystemOperationContext(context))); } bool FileSystemBackend::SupportsStreaming( const fileapi::FileSystemURL& url) const { return false; } scoped_ptr FileSystemBackend::CreateFileStreamReader( const fileapi::FileSystemURL& url, int64 offset, const base::Time& expected_modification_time, fileapi::FileSystemContext* context) const { DCHECK(url.is_valid()); if (!IsAccessAllowed(url)) return scoped_ptr(); if (url.type() == fileapi::kFileSystemTypeDrive) { return drive_delegate_->CreateFileStreamReader( url, offset, expected_modification_time, context); } return scoped_ptr( webkit_blob::FileStreamReader::CreateForFileSystemFile( context, url, offset, expected_modification_time)); } scoped_ptr FileSystemBackend::CreateFileStreamWriter( const fileapi::FileSystemURL& url, int64 offset, fileapi::FileSystemContext* context) const { DCHECK(url.is_valid()); if (!IsAccessAllowed(url)) return scoped_ptr(); if (url.type() == fileapi::kFileSystemTypeDrive) return drive_delegate_->CreateFileStreamWriter(url, offset, context); if (url.type() == fileapi::kFileSystemTypeRestrictedNativeLocal) return scoped_ptr(); DCHECK(url.type() == fileapi::kFileSystemTypeNativeLocal); return scoped_ptr( fileapi::FileStreamWriter::CreateForLocalFile( context->default_file_task_runner(), url.path(), offset, fileapi::FileStreamWriter::OPEN_EXISTING_FILE)); } bool FileSystemBackend::GetVirtualPath( const base::FilePath& filesystem_path, base::FilePath* virtual_path) { return mount_points_->GetVirtualPath(filesystem_path, virtual_path) || system_mount_points_->GetVirtualPath(filesystem_path, virtual_path); } } // namespace chromeos