blob: 5f2b6c1dcd00f7bcf38853d1a407dcb5dc98a8f1 [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/services/storage/storage_service_impl.h"
#include "base/functional/bind.h"
#include "base/not_fatal_until.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/thread_pool.h"
#include "build/build_config.h"
#include "components/services/storage/dom_storage/local_storage_impl.h"
#include "components/services/storage/dom_storage/session_storage_impl.h"
#include "components/services/storage/dom_storage/storage_area_impl.h"
#include "components/services/storage/filesystem_proxy_factory.h"
#include "components/services/storage/public/cpp/filesystem/filesystem_proxy.h"
#include "components/services/storage/sandboxed_vfs_delegate.h"
#include "components/services/storage/test_api_stubs.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "sql/database.h"
#include "sql/sandboxed_vfs.h"
#include "third_party/leveldatabase/env_chromium.h"
namespace storage {
namespace {
const char kSessionStorageDirectory[] = "Session Storage";
// We don't use out-of-process Storage Service on Android, so we can avoid
// pulling all the related code (including Directory mojom) into the build.
#if !BUILDFLAG(IS_ANDROID)
// The name under which we register our own sandboxed VFS instance when running
// out-of-process.
constexpr char kVfsName[] = "storage_service";
using DirectoryBinder =
base::RepeatingCallback<void(mojo::PendingReceiver<mojom::Directory>)>;
std::unique_ptr<FilesystemProxy> CreateRestrictedFilesystemProxy(
const base::FilePath& directory_path,
scoped_refptr<base::SequencedTaskRunner> io_task_runner,
DirectoryBinder binder,
scoped_refptr<base::SequencedTaskRunner> binder_task_runner) {
mojo::PendingRemote<mojom::Directory> directory;
binder_task_runner->PostTask(
FROM_HERE,
base::BindOnce(binder, directory.InitWithNewPipeAndPassReceiver()));
return std::make_unique<FilesystemProxy>(FilesystemProxy::RESTRICTED,
directory_path, std::move(directory),
std::move(io_task_runner));
}
#endif
template <typename T>
base::OnceClosure MakeDeferredDeleter(std::unique_ptr<T> object) {
return base::BindOnce(
[](scoped_refptr<base::SequencedTaskRunner> task_runner, T* object) {
task_runner->DeleteSoon(FROM_HERE, object);
},
base::SequencedTaskRunner::GetCurrentDefault(),
// NOTE: We release `object` immediately. In the case
// where this task never runs, we prefer to leak the
// object rather than potentially destroying it on the
// wrong sequence.
object.release());
}
template <typename T>
void ShutDown(std::unique_ptr<T> object) {
if (T* ptr = object.get()) {
ptr->ShutDown(MakeDeferredDeleter(std::move(object)));
}
}
} // namespace
StorageServiceImpl::StorageServiceImpl(
mojo::PendingReceiver<mojom::StorageService> receiver,
scoped_refptr<base::SequencedTaskRunner> io_task_runner)
: receiver_(this, std::move(receiver)),
io_task_runner_(std::move(io_task_runner)) {}
StorageServiceImpl::~StorageServiceImpl() {
// ShutDown storages before we destroy the service. We transfer ownership of
// the storages to the ShutDown function, which deletes them after ShutDown
// completes.
while (!local_storages_.empty()) {
auto node = local_storages_.extract(local_storages_.begin());
ShutDown(std::move(node.value()));
}
while (!session_storages_.empty()) {
auto node = session_storages_.extract(session_storages_.begin());
ShutDown(std::move(node.value()));
}
}
void StorageServiceImpl::EnableAggressiveDomStorageFlushing() {
StorageAreaImpl::EnableAggressiveCommitDelay();
}
#if !BUILDFLAG(IS_ANDROID)
void StorageServiceImpl::SetDataDirectory(
const base::FilePath& path,
mojo::PendingRemote<mojom::Directory> directory) {
remote_data_directory_path_ = path;
remote_data_directory_.Bind(std::move(directory));
// We can assume we must be sandboxed if we're getting a remote data
// directory handle. Override the default FilesystemProxy factory to produce
// instances restricted to operations within |path|, which can operate
// from within a sandbox.
SetFilesystemProxyFactory(base::BindRepeating(
&CreateRestrictedFilesystemProxy, remote_data_directory_path_,
io_task_runner_,
base::BindRepeating(&StorageServiceImpl::BindDataDirectoryReceiver,
weak_ptr_factory_.GetWeakPtr()),
base::SequencedTaskRunner::GetCurrentDefault()));
// SQLite needs our VFS implementation to work over a FilesystemProxy. This
// installs it as the default implementation for the service process.
sql::SandboxedVfs::Register(
kVfsName, std::make_unique<SandboxedVfsDelegate>(CreateFilesystemProxy()),
/*make_default=*/true);
}
#endif // !BUILDFLAG(IS_ANDROID)
void StorageServiceImpl::BindLocalStorageControl(
const std::optional<base::FilePath>& path,
mojo::PendingReceiver<mojom::LocalStorageControl> receiver) {
if (path.has_value()) {
if (!path->IsAbsolute()) {
// Refuse to bind LocalStorage for relative paths.
return;
}
// TODO(crbug.com/396030877): Remove this workaround to remove the
// pre-existing LocalStorage once the issue is resolved.
auto iter = persistent_local_storage_map_.find(*path);
if (iter != persistent_local_storage_map_.end()) {
ShutDownAndRemoveLocalStorage(iter->second);
}
}
auto new_local_storage = std::make_unique<LocalStorageImpl>(
path.value_or(base::FilePath()),
base::SequencedTaskRunner::GetCurrentDefault(),
base::BindOnce(&StorageServiceImpl::ShutDownAndRemoveLocalStorage,
weak_ptr_factory_.GetWeakPtr()),
std::move(receiver));
if (path.has_value()) {
persistent_local_storage_map_[*path] = new_local_storage.get();
}
local_storages_.insert(std::move(new_local_storage));
}
void StorageServiceImpl::BindSessionStorageControl(
const std::optional<base::FilePath>& path,
mojo::PendingReceiver<mojom::SessionStorageControl> receiver) {
if (path.has_value()) {
if (!path->IsAbsolute()) {
// Refuse to bind SessionStorage for relative paths.
return;
}
// TODO(crbug.com/396030877): Remove this workaround to remove the
// pre-existing SessionStorage once the issue is resolved.
auto iter = persistent_session_storage_map_.find(*path);
if (iter != persistent_session_storage_map_.end()) {
ShutDownAndRemoveSessionStorage(iter->second);
}
}
auto new_session_storage = std::make_unique<SessionStorageImpl>(
path.value_or(base::FilePath()),
base::ThreadPool::CreateSequencedTaskRunner(
{base::MayBlock(), base::WithBaseSyncPrimitives(),
base::TaskShutdownBehavior::BLOCK_SHUTDOWN}),
base::SequencedTaskRunner::GetCurrentDefault(),
#if BUILDFLAG(IS_ANDROID)
// On Android there is no support for session storage restoring, and since
// the restoring code is responsible for database cleanup, we must
// manually delete the old database here before we open a new one.
SessionStorageImpl::BackingMode::kClearDiskStateOnOpen,
#else
path.has_value() ? SessionStorageImpl::BackingMode::kRestoreDiskState
: SessionStorageImpl::BackingMode::kNoDisk,
#endif
std::string(kSessionStorageDirectory),
base::OnceCallback<void(SessionStorageImpl*)>(
base::BindOnce(&StorageServiceImpl::ShutDownAndRemoveSessionStorage,
weak_ptr_factory_.GetWeakPtr())),
std::move(receiver));
if (path.has_value()) {
persistent_session_storage_map_[*path] = new_session_storage.get();
}
session_storages_.insert(std::move(new_session_storage));
}
void StorageServiceImpl::BindTestApi(
mojo::ScopedMessagePipeHandle test_api_receiver) {
GetTestApiBinderForTesting().Run(std::move(test_api_receiver));
}
void StorageServiceImpl::ShutDownAndRemoveSessionStorage(
SessionStorageImpl* storage) {
if (!storage->GetStoragePath().empty()) {
persistent_session_storage_map_.erase(storage->GetStoragePath());
}
auto it = session_storages_.find(storage);
if (it != session_storages_.end()) {
auto node = session_storages_.extract(it);
ShutDown(std::move(node.value()));
}
}
void StorageServiceImpl::ShutDownAndRemoveLocalStorage(
LocalStorageImpl* storage) {
if (!storage->GetStoragePath().empty()) {
persistent_local_storage_map_.erase(storage->GetStoragePath());
}
auto it = local_storages_.find(storage);
if (it != local_storages_.end()) {
auto node = local_storages_.extract(it);
ShutDown(std::move(node.value()));
}
}
#if !BUILDFLAG(IS_ANDROID)
void StorageServiceImpl::BindDataDirectoryReceiver(
mojo::PendingReceiver<mojom::Directory> receiver) {
DCHECK(remote_data_directory_.is_bound());
remote_data_directory_->Clone(std::move(receiver));
}
#endif
} // namespace storage