// 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/strings/stringprintf.h" #include "chrome/browser/chromeos/fileapi/file_access_permissions.h" #include "chrome/browser/chromeos/fileapi/file_system_backend_delegate.h" #include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h" #include "chrome/common/url_constants.h" #include "chromeos/dbus/cros_disks_client.h" #include "storage/browser/blob/file_stream_reader.h" #include "storage/browser/fileapi/async_file_util.h" #include "storage/browser/fileapi/external_mount_points.h" #include "storage/browser/fileapi/file_stream_writer.h" #include "storage/browser/fileapi/file_system_context.h" #include "storage/browser/fileapi/file_system_operation.h" #include "storage/browser/fileapi/file_system_operation_context.h" #include "storage/browser/fileapi/file_system_url.h" namespace chromeos { // static bool FileSystemBackend::CanHandleURL(const storage::FileSystemURL& url) { if (!url.is_valid()) return false; return url.type() == storage::kFileSystemTypeNativeLocal || url.type() == storage::kFileSystemTypeRestrictedNativeLocal || url.type() == storage::kFileSystemTypeDrive || url.type() == storage::kFileSystemTypeProvided || url.type() == storage::kFileSystemTypeDeviceMediaAsFileStorage; } FileSystemBackend::FileSystemBackend( FileSystemBackendDelegate* drive_delegate, FileSystemBackendDelegate* file_system_provider_delegate, FileSystemBackendDelegate* mtp_delegate, scoped_refptr special_storage_policy, scoped_refptr mount_points, storage::ExternalMountPoints* system_mount_points) : special_storage_policy_(special_storage_policy), file_access_permissions_(new FileAccessPermissions()), local_file_util_(storage::AsyncFileUtil::CreateForLocalFileSystem()), drive_delegate_(drive_delegate), file_system_provider_delegate_(file_system_provider_delegate), mtp_delegate_(mtp_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", storage::kFileSystemTypeNativeLocal, storage::FileSystemMountOption(), chromeos::CrosDisksClient::GetArchiveMountPoint()); system_mount_points_->RegisterFileSystem( "removable", storage::kFileSystemTypeNativeLocal, storage::FileSystemMountOption(storage::COPY_SYNC_OPTION_SYNC), chromeos::CrosDisksClient::GetRemovableDiskMountPoint()); system_mount_points_->RegisterFileSystem( "oem", storage::kFileSystemTypeRestrictedNativeLocal, storage::FileSystemMountOption(), base::FilePath(FILE_PATH_LITERAL("/usr/share/oem"))); } bool FileSystemBackend::CanHandleType(storage::FileSystemType type) const { switch (type) { case storage::kFileSystemTypeExternal: case storage::kFileSystemTypeDrive: case storage::kFileSystemTypeRestrictedNativeLocal: case storage::kFileSystemTypeNativeLocal: case storage::kFileSystemTypeNativeForPlatformApp: case storage::kFileSystemTypeDeviceMediaAsFileStorage: case storage::kFileSystemTypeProvided: return true; default: return false; } } void FileSystemBackend::Initialize(storage::FileSystemContext* context) { } void FileSystemBackend::ResolveURL(const storage::FileSystemURL& url, storage::OpenFileSystemMode mode, const OpenFileSystemCallback& callback) { std::string id; storage::FileSystemType type; std::string cracked_id; base::FilePath path; storage::FileSystemMountOption option; if (!mount_points_->CrackVirtualPath( url.virtual_path(), &id, &type, &cracked_id, &path, &option) && !system_mount_points_->CrackVirtualPath( url.virtual_path(), &id, &type, &cracked_id, &path, &option)) { // Not under a mount point, so return an error, since the root is not // accessible. GURL root_url = GURL(storage::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 = storage::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); } storage::FileSystemQuotaUtil* FileSystemBackend::GetQuotaUtil() { // No quota support. return NULL; } const storage::UpdateObserverList* FileSystemBackend::GetUpdateObservers( storage::FileSystemType type) const { return NULL; } const storage::ChangeObserverList* FileSystemBackend::GetChangeObservers( storage::FileSystemType type) const { return NULL; } const storage::AccessObserverList* FileSystemBackend::GetAccessObservers( storage::FileSystemType type) const { return NULL; } bool FileSystemBackend::IsAccessAllowed( const storage::FileSystemURL& url) const { if (!url.is_valid()) return false; // No extra check is needed for isolated file systems. if (url.mount_type() == storage::kFileSystemTypeIsolated) return true; if (!CanHandleURL(url)) return false; std::string extension_id = url.origin().host(); // TODO(mtomasz): Temporarily whitelist TimeScapes. Remove this in M-31. // See: crbug.com/271946 if (extension_id == "mlbmkoenclnokonejhlfakkeabdlmpek" && url.type() == storage::kFileSystemTypeRestrictedNativeLocal) { return true; } // Grant access for URL having "externalfile:" scheme. The URL // filesystem:externalfile:/xxx cannot be parsed directly. The URL is created // only by DriveURLRequestJob. if (url.origin().scheme() == chrome::kExternalFileScheme) return true; // Check first to make sure this extension has fileBrowserHander permissions. if (!special_storage_policy_.get() || !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) { if (!special_storage_policy_.get()) return; if (!special_storage_policy_->IsFileHandler(extension_id)) { NOTREACHED(); return; } file_access_permissions_->GrantFullAccessPermission(extension_id); } void FileSystemBackend::GrantFileAccessToExtension( const std::string& extension_id, const base::FilePath& virtual_path) { if (!special_storage_policy_.get()) return; // All we care about here is access from extensions for now. if (!special_storage_policy_->IsFileHandler(extension_id)) { NOTREACHED(); return; } std::string id; storage::FileSystemType type; std::string cracked_id; base::FilePath path; storage::FileSystemMountOption option; if (!mount_points_->CrackVirtualPath(virtual_path, &id, &type, &cracked_id, &path, &option) && !system_mount_points_->CrackVirtualPath(virtual_path, &id, &type, &cracked_id, &path, &option)) { return; } if (type == storage::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; } storage::AsyncFileUtil* FileSystemBackend::GetAsyncFileUtil( storage::FileSystemType type) { switch (type) { case storage::kFileSystemTypeDrive: return drive_delegate_->GetAsyncFileUtil(type); case storage::kFileSystemTypeProvided: return file_system_provider_delegate_->GetAsyncFileUtil(type); case storage::kFileSystemTypeNativeLocal: case storage::kFileSystemTypeRestrictedNativeLocal: return local_file_util_.get(); case storage::kFileSystemTypeDeviceMediaAsFileStorage: return mtp_delegate_->GetAsyncFileUtil(type); default: NOTREACHED(); } return NULL; } storage::WatcherManager* FileSystemBackend::GetWatcherManager( storage::FileSystemType type) { return NULL; } storage::CopyOrMoveFileValidatorFactory* FileSystemBackend::GetCopyOrMoveFileValidatorFactory( storage::FileSystemType type, base::File::Error* error_code) { DCHECK(error_code); *error_code = base::File::FILE_OK; return NULL; } storage::FileSystemOperation* FileSystemBackend::CreateFileSystemOperation( const storage::FileSystemURL& url, storage::FileSystemContext* context, base::File::Error* error_code) const { DCHECK(url.is_valid()); if (!IsAccessAllowed(url)) { *error_code = base::File::FILE_ERROR_SECURITY; return NULL; } if (url.type() == storage::kFileSystemTypeDeviceMediaAsFileStorage) { // MTP file operations run on MediaTaskRunner. return storage::FileSystemOperation::Create( url, context, make_scoped_ptr(new storage::FileSystemOperationContext( context, MediaFileSystemBackend::MediaTaskRunner().get()))); } DCHECK(url.type() == storage::kFileSystemTypeNativeLocal || url.type() == storage::kFileSystemTypeRestrictedNativeLocal || url.type() == storage::kFileSystemTypeDrive || url.type() == storage::kFileSystemTypeProvided); return storage::FileSystemOperation::Create( url, context, make_scoped_ptr(new storage::FileSystemOperationContext(context))); } bool FileSystemBackend::SupportsStreaming( const storage::FileSystemURL& url) const { return url.type() == storage::kFileSystemTypeDrive || url.type() == storage::kFileSystemTypeProvided || url.type() == storage::kFileSystemTypeDeviceMediaAsFileStorage; } bool FileSystemBackend::HasInplaceCopyImplementation( storage::FileSystemType type) const { switch (type) { case storage::kFileSystemTypeDrive: case storage::kFileSystemTypeProvided: case storage::kFileSystemTypeDeviceMediaAsFileStorage: return true; case storage::kFileSystemTypeNativeLocal: case storage::kFileSystemTypeRestrictedNativeLocal: return false; default: NOTREACHED(); } return true; } scoped_ptr FileSystemBackend::CreateFileStreamReader( const storage::FileSystemURL& url, int64 offset, int64 max_bytes_to_read, const base::Time& expected_modification_time, storage::FileSystemContext* context) const { DCHECK(url.is_valid()); if (!IsAccessAllowed(url)) return scoped_ptr(); switch (url.type()) { case storage::kFileSystemTypeDrive: return drive_delegate_->CreateFileStreamReader( url, offset, max_bytes_to_read, expected_modification_time, context); case storage::kFileSystemTypeProvided: return file_system_provider_delegate_->CreateFileStreamReader( url, offset, max_bytes_to_read, expected_modification_time, context); case storage::kFileSystemTypeNativeLocal: case storage::kFileSystemTypeRestrictedNativeLocal: return scoped_ptr( storage::FileStreamReader::CreateForFileSystemFile( context, url, offset, expected_modification_time)); case storage::kFileSystemTypeDeviceMediaAsFileStorage: return mtp_delegate_->CreateFileStreamReader( url, offset, max_bytes_to_read, expected_modification_time, context); default: NOTREACHED(); } return scoped_ptr(); } scoped_ptr FileSystemBackend::CreateFileStreamWriter( const storage::FileSystemURL& url, int64 offset, storage::FileSystemContext* context) const { DCHECK(url.is_valid()); if (!IsAccessAllowed(url)) return scoped_ptr(); switch (url.type()) { case storage::kFileSystemTypeDrive: return drive_delegate_->CreateFileStreamWriter(url, offset, context); case storage::kFileSystemTypeProvided: return file_system_provider_delegate_->CreateFileStreamWriter( url, offset, context); case storage::kFileSystemTypeNativeLocal: return scoped_ptr( storage::FileStreamWriter::CreateForLocalFile( context->default_file_task_runner(), url.path(), offset, storage::FileStreamWriter::OPEN_EXISTING_FILE)); case storage::kFileSystemTypeRestrictedNativeLocal: // Restricted native local file system is read only. return scoped_ptr(); case storage::kFileSystemTypeDeviceMediaAsFileStorage: return mtp_delegate_->CreateFileStreamWriter(url, offset, context); default: NOTREACHED(); } return scoped_ptr(); } 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); } void FileSystemBackend::GetRedirectURLForContents( const storage::FileSystemURL& url, const storage::URLCallback& callback) { DCHECK(url.is_valid()); if (!IsAccessAllowed(url)) return callback.Run(GURL()); switch (url.type()) { case storage::kFileSystemTypeDrive: drive_delegate_->GetRedirectURLForContents(url, callback); return; case storage::kFileSystemTypeProvided: file_system_provider_delegate_->GetRedirectURLForContents(url, callback); return; case storage::kFileSystemTypeDeviceMediaAsFileStorage: mtp_delegate_->GetRedirectURLForContents(url, callback); return; case storage::kFileSystemTypeNativeLocal: case storage::kFileSystemTypeRestrictedNativeLocal: callback.Run(GURL()); return; default: NOTREACHED(); } callback.Run(GURL()); } } // namespace chromeos