[go: nahoru, domu]

blob: f64ae7f8ddb520a6de6732fe87479f1ce2c44c97 [file] [log] [blame]
// 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 "storage/browser/file_system/plugin_private_file_system_backend.h"
#include <stdint.h>
#include <map>
#include <memory>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/containers/contains.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/memory/scoped_refptr.h"
#include "base/synchronization/lock.h"
#include "base/task/task_runner_util.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "storage/browser/file_system/async_file_util_adapter.h"
#include "storage/browser/file_system/file_system_context.h"
#include "storage/browser/file_system/file_system_operation.h"
#include "storage/browser/file_system/file_system_operation_context.h"
#include "storage/browser/file_system/isolated_context.h"
#include "storage/browser/file_system/obfuscated_file_util.h"
#include "storage/browser/file_system/obfuscated_file_util_memory_delegate.h"
#include "storage/browser/file_system/quota/quota_reservation.h"
#include "storage/browser/file_system/sandbox_file_stream_reader.h"
#include "storage/browser/file_system/sandbox_file_stream_writer.h"
#include "storage/browser/quota/special_storage_policy.h"
#include "storage/common/file_system/file_system_util.h"
#include "third_party/blink/public/common/storage_key/storage_key.h"
#include "url/origin.h"
namespace storage {
class PluginPrivateFileSystemBackend::FileSystemIDToPluginMap {
public:
explicit FileSystemIDToPluginMap(
scoped_refptr<base::SequencedTaskRunner> task_runner)
: task_runner_(std::move(task_runner)) {}
~FileSystemIDToPluginMap() = default;
std::string GetPluginIDForURL(const FileSystemURL& url) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
auto found = map_.find(url.filesystem_id());
if (url.type() != kFileSystemTypePluginPrivate || found == map_.end()) {
NOTREACHED() << "Unsupported url is given: " << url.DebugString();
return std::string();
}
return found->second;
}
void RegisterFileSystem(const std::string& filesystem_id,
const std::string& plugin_id) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
DCHECK(!filesystem_id.empty());
DCHECK(!base::Contains(map_, filesystem_id)) << filesystem_id;
map_[filesystem_id] = plugin_id;
}
void RemoveFileSystem(const std::string& filesystem_id) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
map_.erase(filesystem_id);
}
private:
using Map = std::map<std::string, std::string>;
const scoped_refptr<base::SequencedTaskRunner> task_runner_;
Map map_;
};
namespace {
const base::FilePath::CharType* kFileSystemDirectory =
SandboxFileSystemBackendDelegate::kFileSystemDirectory;
const base::FilePath::CharType* kPluginPrivateDirectory =
FILE_PATH_LITERAL("Plugins");
base::File::Error OpenFileSystemOnFileTaskRunner(
ObfuscatedFileUtil* file_util,
PluginPrivateFileSystemBackend::FileSystemIDToPluginMap* plugin_map,
const url::Origin& origin,
const std::string& filesystem_id,
const std::string& plugin_id,
OpenFileSystemMode mode) {
base::File::Error error = base::File::FILE_ERROR_FAILED;
const bool create = (mode == OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT);
// TODO(https://crbug.com/1231162): determine whether EME/CDM/plugin private
// file system will be partitioned; if so, replace the in-line conversion with
// the correct third-party StorageKey.
file_util->GetDirectoryForStorageKeyAndType(blink::StorageKey(origin),
plugin_id, create, &error);
if (error == base::File::FILE_OK)
plugin_map->RegisterFileSystem(filesystem_id, plugin_id);
return error;
}
} // namespace
PluginPrivateFileSystemBackend::CdmFileInfo::CdmFileInfo(
const std::string& name,
const std::string& legacy_file_system_id)
: name(name), legacy_file_system_id(legacy_file_system_id) {}
PluginPrivateFileSystemBackend::CdmFileInfo::CdmFileInfo(const CdmFileInfo&) =
default;
PluginPrivateFileSystemBackend::CdmFileInfo::CdmFileInfo(CdmFileInfo&&) =
default;
PluginPrivateFileSystemBackend::CdmFileInfo::~CdmFileInfo() = default;
PluginPrivateFileSystemBackend::PluginPrivateFileSystemBackend(
scoped_refptr<base::SequencedTaskRunner> file_task_runner,
const base::FilePath& profile_path,
const base::FilePath& bucket_base_path,
scoped_refptr<SpecialStoragePolicy> special_storage_policy,
const FileSystemOptions& file_system_options,
leveldb::Env* env_override)
: file_task_runner_(std::move(file_task_runner)),
file_system_options_(file_system_options),
base_path_(profile_path.Append(kFileSystemDirectory)
.Append(kPluginPrivateDirectory)),
plugin_map_(new FileSystemIDToPluginMap(file_task_runner_)) {
file_util_ = std::make_unique<AsyncFileUtilAdapter>(
std::make_unique<ObfuscatedFileUtil>(
std::move(special_storage_policy), base_path_, bucket_base_path,
env_override,
base::BindRepeating(&FileSystemIDToPluginMap::GetPluginIDForURL,
base::Owned(plugin_map_.get())),
std::set<std::string>(), nullptr,
file_system_options.is_incognito()));
}
PluginPrivateFileSystemBackend::~PluginPrivateFileSystemBackend() {
if (!file_task_runner_->RunsTasksInCurrentSequence()) {
AsyncFileUtil* file_util = file_util_.release();
if (!file_task_runner_->DeleteSoon(FROM_HERE, file_util))
delete file_util;
}
}
void PluginPrivateFileSystemBackend::OpenPrivateFileSystem(
const url::Origin& origin,
FileSystemType type,
const std::string& filesystem_id,
const std::string& plugin_id,
OpenFileSystemMode mode,
StatusCallback callback) {
if (!CanHandleType(type)) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), base::File::FILE_ERROR_SECURITY));
return;
}
PostTaskAndReplyWithResult(
file_task_runner_.get(), FROM_HERE,
base::BindOnce(&OpenFileSystemOnFileTaskRunner, obfuscated_file_util(),
plugin_map_, origin, filesystem_id, plugin_id, mode),
std::move(callback));
}
bool PluginPrivateFileSystemBackend::CanHandleType(FileSystemType type) const {
return type == kFileSystemTypePluginPrivate;
}
void PluginPrivateFileSystemBackend::Initialize(FileSystemContext* context) {}
void PluginPrivateFileSystemBackend::ResolveURL(
const FileSystemURL& url,
OpenFileSystemMode mode,
OpenFileSystemCallback callback) {
// We never allow opening a new plugin-private filesystem via usual
// ResolveURL.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), GURL(), std::string(),
base::File::FILE_ERROR_SECURITY));
}
AsyncFileUtil* PluginPrivateFileSystemBackend::GetAsyncFileUtil(
FileSystemType type) {
return file_util_.get();
}
WatcherManager* PluginPrivateFileSystemBackend::GetWatcherManager(
FileSystemType type) {
return nullptr;
}
CopyOrMoveFileValidatorFactory*
PluginPrivateFileSystemBackend::GetCopyOrMoveFileValidatorFactory(
FileSystemType type,
base::File::Error* error_code) {
DCHECK(error_code);
*error_code = base::File::FILE_OK;
return nullptr;
}
std::unique_ptr<FileSystemOperation>
PluginPrivateFileSystemBackend::CreateFileSystemOperation(
const FileSystemURL& url,
FileSystemContext* context,
base::File::Error* error_code) const {
auto operation_context =
std::make_unique<FileSystemOperationContext>(context);
return FileSystemOperation::Create(url, context,
std::move(operation_context));
}
bool PluginPrivateFileSystemBackend::SupportsStreaming(
const FileSystemURL& url) const {
// Streaming is required for incognito file systems in order to access
// memory-backed files.
DCHECK(CanHandleType(url.type()));
return file_system_options_.is_incognito();
}
bool PluginPrivateFileSystemBackend::HasInplaceCopyImplementation(
FileSystemType type) const {
return false;
}
std::unique_ptr<FileStreamReader>
PluginPrivateFileSystemBackend::CreateFileStreamReader(
const FileSystemURL& url,
int64_t offset,
int64_t max_bytes_to_read,
const base::Time& expected_modification_time,
FileSystemContext* context) const {
DCHECK(CanHandleType(url.type()));
return std::make_unique<SandboxFileStreamReader>(context, url, offset,
expected_modification_time);
}
std::unique_ptr<FileStreamWriter>
PluginPrivateFileSystemBackend::CreateFileStreamWriter(
const FileSystemURL& url,
int64_t offset,
FileSystemContext* context) const {
DCHECK(CanHandleType(url.type()));
// Observers not supported by PluginPrivateFileSystemBackend.
return std::make_unique<SandboxFileStreamWriter>(context, url, offset,
UpdateObserverList());
}
FileSystemQuotaUtil* PluginPrivateFileSystemBackend::GetQuotaUtil() {
return this;
}
base::File::Error
PluginPrivateFileSystemBackend::DeleteStorageKeyDataOnFileTaskRunner(
FileSystemContext* context,
QuotaManagerProxy* proxy,
const blink::StorageKey& storage_key,
FileSystemType type) {
if (!CanHandleType(type))
return base::File::FILE_ERROR_SECURITY;
bool result = obfuscated_file_util()->DeleteDirectoryForStorageKeyAndType(
storage_key, std::string());
if (result)
return base::File::FILE_OK;
return base::File::FILE_ERROR_FAILED;
}
void PluginPrivateFileSystemBackend::PerformStorageCleanupOnFileTaskRunner(
FileSystemContext* context,
QuotaManagerProxy* proxy,
FileSystemType type) {
if (!CanHandleType(type))
return;
obfuscated_file_util()->RewriteDatabases();
}
std::vector<blink::StorageKey>
PluginPrivateFileSystemBackend::GetStorageKeysForTypeOnFileTaskRunner(
FileSystemType type) {
if (!CanHandleType(type))
return std::vector<blink::StorageKey>();
std::unique_ptr<ObfuscatedFileUtil::AbstractStorageKeyEnumerator> enumerator(
obfuscated_file_util()->CreateStorageKeyEnumerator());
std::vector<blink::StorageKey> storage_keys;
absl::optional<blink::StorageKey> storage_key;
while ((storage_key = enumerator->Next()).has_value())
storage_keys.push_back(std::move(storage_key).value());
return storage_keys;
}
int64_t PluginPrivateFileSystemBackend::GetStorageKeyUsageOnFileTaskRunner(
FileSystemContext* context,
const blink::StorageKey& storage_key,
FileSystemType type) {
DCHECK(file_task_runner_->RunsTasksInCurrentSequence());
if (!CanHandleType(type))
return 0;
int64_t total_size;
base::Time last_modified_time;
GetOriginDetailsOnFileTaskRunner(context, storage_key.origin(), &total_size,
&last_modified_time);
return total_size;
}
void PluginPrivateFileSystemBackend::GetOriginDetailsOnFileTaskRunner(
FileSystemContext* context,
const url::Origin& origin,
int64_t* total_size,
base::Time* last_modified_time) {
DCHECK(file_task_runner_->RunsTasksInCurrentSequence());
*total_size = 0;
*last_modified_time = base::Time::UnixEpoch();
std::string fsid =
IsolatedContext::GetInstance()->RegisterFileSystemForVirtualPath(
kFileSystemTypePluginPrivate, kPluginPrivateRootName,
base::FilePath());
DCHECK(ValidateIsolatedFileSystemId(fsid));
std::string root = GetIsolatedFileSystemRootURIString(origin.GetURL(), fsid,
kPluginPrivateRootName);
std::unique_ptr<FileSystemOperationContext> operation_context(
std::make_unique<FileSystemOperationContext>(context));
// Determine the available plugin private filesystem directories for this
// origin. Currently the plugin private filesystem is only used by Encrypted
// Media Content Decryption Modules. Each CDM gets a directory based on the
// mimetype (e.g. plugin application/x-ppapi-widevine-cdm uses directory
// application_x-ppapi-widevine-cdm). Enumerate through the set of
// directories so that data from any CDM used by this origin is counted.
base::File::Error error;
// TODO(https://crbug.com/1231162): determine whether EME/CDM/plugin private
// file system will be partitioned; if so, replace the in-line conversion with
// the correct third-party StorageKey.
base::FilePath path =
obfuscated_file_util()->GetDirectoryForStorageKeyAndType(
blink::StorageKey(origin), "", false, &error);
if (error != base::File::FILE_OK) {
DLOG(ERROR) << "Unable to read directory for " << origin;
return;
}
base::FileEnumerator directory_enumerator(path, false,
base::FileEnumerator::DIRECTORIES);
base::FilePath plugin_path;
while (!(plugin_path = directory_enumerator.Next()).empty()) {
std::string plugin_name = plugin_path.BaseName().MaybeAsASCII();
if (OpenFileSystemOnFileTaskRunner(
obfuscated_file_util(), plugin_map_, origin, fsid, plugin_name,
OPEN_FILE_SYSTEM_FAIL_IF_NONEXISTENT) != base::File::FILE_OK) {
continue;
}
// TODO(https://crbug.com/1231162): determine whether EME/CDM/plugin private
// file system will be partitioned and use the appropriate StorageKey
std::unique_ptr<FileSystemFileUtil::AbstractFileEnumerator> enumerator(
obfuscated_file_util()->CreateFileEnumerator(
operation_context.get(),
context->CrackURL(
GURL(root), blink::StorageKey(url::Origin::Create(GURL(root)))),
true));
while (!enumerator->Next().empty()) {
*total_size += enumerator->Size();
if (enumerator->LastModifiedTime() > *last_modified_time)
*last_modified_time = enumerator->LastModifiedTime();
}
}
}
std::vector<PluginPrivateFileSystemBackend::CdmFileInfo>
PluginPrivateFileSystemBackend::GetMediaLicenseFilesForOriginOnFileTaskRunner(
FileSystemContext* context,
const url::Origin& origin) {
DCHECK(file_task_runner_->RunsTasksInCurrentSequence());
std::unique_ptr<FileSystemOperationContext> operation_context(
std::make_unique<FileSystemOperationContext>(context));
// Determine the available plugin private filesystem directories for this
// origin. Currently the plugin private filesystem is only used by Encrypted
// Media Content Decryption Modules. Each CDM gets a directory based on the
// mimetype (e.g. plugin application/x-ppapi-widevine-cdm uses directory
// application_x-ppapi-widevine-cdm). Enumerate through the set of
// directories so that data from any CDM used by this origin is counted.
base::File::Error error;
base::FilePath path =
obfuscated_file_util()->GetDirectoryForStorageKeyAndType(
blink::StorageKey(origin), "", false, &error);
if (error != base::File::FILE_OK)
return {};
std::vector<CdmFileInfo> cdm_files;
base::FileEnumerator directory_enumerator(path, false,
base::FileEnumerator::DIRECTORIES);
base::FilePath plugin_path;
while (!(plugin_path = directory_enumerator.Next()).empty()) {
std::string plugin_name = plugin_path.BaseName().MaybeAsASCII();
std::string fsid =
IsolatedContext::GetInstance()->RegisterFileSystemForVirtualPath(
kFileSystemTypePluginPrivate, kPluginPrivateRootName,
base::FilePath());
DCHECK(ValidateIsolatedFileSystemId(fsid));
std::string root = GetIsolatedFileSystemRootURIString(
origin.GetURL(), fsid, kPluginPrivateRootName);
if (OpenFileSystemOnFileTaskRunner(
obfuscated_file_util(), plugin_map_, origin, fsid, plugin_name,
OPEN_FILE_SYSTEM_FAIL_IF_NONEXISTENT) != base::File::FILE_OK) {
continue;
}
std::unique_ptr<FileSystemFileUtil::AbstractFileEnumerator> enumerator(
obfuscated_file_util()->CreateFileEnumerator(
operation_context.get(),
context->CrackURL(
GURL(root), blink::StorageKey(url::Origin::Create(GURL(root)))),
true));
base::FilePath cdm_file_path;
while (!(cdm_file_path = enumerator->Next()).empty()) {
cdm_files.emplace_back(cdm_file_path.BaseName().AsUTF8Unsafe(),
plugin_path.BaseName().AsUTF8Unsafe());
}
}
return cdm_files;
}
scoped_refptr<QuotaReservation>
PluginPrivateFileSystemBackend::CreateQuotaReservationOnFileTaskRunner(
const blink::StorageKey& storage_key,
FileSystemType type) {
// We don't track usage on this filesystem.
NOTREACHED();
return scoped_refptr<QuotaReservation>();
}
const UpdateObserverList* PluginPrivateFileSystemBackend::GetUpdateObservers(
FileSystemType type) const {
return nullptr;
}
const ChangeObserverList* PluginPrivateFileSystemBackend::GetChangeObservers(
FileSystemType type) const {
return nullptr;
}
const AccessObserverList* PluginPrivateFileSystemBackend::GetAccessObservers(
FileSystemType type) const {
return nullptr;
}
ObfuscatedFileUtil* PluginPrivateFileSystemBackend::obfuscated_file_util() {
return static_cast<ObfuscatedFileUtil*>(
static_cast<AsyncFileUtilAdapter*>(file_util_.get())->sync_file_util());
}
ObfuscatedFileUtilMemoryDelegate*
PluginPrivateFileSystemBackend::obfuscated_file_util_memory_delegate() {
auto* file_util = obfuscated_file_util();
DCHECK(file_util->is_incognito());
return static_cast<ObfuscatedFileUtilMemoryDelegate*>(file_util->delegate());
}
} // namespace storage