[go: nahoru, domu]

blob: 863d9f49461310d3505c4d42c23c320d944c9166 [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// 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/file_system_context.h"
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <utility>
#include "base/containers/contains.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/ptr_util.h"
#include "base/memory/scoped_refptr.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "base/types/pass_key.h"
#include "components/file_access/scoped_file_access_delegate.h"
#include "components/services/storage/public/cpp/buckets/bucket_info.h"
#include "components/services/storage/public/cpp/buckets/constants.h"
#include "components/services/storage/public/cpp/quota_client_callback_wrapper.h"
#include "components/services/storage/public/mojom/quota_client.mojom.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "storage/browser/file_system/copy_or_move_file_validator.h"
#include "storage/browser/file_system/external_mount_points.h"
#include "storage/browser/file_system/file_permission_policy.h"
#include "storage/browser/file_system/file_stream_reader.h"
#include "storage/browser/file_system/file_stream_writer.h"
#include "storage/browser/file_system/file_system_features.h"
#include "storage/browser/file_system/file_system_file_util.h"
#include "storage/browser/file_system/file_system_operation.h"
#include "storage/browser/file_system/file_system_operation_runner.h"
#include "storage/browser/file_system/file_system_options.h"
#include "storage/browser/file_system/file_system_quota_client.h"
#include "storage/browser/file_system/file_system_request_info.h"
#include "storage/browser/file_system/file_system_url.h"
#include "storage/browser/file_system/file_system_util.h"
#include "storage/browser/file_system/isolated_context.h"
#include "storage/browser/file_system/isolated_file_system_backend.h"
#include "storage/browser/file_system/mount_points.h"
#include "storage/browser/file_system/quota/quota_reservation.h"
#include "storage/browser/file_system/sandbox_file_system_backend.h"
#include "storage/browser/quota/quota_manager_proxy.h"
#include "storage/browser/quota/special_storage_policy.h"
#include "storage/common/file_system/file_system_info.h"
#include "storage/common/file_system/file_system_util.h"
#include "third_party/blink/public/common/storage_key/storage_key.h"
#include "third_party/blink/public/mojom/quota/quota_types.mojom-shared.h"
#include "third_party/blink/public/mojom/quota/quota_types.mojom.h"
#include "third_party/leveldatabase/leveldb_chrome.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace storage {
namespace {
void DidGetMetadataForResolveURL(const base::FilePath& path,
FileSystemContext::ResolveURLCallback callback,
const FileSystemInfo& info,
base::File::Error error,
const base::File::Info& file_info) {
if (error != base::File::FILE_OK) {
if (error == base::File::FILE_ERROR_NOT_FOUND) {
std::move(callback).Run(base::File::FILE_OK, info, path,
FileSystemContext::RESOLVED_ENTRY_NOT_FOUND);
} else {
std::move(callback).Run(error, FileSystemInfo(), base::FilePath(),
FileSystemContext::RESOLVED_ENTRY_NOT_FOUND);
}
return;
}
std::move(callback).Run(error, info, path,
file_info.is_directory
? FileSystemContext::RESOLVED_ENTRY_DIRECTORY
: FileSystemContext::RESOLVED_ENTRY_FILE);
}
void RelayResolveURLCallback(
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
FileSystemContext::ResolveURLCallback callback,
base::File::Error result,
const FileSystemInfo& info,
const base::FilePath& file_path,
FileSystemContext::ResolvedEntryType type) {
task_runner->PostTask(FROM_HERE, base::BindOnce(std::move(callback), result,
info, file_path, type));
}
} // namespace
// static
int FileSystemContext::GetPermissionPolicy(FileSystemType type) {
switch (type) {
case kFileSystemTypeTemporary:
case kFileSystemTypePersistent:
case kFileSystemTypeSyncable:
return FILE_PERMISSION_SANDBOX;
case kFileSystemTypeLocalForPlatformApp:
case kFileSystemTypeLocal:
case kFileSystemTypeProvided:
case kFileSystemTypeDeviceMediaAsFileStorage:
case kFileSystemTypeDriveFs:
case kFileSystemTypeArcContent:
case kFileSystemTypeArcDocumentsProvider:
case kFileSystemTypeSmbFs:
case kFileSystemTypeFuseBox:
return FILE_PERMISSION_USE_FILE_PERMISSION;
case kFileSystemTypeDeviceMedia:
case kFileSystemTypeLocalMedia:
return FILE_PERMISSION_USE_FILE_PERMISSION;
// Following types are only accessed via IsolatedFileSystem, and
// don't have their own permission policies.
case kFileSystemTypeDragged:
case kFileSystemTypeForTransientFile:
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;
}
scoped_refptr<FileSystemContext> FileSystemContext::Create(
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
scoped_refptr<base::SequencedTaskRunner> file_task_runner,
scoped_refptr<ExternalMountPoints> external_mount_points,
scoped_refptr<SpecialStoragePolicy> special_storage_policy,
scoped_refptr<QuotaManagerProxy> quota_manager_proxy,
std::vector<std::unique_ptr<FileSystemBackend>> additional_backends,
const std::vector<URLRequestAutoMountHandler>& auto_mount_handlers,
const base::FilePath& partition_path,
const FileSystemOptions& options) {
bool force_override_incognito = base::FeatureList::IsEnabled(
features::kIncognitoFileSystemContextForTesting);
FileSystemOptions maybe_overridden_options =
force_override_incognito
? FileSystemOptions(FileSystemOptions::PROFILE_MODE_INCOGNITO,
/*force_in_memory=*/true,
options.additional_allowed_schemes())
: options;
auto context = base::MakeRefCounted<FileSystemContext>(
std::move(io_task_runner), std::move(file_task_runner),
std::move(external_mount_points), std::move(special_storage_policy),
std::move(quota_manager_proxy), std::move(additional_backends),
auto_mount_handlers, partition_path, maybe_overridden_options,
base::PassKey<FileSystemContext>());
context->Initialize();
return context;
}
FileSystemContext::FileSystemContext(
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
scoped_refptr<base::SequencedTaskRunner> file_task_runner,
scoped_refptr<ExternalMountPoints> external_mount_points,
scoped_refptr<SpecialStoragePolicy> special_storage_policy,
scoped_refptr<QuotaManagerProxy> quota_manager_proxy,
std::vector<std::unique_ptr<FileSystemBackend>> additional_backends,
const std::vector<URLRequestAutoMountHandler>& auto_mount_handlers,
const base::FilePath& partition_path,
const FileSystemOptions& options,
base::PassKey<FileSystemContext>)
: base::RefCountedDeleteOnSequence<FileSystemContext>(io_task_runner),
env_override_(options.is_in_memory()
? leveldb_chrome::NewMemEnv("FileSystem")
: nullptr),
io_task_runner_(std::move(io_task_runner)),
default_file_task_runner_(std::move(file_task_runner)),
quota_manager_proxy_(std::move(quota_manager_proxy)),
quota_client_(std::make_unique<FileSystemQuotaClient>(this)),
quota_client_wrapper_(
std::make_unique<QuotaClientCallbackWrapper>(quota_client_.get())),
sandbox_delegate_(std::make_unique<SandboxFileSystemBackendDelegate>(
quota_manager_proxy_.get(),
default_file_task_runner_.get(),
partition_path,
special_storage_policy,
options,
env_override_.get())),
sandbox_backend_(
std::make_unique<SandboxFileSystemBackend>(sandbox_delegate_.get())),
additional_backends_(std::move(additional_backends)),
auto_mount_handlers_(auto_mount_handlers),
external_mount_points_(std::move(external_mount_points)),
partition_path_(partition_path),
is_incognito_(options.is_incognito()),
operation_runner_(std::make_unique<FileSystemOperationRunner>(
base::PassKey<FileSystemContext>(),
this)),
quota_client_receiver_(
std::make_unique<mojo::Receiver<mojom::QuotaClient>>(
quota_client_wrapper_.get())) {
RegisterBackend(sandbox_backend_.get());
for (const auto& backend : additional_backends_)
RegisterBackend(backend.get());
// If the embedder's additional backends already provide support for
// kFileSystemTypeLocal and kFileSystemTypeLocalForPlatformApp then
// IsolatedFileSystemBackend does not need to handle them. For example, on
// Chrome OS the additional backend ash::FileSystemBackend handles these
// types.
isolated_backend_ = std::make_unique<IsolatedFileSystemBackend>(
!base::Contains(backend_map_, kFileSystemTypeLocal),
!base::Contains(backend_map_, kFileSystemTypeLocalForPlatformApp));
RegisterBackend(isolated_backend_.get());
}
void FileSystemContext::Initialize() {
sandbox_backend_->Initialize(this);
isolated_backend_->Initialize(this);
for (const auto& backend : additional_backends_)
backend->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_.get());
url_crackers_.push_back(ExternalMountPoints::GetSystemInstance());
url_crackers_.push_back(IsolatedContext::GetInstance());
if (!quota_manager_proxy_)
return;
// QuotaManagerProxy::RegisterClient() must be called synchronously during
// DatabaseTracker creation until crbug.com/1182630 is fixed.
mojo::PendingRemote<mojom::QuotaClient> quota_client_remote;
mojo::PendingReceiver<mojom::QuotaClient> quota_client_receiver =
quota_client_remote.InitWithNewPipeAndPassReceiver();
quota_manager_proxy_->RegisterClient(std::move(quota_client_remote),
QuotaClientType::kFileSystem,
QuotaManagedStorageTypes());
io_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
[](scoped_refptr<FileSystemContext> self,
mojo::PendingReceiver<mojom::QuotaClient> receiver) {
if (!self->quota_client_receiver_) {
// Shutdown() may be called directly on the IO sequence. If that
// happens, `quota_client_receiver_` may get reset before this
// task runs.
return;
}
self->quota_client_receiver_->Bind(std::move(receiver));
},
base::RetainedRef(this), std::move(quota_client_receiver)));
}
void FileSystemContext::DeleteDataForStorageKeyOnFileTaskRunner(
const blink::StorageKey& storage_key) {
DCHECK(default_file_task_runner()->RunsTasksInCurrentSequence());
DCHECK(!storage_key.origin().opaque());
// Different FileSystemTypes may map to the same BucketLocator. Retrieve the
// bucket once for those FileSystemTypes.
base::flat_map<blink::mojom::StorageType, std::vector<FileSystemType>>
quota_to_fs_type_map;
for (auto& type_backend_pair : backend_map_) {
if (!type_backend_pair.second->GetQuotaUtil()) {
continue;
}
auto quota_type = FileSystemTypeToQuotaStorageType(type_backend_pair.first);
quota_to_fs_type_map[quota_type].push_back(type_backend_pair.first);
}
for (auto& type_pair : quota_to_fs_type_map) {
quota_manager_proxy()->GetOrCreateBucketDeprecated(
BucketInitParams::ForDefaultBucket(storage_key), type_pair.first,
default_file_task_runner_.get(),
base::BindOnce(&FileSystemContext::OnGetBucketForStorageKeyDeletion,
weak_factory_.GetWeakPtr(), type_pair.second));
}
}
void FileSystemContext::OnGetBucketForStorageKeyDeletion(
std::vector<FileSystemType> types,
QuotaErrorOr<BucketInfo> result) {
if (!result.has_value()) {
return;
}
auto bucket = result->ToBucketLocator();
for (auto& type : types) {
FileSystemBackend* backend = GetFileSystemBackend(type);
backend->GetQuotaUtil()->DeleteBucketDataOnFileTaskRunner(
this, quota_manager_proxy().get(), bucket, type);
}
// Trigger cache deletion for the default bucket once. This is done after
// `storage_key` data deletion so deletion doesn't trigger twice for
// kFileSystemTypeTemporary and kFileSystemTypePersistent.
if (bucket.type == blink::mojom::StorageType::kTemporary) {
if (auto* quota_util = GetQuotaUtil(kFileSystemTypeTemporary)) {
quota_util->DeleteCachedDefaultBucket(bucket.storage_key);
}
}
}
scoped_refptr<QuotaReservation>
FileSystemContext::CreateQuotaReservationOnFileTaskRunner(
const blink::StorageKey& storage_key,
FileSystemType type) {
DCHECK(default_file_task_runner()->RunsTasksInCurrentSequence());
FileSystemBackend* backend = GetFileSystemBackend(type);
if (!backend || !backend->GetQuotaUtil())
return scoped_refptr<QuotaReservation>();
return backend->GetQuotaUtil()->CreateQuotaReservationOnFileTaskRunner(
storage_key, type);
}
void FileSystemContext::Shutdown() {
if (!io_task_runner_->RunsTasksInCurrentSequence()) {
io_task_runner_->PostTask(FROM_HERE,
base::BindOnce(&FileSystemContext::Shutdown,
base::WrapRefCounted(this)));
return;
}
// The mojo receiver must be destroyed before the instance it calls into is
// destroyed.
quota_client_receiver_.reset();
quota_client_wrapper_.reset();
quota_client_.reset();
operation_runner_->Shutdown();
}
FileSystemQuotaUtil* FileSystemContext::GetQuotaUtil(
FileSystemType type) const {
FileSystemBackend* backend = GetFileSystemBackend(type);
if (!backend)
return nullptr;
return backend->GetQuotaUtil();
}
AsyncFileUtil* FileSystemContext::GetAsyncFileUtil(FileSystemType type) const {
FileSystemBackend* backend = GetFileSystemBackend(type);
if (!backend)
return nullptr;
return backend->GetAsyncFileUtil(type);
}
CopyOrMoveFileValidatorFactory*
FileSystemContext::GetCopyOrMoveFileValidatorFactory(
FileSystemType type,
base::File::Error* error_code) const {
DCHECK(error_code);
*error_code = base::File::FILE_OK;
FileSystemBackend* backend = GetFileSystemBackend(type);
if (!backend)
return nullptr;
return backend->GetCopyOrMoveFileValidatorFactory(type, error_code);
}
FileSystemBackend* FileSystemContext::GetFileSystemBackend(
FileSystemType type) const {
auto found = backend_map_.find(type);
if (found != backend_map_.end())
return found->second;
NOTREACHED() << "Unknown filesystem type: " << type;
return nullptr;
}
WatcherManager* FileSystemContext::GetWatcherManager(
FileSystemType type) const {
FileSystemBackend* backend = GetFileSystemBackend(type);
if (!backend)
return nullptr;
return backend->GetWatcherManager(type);
}
bool FileSystemContext::IsSandboxFileSystem(FileSystemType type) const {
auto found = backend_map_.find(type);
return found != backend_map_.end() && found->second->GetQuotaUtil();
}
const UpdateObserverList* FileSystemContext::GetUpdateObservers(
FileSystemType type) const {
FileSystemBackend* backend = GetFileSystemBackend(type);
return backend->GetUpdateObservers(type);
}
const ChangeObserverList* FileSystemContext::GetChangeObservers(
FileSystemType type) const {
FileSystemBackend* backend = GetFileSystemBackend(type);
return backend->GetChangeObservers(type);
}
const AccessObserverList* FileSystemContext::GetAccessObservers(
FileSystemType type) const {
FileSystemBackend* backend = GetFileSystemBackend(type);
return backend->GetAccessObservers(type);
}
std::vector<FileSystemType> FileSystemContext::GetFileSystemTypes() const {
std::vector<FileSystemType> types;
types.reserve(backend_map_.size());
for (const auto& type_backend_pair : backend_map_)
types.push_back(type_backend_pair.first);
return types;
}
void FileSystemContext::OpenFileSystem(
const blink::StorageKey& storage_key,
const absl::optional<storage::BucketLocator>& bucket,
FileSystemType type,
OpenFileSystemMode mode,
OpenFileSystemCallback callback) {
DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
DCHECK(!callback.is_null());
CHECK(!bucket.has_value() || storage_key == bucket->storage_key);
if (!FileSystemContext::IsSandboxFileSystem(type)) {
// Disallow opening a non-sandboxed filesystem.
std::move(callback).Run(FileSystemURL(), std::string(),
base::File::FILE_ERROR_SECURITY);
return;
}
// Quota manager isn't provided by all tests.
if (!quota_manager_proxy()) {
ResolveURLOnOpenFileSystem(storage_key, bucket, type, mode,
std::move(callback));
return;
}
auto got_bucket = base::BindOnce(&FileSystemContext::OnGetOrCreateBucket,
weak_factory_.GetWeakPtr(), storage_key,
type, mode, std::move(callback));
if (bucket.has_value()) {
if (!bucket->id) {
// This branch can be hit if the bucket has been deleted but `BucketHost`
// is still alive.
std::move(got_bucket).Run(base::unexpected(QuotaError::kUnknownError));
} else {
quota_manager_proxy()->GetBucketById(bucket->id, io_task_runner_.get(),
std::move(got_bucket));
}
} else {
// Ensure default bucket for `storage_key` exists so that Quota API
// is aware of the usage.
quota_manager_proxy()->GetOrCreateBucketDeprecated(
BucketInitParams::ForDefaultBucket(storage_key),
FileSystemTypeToQuotaStorageType(type), io_task_runner_.get(),
std::move(got_bucket));
}
}
void FileSystemContext::OnGetOrCreateBucket(
const blink::StorageKey& storage_key,
FileSystemType type,
OpenFileSystemMode mode,
OpenFileSystemCallback callback,
QuotaErrorOr<BucketInfo> result) {
if (!result.has_value()) {
std::move(callback).Run(FileSystemURL(), std::string(),
base::File::FILE_ERROR_FAILED);
return;
}
ResolveURLOnOpenFileSystem(storage_key, result->ToBucketLocator(), type, mode,
std::move(callback));
}
void FileSystemContext::ResolveURLOnOpenFileSystem(
const blink::StorageKey& storage_key,
const absl::optional<storage::BucketLocator>& bucket,
FileSystemType type,
OpenFileSystemMode mode,
OpenFileSystemCallback callback) {
DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
FileSystemBackend* backend = GetFileSystemBackend(type);
if (!backend) {
std::move(callback).Run(FileSystemURL(), std::string(),
base::File::FILE_ERROR_SECURITY);
return;
}
FileSystemURL url =
CreateCrackedFileSystemURL(storage_key, type, base::FilePath());
// Override the default storage bucket if a custom BucketLocator was given.
if (bucket.has_value()) {
url.SetBucket(bucket.value());
}
// Bind `this` to the callback to ensure this instance stays alive while the
// URL is resolving.
backend->ResolveURL(
url, mode,
base::BindOnce(&FileSystemContext::DidResolveURLOnOpenFileSystem, this,
url, std::move(callback)));
}
void FileSystemContext::DidResolveURLOnOpenFileSystem(
const FileSystemURL& filesystem_root_url,
OpenFileSystemCallback callback,
const GURL& filesystem_root,
const std::string& filesystem_name,
base::File::Error error) {
DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
std::move(callback).Run(filesystem_root_url, filesystem_name, error);
}
void FileSystemContext::ResolveURL(const FileSystemURL& url,
ResolveURLCallback callback) {
DCHECK(!callback.is_null());
// If not on IO thread, forward before passing the task to the backend.
if (!io_task_runner_->RunsTasksInCurrentSequence()) {
ResolveURLCallback relay_callback = base::BindOnce(
&RelayResolveURLCallback,
base::SingleThreadTaskRunner::GetCurrentDefault(), std::move(callback));
io_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&FileSystemContext::ResolveURL, this, url,
std::move(relay_callback)));
return;
}
FileSystemBackend* backend = GetFileSystemBackend(url.type());
if (!backend) {
std::move(callback).Run(base::File::FILE_ERROR_SECURITY, FileSystemInfo(),
base::FilePath(),
FileSystemContext::RESOLVED_ENTRY_NOT_FOUND);
return;
}
backend->ResolveURL(
url, OPEN_FILE_SYSTEM_FAIL_IF_NONEXISTENT,
base::BindOnce(&FileSystemContext::DidOpenFileSystemForResolveURL, this,
url, std::move(callback)));
}
void FileSystemContext::AttemptAutoMountForURLRequest(
const FileSystemRequestInfo& request_info,
StatusCallback callback) {
const FileSystemURL filesystem_url(request_info.url,
request_info.storage_key);
if (filesystem_url.type() == kFileSystemTypeExternal) {
for (auto& handler : auto_mount_handlers_) {
auto split_callback = base::SplitOnceCallback(std::move(callback));
callback = std::move(split_callback.first);
if (handler.Run(request_info, filesystem_url,
std::move(split_callback.second))) {
// The `callback` will be run if true was returned.
return;
}
}
}
// If every handler returned false, then `callback` was not run yet.
std::move(callback).Run(base::File::FILE_ERROR_NOT_FOUND);
}
void FileSystemContext::DeleteFileSystem(const blink::StorageKey& storage_key,
FileSystemType type,
StatusCallback callback) {
DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
DCHECK(!storage_key.origin().opaque());
DCHECK(!callback.is_null());
FileSystemBackend* backend = GetFileSystemBackend(type);
if (!backend) {
std::move(callback).Run(base::File::FILE_ERROR_SECURITY);
return;
}
if (!backend->GetQuotaUtil()) {
std::move(callback).Run(base::File::FILE_ERROR_INVALID_OPERATION);
return;
}
quota_manager_proxy()->GetOrCreateBucketDeprecated(
BucketInitParams::ForDefaultBucket(storage_key),
FileSystemTypeToQuotaStorageType(type), io_task_runner_.get(),
base::BindOnce(&FileSystemContext::OnGetBucketForDeleteFileSystem,
weak_factory_.GetWeakPtr(), type, std::move(callback)));
}
void FileSystemContext::OnGetBucketForDeleteFileSystem(
FileSystemType type,
StatusCallback callback,
QuotaErrorOr<BucketInfo> result) {
if (!result.has_value()) {
std::move(callback).Run(base::File::FILE_ERROR_FAILED);
return;
}
FileSystemBackend* backend = GetFileSystemBackend(type);
default_file_task_runner()->PostTaskAndReplyWithResult(
FROM_HERE,
// It is safe to pass Unretained(quota_util) since context owns it.
base::BindOnce(&FileSystemQuotaUtil::DeleteBucketDataOnFileTaskRunner,
base::Unretained(backend->GetQuotaUtil()),
base::RetainedRef(this),
base::Unretained(quota_manager_proxy().get()),
result->ToBucketLocator(), type),
std::move(callback));
}
std::unique_ptr<FileStreamReader> FileSystemContext::CreateFileStreamReader(
const FileSystemURL& url,
int64_t offset,
int64_t max_bytes_to_read,
const base::Time& expected_modification_time,
file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
file_access) {
if (!url.is_valid())
return nullptr;
FileSystemBackend* backend = GetFileSystemBackend(url.type());
if (!backend)
return nullptr;
return backend->CreateFileStreamReader(url, offset, max_bytes_to_read,
expected_modification_time, this,
std::move(file_access));
}
std::unique_ptr<FileStreamWriter> FileSystemContext::CreateFileStreamWriter(
const FileSystemURL& url,
int64_t offset) {
if (!url.is_valid())
return nullptr;
FileSystemBackend* backend = GetFileSystemBackend(url.type());
if (!backend)
return nullptr;
return backend->CreateFileStreamWriter(url, offset, this);
}
std::unique_ptr<FileSystemOperationRunner>
FileSystemContext::CreateFileSystemOperationRunner() {
return std::make_unique<FileSystemOperationRunner>(
base::PassKey<FileSystemContext>(), this);
}
base::SequenceBound<FileSystemOperationRunner>
FileSystemContext::CreateSequenceBoundFileSystemOperationRunner() {
return base::SequenceBound<FileSystemOperationRunner>(
io_task_runner_, base::PassKey<FileSystemContext>(),
base::WrapRefCounted(this));
}
FileSystemURL FileSystemContext::CrackURL(
const GURL& url,
const blink::StorageKey& storage_key) const {
return CrackFileSystemURL(FileSystemURL(url, storage_key));
}
FileSystemURL FileSystemContext::CrackURLInFirstPartyContext(
const GURL& url) const {
return CrackFileSystemURL(FileSystemURL(
url, blink::StorageKey::CreateFirstParty(url::Origin::Create(url))));
}
FileSystemURL FileSystemContext::CreateCrackedFileSystemURL(
const blink::StorageKey& storage_key,
FileSystemType type,
const base::FilePath& path) const {
return CrackFileSystemURL(FileSystemURL(storage_key, type, path));
}
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 (url.type() == kFileSystemTypeTemporary)
return true;
if (url.type() == kFileSystemTypePersistent &&
base::FeatureList::IsEnabled(
features::kEnablePersistentFilesystemInIncognito)) {
return true;
}
return !is_incognito_ || !FileSystemContext::IsSandboxFileSystem(url.type());
}
FileSystemContext::~FileSystemContext() {
// TODO(crbug.com/823854) This is a leak. Delete env after the backends have
// been deleted.
env_override_.release();
}
std::vector<blink::mojom::StorageType>
FileSystemContext::QuotaManagedStorageTypes() {
std::vector<blink::mojom::StorageType> quota_storage_types;
for (const auto& file_system_type_and_backend : backend_map_) {
FileSystemType file_system_type = file_system_type_and_backend.first;
blink::mojom::StorageType storage_type =
FileSystemTypeToQuotaStorageType(file_system_type);
// An more elegant way of filtering out non-quota-managed backends would be
// to call GetQuotaUtil() on backends. Unfortunately, the method assumes the
// backends are initialized.
if (storage_type == blink::mojom::StorageType::kUnknown ||
storage_type == blink::mojom::StorageType::kDeprecatedQuotaNotManaged) {
continue;
}
quota_storage_types.push_back(storage_type);
}
return quota_storage_types;
}
std::unique_ptr<FileSystemOperation>
FileSystemContext::CreateFileSystemOperation(OperationType type,
const FileSystemURL& url,
base::File::Error* error_code) {
if (!url.is_valid()) {
if (error_code)
*error_code = base::File::FILE_ERROR_INVALID_URL;
return nullptr;
}
FileSystemBackend* backend = GetFileSystemBackend(url.type());
if (!backend) {
if (error_code)
*error_code = base::File::FILE_ERROR_FAILED;
return nullptr;
}
base::File::Error fs_error = base::File::FILE_OK;
std::unique_ptr<FileSystemOperation> operation =
backend->CreateFileSystemOperation(type, 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 (MountPoints* url_cracker : url_crackers_) {
if (!url_cracker->HandlesFileSystemMountType(current.type()))
continue;
cracked = url_cracker->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 (const auto& mount_type : mount_types) {
if (backend->CanHandleType(mount_type)) {
const bool inserted =
backend_map_.insert(std::make_pair(mount_type, 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,
FileSystemContext::ResolveURLCallback callback,
const GURL& filesystem_root,
const std::string& filesystem_name,
base::File::Error error) {
DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
if (error != base::File::FILE_OK) {
std::move(callback).Run(error, FileSystemInfo(), base::FilePath(),
FileSystemContext::RESOLVED_ENTRY_NOT_FOUND);
return;
}
FileSystemInfo info(filesystem_name, filesystem_root, url.mount_type());
// Extract the virtual path not containing a filesystem type part from `url`.
base::FilePath parent =
CrackURLInFirstPartyContext(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, FileSystemOperation::GET_METADATA_FIELD_IS_DIRECTORY,
base::BindOnce(&DidGetMetadataForResolveURL, path, std::move(callback),
info));
}
} // namespace storage