blob: d525f1572fe47a05522965a2b20bf8116849e97a [file] [log] [blame]
// Copyright (c) 2012 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 "content/browser/fileapi/file_system_manager_impl.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/metrics/user_metrics.h"
#include "base/sequenced_task_runner.h"
#include "base/strings/string_util.h"
#include "base/task/post_task.h"
#include "base/threading/thread.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "components/services/filesystem/public/interfaces/types.mojom.h"
#include "content/browser/bad_message.h"
#include "content/browser/blob_storage/chrome_blob_storage_context.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/fileapi/browser_file_system_helper.h"
#include "content/browser/fileapi/file_system_chooser.h"
#include "content/common/fileapi/webblob_messages.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "ipc/ipc_platform_file.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
#include "net/base/mime_util.h"
#include "storage/browser/blob/blob_data_builder.h"
#include "storage/browser/blob/blob_storage_context.h"
#include "storage/browser/blob/shareable_file_reference.h"
#include "storage/browser/fileapi/file_observers.h"
#include "storage/browser/fileapi/file_permission_policy.h"
#include "storage/browser/fileapi/file_system_context.h"
#include "storage/browser/fileapi/file_writer_impl.h"
#include "storage/browser/fileapi/isolated_context.h"
#include "storage/common/fileapi/file_system_info.h"
#include "storage/common/fileapi/file_system_type_converters.h"
#include "storage/common/fileapi/file_system_types.h"
#include "storage/common/fileapi/file_system_util.h"
#include "third_party/blink/public/common/features.h"
#include "url/gurl.h"
using storage::FileSystemFileUtil;
using storage::FileSystemBackend;
using storage::FileSystemOperation;
using storage::FileSystemURL;
using storage::BlobDataBuilder;
using storage::BlobStorageContext;
namespace content {
namespace {
void RevokeFilePermission(int child_id, const base::FilePath& path) {
ChildProcessSecurityPolicyImpl::GetInstance()->RevokeAllPermissionsForFile(
child_id, path);
}
} // namespace
class FileSystemManagerImpl::FileSystemCancellableOperationImpl
: public blink::mojom::FileSystemCancellableOperation {
using OperationID = storage::FileSystemOperationRunner::OperationID;
public:
FileSystemCancellableOperationImpl(
OperationID id,
FileSystemManagerImpl* file_system_manager_impl)
: id_(id), file_system_manager_impl_(file_system_manager_impl) {}
~FileSystemCancellableOperationImpl() override = default;
private:
void Cancel(CancelCallback callback) override {
file_system_manager_impl_->Cancel(id_, std::move(callback));
}
const OperationID id_;
// |file_system_manager_impl| owns |this| through a StrongBindingSet.
FileSystemManagerImpl* const file_system_manager_impl_;
};
class FileSystemManagerImpl::ReceivedSnapshotListenerImpl
: public blink::mojom::ReceivedSnapshotListener {
public:
ReceivedSnapshotListenerImpl(int snapshot_id,
FileSystemManagerImpl* file_system_manager_impl)
: snapshot_id_(snapshot_id),
file_system_manager_impl_(file_system_manager_impl) {}
~ReceivedSnapshotListenerImpl() override = default;
private:
void DidReceiveSnapshotFile() override {
file_system_manager_impl_->DidReceiveSnapshotFile(snapshot_id_);
}
const int snapshot_id_;
// |file_system_manager_impl| owns |this| through a StrongBindingSet.
FileSystemManagerImpl* const file_system_manager_impl_;
};
struct FileSystemManagerImpl::WriteSyncCallbackEntry {
WriteSyncCallback callback;
int64_t bytes;
explicit WriteSyncCallbackEntry(WriteSyncCallback cb)
: callback(std::move(cb)), bytes(0) {}
};
struct FileSystemManagerImpl::ReadDirectorySyncCallbackEntry {
ReadDirectorySyncCallback callback;
std::vector<filesystem::mojom::DirectoryEntryPtr> entries;
explicit ReadDirectorySyncCallbackEntry(ReadDirectorySyncCallback cb)
: callback(std::move(cb)) {}
};
FileSystemManagerImpl::FileSystemManagerImpl(
int process_id,
int frame_id,
storage::FileSystemContext* file_system_context,
scoped_refptr<ChromeBlobStorageContext> blob_storage_context)
: process_id_(process_id),
frame_id_(frame_id),
context_(file_system_context),
security_policy_(ChildProcessSecurityPolicyImpl::GetInstance()),
blob_storage_context_(blob_storage_context),
weak_factory_(this) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(context_);
DCHECK(blob_storage_context);
bindings_.set_connection_error_handler(base::BindRepeating(
&FileSystemManagerImpl::OnConnectionError, base::Unretained(this)));
}
FileSystemManagerImpl::~FileSystemManagerImpl() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
}
base::WeakPtr<FileSystemManagerImpl> FileSystemManagerImpl::GetWeakPtr() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
return weak_factory_.GetWeakPtr();
}
void FileSystemManagerImpl::BindRequest(
blink::mojom::FileSystemManagerRequest request) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!operation_runner_)
operation_runner_ = context_->CreateFileSystemOperationRunner();
bindings_.AddBinding(this, std::move(request));
}
void FileSystemManagerImpl::Open(const url::Origin& origin,
blink::mojom::FileSystemType file_system_type,
OpenCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (file_system_type == blink::mojom::FileSystemType::kTemporary) {
RecordAction(base::UserMetricsAction("OpenFileSystemTemporary"));
} else if (file_system_type == blink::mojom::FileSystemType::kPersistent) {
RecordAction(base::UserMetricsAction("OpenFileSystemPersistent"));
}
context_->OpenFileSystem(
origin.GetURL(),
mojo::ConvertTo<storage::FileSystemType>(file_system_type),
storage::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
base::BindOnce(&FileSystemManagerImpl::DidOpenFileSystem, GetWeakPtr(),
std::move(callback)));
}
void FileSystemManagerImpl::ResolveURL(const GURL& filesystem_url,
ResolveURLCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
FileSystemURL url(context_->CrackURL(filesystem_url));
base::Optional<base::File::Error> opt_error = ValidateFileSystemURL(url);
if (opt_error) {
std::move(callback).Run(blink::mojom::FileSystemInfo::New(),
base::FilePath(), false, opt_error.value());
return;
}
if (!security_policy_->CanReadFileSystemFile(process_id_, url)) {
std::move(callback).Run(blink::mojom::FileSystemInfo::New(),
base::FilePath(), false,
base::File::FILE_ERROR_SECURITY);
return;
}
context_->ResolveURL(
url, base::BindOnce(&FileSystemManagerImpl::DidResolveURL, GetWeakPtr(),
std::move(callback)));
}
void FileSystemManagerImpl::Move(const GURL& src_path,
const GURL& dest_path,
MoveCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
FileSystemURL src_url(context_->CrackURL(src_path));
FileSystemURL dest_url(context_->CrackURL(dest_path));
base::Optional<base::File::Error> opt_error = ValidateFileSystemURL(src_url);
if (!opt_error)
opt_error = ValidateFileSystemURL(dest_url);
if (opt_error) {
std::move(callback).Run(opt_error.value());
return;
}
if (!security_policy_->CanReadFileSystemFile(process_id_, src_url) ||
!security_policy_->CanDeleteFileSystemFile(process_id_, src_url) ||
!security_policy_->CanCreateFileSystemFile(process_id_, dest_url)) {
std::move(callback).Run(base::File::FILE_ERROR_SECURITY);
return;
}
operation_runner()->Move(
src_url, dest_url, storage::FileSystemOperation::OPTION_NONE,
base::BindRepeating(&FileSystemManagerImpl::DidFinish, GetWeakPtr(),
base::Passed(&callback)));
}
void FileSystemManagerImpl::Copy(const GURL& src_path,
const GURL& dest_path,
CopyCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
FileSystemURL src_url(context_->CrackURL(src_path));
FileSystemURL dest_url(context_->CrackURL(dest_path));
base::Optional<base::File::Error> opt_error = ValidateFileSystemURL(src_url);
if (!opt_error)
opt_error = ValidateFileSystemURL(dest_url);
if (opt_error) {
std::move(callback).Run(opt_error.value());
return;
}
if (!security_policy_->CanReadFileSystemFile(process_id_, src_url) ||
!security_policy_->CanCopyIntoFileSystemFile(process_id_, dest_url)) {
std::move(callback).Run(base::File::FILE_ERROR_SECURITY);
return;
}
operation_runner()->Copy(
src_url, dest_url, storage::FileSystemOperation::OPTION_NONE,
FileSystemOperation::ERROR_BEHAVIOR_ABORT,
storage::FileSystemOperationRunner::CopyProgressCallback(),
base::BindRepeating(&FileSystemManagerImpl::DidFinish, GetWeakPtr(),
base::Passed(&callback)));
}
void FileSystemManagerImpl::Remove(const GURL& path,
bool recursive,
RemoveCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
FileSystemURL url(context_->CrackURL(path));
base::Optional<base::File::Error> opt_error = ValidateFileSystemURL(url);
if (opt_error) {
std::move(callback).Run(opt_error.value());
return;
}
if (!security_policy_->CanDeleteFileSystemFile(process_id_, url)) {
std::move(callback).Run(base::File::FILE_ERROR_SECURITY);
return;
}
operation_runner()->Remove(
url, recursive,
base::BindRepeating(&FileSystemManagerImpl::DidFinish, GetWeakPtr(),
base::Passed(&callback)));
}
void FileSystemManagerImpl::ReadMetadata(const GURL& path,
ReadMetadataCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
FileSystemURL url(context_->CrackURL(path));
base::Optional<base::File::Error> opt_error = ValidateFileSystemURL(url);
if (opt_error) {
std::move(callback).Run(base::File::Info(), opt_error.value());
return;
}
if (!security_policy_->CanReadFileSystemFile(process_id_, url)) {
std::move(callback).Run(base::File::Info(),
base::File::FILE_ERROR_SECURITY);
return;
}
operation_runner()->GetMetadata(
url,
FileSystemOperation::GET_METADATA_FIELD_IS_DIRECTORY |
FileSystemOperation::GET_METADATA_FIELD_SIZE |
FileSystemOperation::GET_METADATA_FIELD_LAST_MODIFIED,
base::BindRepeating(&FileSystemManagerImpl::DidGetMetadata, GetWeakPtr(),
base::Passed(&callback)));
}
void FileSystemManagerImpl::Create(const GURL& path,
bool exclusive,
bool is_directory,
bool recursive,
CreateCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
FileSystemURL url(context_->CrackURL(path));
base::Optional<base::File::Error> opt_error = ValidateFileSystemURL(url);
if (opt_error) {
std::move(callback).Run(opt_error.value());
return;
}
if (!security_policy_->CanCreateFileSystemFile(process_id_, url)) {
std::move(callback).Run(base::File::FILE_ERROR_SECURITY);
return;
}
if (is_directory) {
operation_runner()->CreateDirectory(
url, exclusive, recursive,
base::BindRepeating(&FileSystemManagerImpl::DidFinish, GetWeakPtr(),
base::Passed(&callback)));
} else {
operation_runner()->CreateFile(
url, exclusive,
base::BindRepeating(&FileSystemManagerImpl::DidFinish, GetWeakPtr(),
base::Passed(&callback)));
}
}
void FileSystemManagerImpl::Exists(const GURL& path,
bool is_directory,
ExistsCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
FileSystemURL url(context_->CrackURL(path));
base::Optional<base::File::Error> opt_error = ValidateFileSystemURL(url);
if (opt_error) {
std::move(callback).Run(opt_error.value());
return;
}
if (!security_policy_->CanReadFileSystemFile(process_id_, url)) {
std::move(callback).Run(base::File::FILE_ERROR_SECURITY);
return;
}
if (is_directory) {
operation_runner()->DirectoryExists(
url, base::BindRepeating(&FileSystemManagerImpl::DidFinish,
GetWeakPtr(), base::Passed(&callback)));
} else {
operation_runner()->FileExists(
url, base::BindRepeating(&FileSystemManagerImpl::DidFinish,
GetWeakPtr(), base::Passed(&callback)));
}
}
void FileSystemManagerImpl::ReadDirectory(
const GURL& path,
blink::mojom::FileSystemOperationListenerPtr listener) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
FileSystemURL url(context_->CrackURL(path));
base::Optional<base::File::Error> opt_error = ValidateFileSystemURL(url);
if (opt_error) {
listener->ErrorOccurred(opt_error.value());
return;
}
if (!security_policy_->CanReadFileSystemFile(process_id_, url)) {
listener->ErrorOccurred(base::File::FILE_ERROR_SECURITY);
return;
}
OperationListenerID listener_id = AddOpListener(std::move(listener));
operation_runner()->ReadDirectory(
url, base::BindRepeating(&FileSystemManagerImpl::DidReadDirectory,
GetWeakPtr(), listener_id));
}
void FileSystemManagerImpl::ReadDirectorySync(
const GURL& path,
ReadDirectorySyncCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
FileSystemURL url(context_->CrackURL(path));
base::Optional<base::File::Error> opt_error = ValidateFileSystemURL(url);
if (opt_error) {
std::move(callback).Run(std::vector<filesystem::mojom::DirectoryEntryPtr>(),
opt_error.value());
return;
}
if (!security_policy_->CanReadFileSystemFile(process_id_, url)) {
std::move(callback).Run(std::vector<filesystem::mojom::DirectoryEntryPtr>(),
base::File::FILE_ERROR_SECURITY);
return;
}
operation_runner()->ReadDirectory(
url, base::BindRepeating(
&FileSystemManagerImpl::DidReadDirectorySync, GetWeakPtr(),
base::Owned(
new ReadDirectorySyncCallbackEntry(std::move(callback)))));
}
void FileSystemManagerImpl::Write(
const GURL& file_path,
const std::string& blob_uuid,
int64_t position,
blink::mojom::FileSystemCancellableOperationRequest op_request,
blink::mojom::FileSystemOperationListenerPtr listener) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
FileSystemURL url(context_->CrackURL(file_path));
base::Optional<base::File::Error> opt_error = ValidateFileSystemURL(url);
if (opt_error) {
listener->ErrorOccurred(opt_error.value());
return;
}
if (!security_policy_->CanWriteFileSystemFile(process_id_, url)) {
listener->ErrorOccurred(base::File::FILE_ERROR_SECURITY);
return;
}
std::unique_ptr<storage::BlobDataHandle> blob =
blob_storage_context_->context()->GetBlobDataFromUUID(blob_uuid);
OperationListenerID listener_id = AddOpListener(std::move(listener));
OperationID op_id = operation_runner()->Write(
url, std::move(blob), position,
base::BindRepeating(&FileSystemManagerImpl::DidWrite, GetWeakPtr(),
listener_id));
cancellable_operations_.AddBinding(
std::make_unique<FileSystemCancellableOperationImpl>(op_id, this),
std::move(op_request));
}
void FileSystemManagerImpl::WriteSync(const GURL& file_path,
const std::string& blob_uuid,
int64_t position,
WriteSyncCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
FileSystemURL url(context_->CrackURL(file_path));
base::Optional<base::File::Error> opt_error = ValidateFileSystemURL(url);
if (opt_error) {
std::move(callback).Run(0, opt_error.value());
return;
}
if (!security_policy_->CanWriteFileSystemFile(process_id_, url)) {
std::move(callback).Run(0, base::File::FILE_ERROR_SECURITY);
return;
}
std::unique_ptr<storage::BlobDataHandle> blob =
blob_storage_context_->context()->GetBlobDataFromUUID(blob_uuid);
operation_runner()->Write(
url, std::move(blob), position,
base::BindRepeating(
&FileSystemManagerImpl::DidWriteSync, GetWeakPtr(),
base::Owned(new WriteSyncCallbackEntry(std::move(callback)))));
}
void FileSystemManagerImpl::Truncate(
const GURL& file_path,
int64_t length,
blink::mojom::FileSystemCancellableOperationRequest op_request,
TruncateCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
FileSystemURL url(context_->CrackURL(file_path));
base::Optional<base::File::Error> opt_error = ValidateFileSystemURL(url);
if (opt_error) {
std::move(callback).Run(opt_error.value());
return;
}
if (!security_policy_->CanWriteFileSystemFile(process_id_, url)) {
std::move(callback).Run(base::File::FILE_ERROR_SECURITY);
return;
}
OperationID op_id = operation_runner()->Truncate(
url, length,
base::BindRepeating(&FileSystemManagerImpl::DidFinish, GetWeakPtr(),
base::Passed(&callback)));
cancellable_operations_.AddBinding(
std::make_unique<FileSystemCancellableOperationImpl>(op_id, this),
std::move(op_request));
}
void FileSystemManagerImpl::TruncateSync(const GURL& file_path,
int64_t length,
TruncateSyncCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
FileSystemURL url(context_->CrackURL(file_path));
base::Optional<base::File::Error> opt_error = ValidateFileSystemURL(url);
if (opt_error) {
std::move(callback).Run(opt_error.value());
return;
}
if (!security_policy_->CanWriteFileSystemFile(process_id_, url)) {
std::move(callback).Run(base::File::FILE_ERROR_SECURITY);
return;
}
operation_runner()->Truncate(
url, length,
base::BindRepeating(&FileSystemManagerImpl::DidFinish, GetWeakPtr(),
base::Passed(&callback)));
}
void FileSystemManagerImpl::TouchFile(const GURL& path,
base::Time last_access_time,
base::Time last_modified_time,
TouchFileCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
FileSystemURL url(context_->CrackURL(path));
base::Optional<base::File::Error> opt_error = ValidateFileSystemURL(url);
if (opt_error) {
std::move(callback).Run(opt_error.value());
return;
}
if (!security_policy_->CanCreateFileSystemFile(process_id_, url)) {
std::move(callback).Run(base::File::FILE_ERROR_SECURITY);
return;
}
operation_runner()->TouchFile(
url, last_access_time, last_modified_time,
base::BindRepeating(&FileSystemManagerImpl::DidFinish, GetWeakPtr(),
base::Passed(&callback)));
}
void FileSystemManagerImpl::CreateSnapshotFile(
const GURL& file_path,
CreateSnapshotFileCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
FileSystemURL url(context_->CrackURL(file_path));
// Make sure if this file can be read by the renderer as this is
// called when the renderer is about to create a new File object
// (for reading the file).
base::Optional<base::File::Error> opt_error = ValidateFileSystemURL(url);
if (opt_error) {
std::move(callback).Run(base::File::Info(), base::FilePath(),
opt_error.value(), nullptr);
return;
}
if (!security_policy_->CanReadFileSystemFile(process_id_, url)) {
std::move(callback).Run(base::File::Info(), base::FilePath(),
base::File::FILE_ERROR_SECURITY, nullptr);
return;
}
FileSystemBackend* backend = context_->GetFileSystemBackend(url.type());
if (backend->SupportsStreaming(url)) {
operation_runner()->GetMetadata(
url,
FileSystemOperation::GET_METADATA_FIELD_IS_DIRECTORY |
FileSystemOperation::GET_METADATA_FIELD_SIZE |
FileSystemOperation::GET_METADATA_FIELD_LAST_MODIFIED,
base::BindRepeating(&FileSystemManagerImpl::DidGetMetadataForStreaming,
GetWeakPtr(), base::Passed(&callback)));
} else {
operation_runner()->CreateSnapshotFile(
url, base::BindRepeating(&FileSystemManagerImpl::DidCreateSnapshot,
GetWeakPtr(), base::Passed(&callback), url));
}
}
void FileSystemManagerImpl::GetPlatformPath(const GURL& path,
GetPlatformPathCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
base::FilePath platform_path;
context_->default_file_task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&FileSystemManagerImpl::GetPlatformPathOnFileThread, path,
process_id_, base::Unretained(context_), GetWeakPtr(),
std::move(callback)));
}
void FileSystemManagerImpl::CreateWriter(const GURL& file_path,
CreateWriterCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!base::FeatureList::IsEnabled(blink::features::kWritableFilesAPI)) {
bindings_.ReportBadMessage("FileSystemManager.CreateWriter");
return;
}
FileSystemURL url(context_->CrackURL(file_path));
base::Optional<base::File::Error> opt_error = ValidateFileSystemURL(url);
if (opt_error) {
std::move(callback).Run(opt_error.value(), nullptr);
return;
}
if (!security_policy_->CanWriteFileSystemFile(process_id_, url)) {
std::move(callback).Run(base::File::FILE_ERROR_SECURITY, nullptr);
return;
}
blink::mojom::FileWriterPtr writer;
mojo::MakeStrongBinding(std::make_unique<storage::FileWriterImpl>(
url, context_->CreateFileSystemOperationRunner(),
blob_storage_context_->context()->AsWeakPtr()),
MakeRequest(&writer));
std::move(callback).Run(base::File::FILE_OK, std::move(writer));
}
void FileSystemManagerImpl::ChooseEntry(
blink::mojom::ChooseFileSystemEntryType type,
std::vector<blink::mojom::ChooseFileSystemEntryAcceptsOptionPtr> accepts,
bool include_accepts_all,
ChooseEntryCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!base::FeatureList::IsEnabled(blink::features::kWritableFilesAPI)) {
bindings_.ReportBadMessage("FSMI_WRITABLE_FILES_DISABLED");
return;
}
base::PostTaskWithTraits(
FROM_HERE, {BrowserThread::UI},
base::BindOnce(
&FileSystemChooser::CreateAndShow, process_id_, frame_id_, type,
std::move(accepts), include_accepts_all, std::move(callback),
base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::IO})));
}
void FileSystemManagerImpl::Cancel(
OperationID op_id,
FileSystemCancellableOperationImpl::CancelCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
operation_runner()->Cancel(
op_id, base::BindRepeating(&FileSystemManagerImpl::DidFinish,
GetWeakPtr(), base::Passed(&callback)));
}
void FileSystemManagerImpl::DidReceiveSnapshotFile(int snapshot_id) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
in_transit_snapshot_files_.Remove(snapshot_id);
}
void FileSystemManagerImpl::OnConnectionError() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (bindings_.empty()) {
in_transit_snapshot_files_.Clear();
operation_runner_.reset();
cancellable_operations_.CloseAllBindings();
}
}
void FileSystemManagerImpl::DidFinish(
base::OnceCallback<void(base::File::Error)> callback,
base::File::Error error_code) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
std::move(callback).Run(error_code);
}
void FileSystemManagerImpl::DidGetMetadata(ReadMetadataCallback callback,
base::File::Error result,
const base::File::Info& info) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
std::move(callback).Run(info, result);
}
void FileSystemManagerImpl::DidGetMetadataForStreaming(
CreateSnapshotFileCallback callback,
base::File::Error result,
const base::File::Info& info) {
// For now, streaming Blobs are implemented as a successful snapshot file
// creation with an empty path.
DCHECK_CURRENTLY_ON(BrowserThread::IO);
std::move(callback).Run(info, base::FilePath(), result, nullptr);
}
void FileSystemManagerImpl::DidReadDirectory(
OperationListenerID listener_id,
base::File::Error result,
std::vector<filesystem::mojom::DirectoryEntry> entries,
bool has_more) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
blink::mojom::FileSystemOperationListener* listener =
GetOpListener(listener_id);
if (!listener)
return;
if (result != base::File::FILE_OK) {
DCHECK(!has_more);
listener->ErrorOccurred(result);
RemoveOpListener(listener_id);
return;
}
std::vector<filesystem::mojom::DirectoryEntryPtr> entry_struct_ptrs;
for (const auto& entry : entries) {
entry_struct_ptrs.emplace_back(
filesystem::mojom::DirectoryEntry::New(entry));
}
listener->ResultsRetrieved(std::move(entry_struct_ptrs), has_more);
if (!has_more)
RemoveOpListener(listener_id);
}
void FileSystemManagerImpl::DidReadDirectorySync(
ReadDirectorySyncCallbackEntry* callback_entry,
base::File::Error result,
std::vector<filesystem::mojom::DirectoryEntry> entries,
bool has_more) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
for (const auto& entry : entries) {
callback_entry->entries.emplace_back(
filesystem::mojom::DirectoryEntry::New(std::move(entry)));
}
if (result != base::File::FILE_OK || !has_more) {
std::move(callback_entry->callback)
.Run(std::move(callback_entry->entries), result);
}
}
void FileSystemManagerImpl::DidWrite(OperationListenerID listener_id,
base::File::Error result,
int64_t bytes,
bool complete) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
blink::mojom::FileSystemOperationListener* listener =
GetOpListener(listener_id);
if (!listener)
return;
if (result == base::File::FILE_OK) {
listener->DidWrite(bytes, complete);
if (complete)
RemoveOpListener(listener_id);
} else {
listener->ErrorOccurred(result);
RemoveOpListener(listener_id);
}
}
void FileSystemManagerImpl::DidWriteSync(WriteSyncCallbackEntry* entry,
base::File::Error result,
int64_t bytes,
bool complete) {
entry->bytes += bytes;
if (complete || result != base::File::FILE_OK)
std::move(entry->callback).Run(entry->bytes, result);
}
void FileSystemManagerImpl::DidOpenFileSystem(
OpenCallback callback,
const GURL& root,
const std::string& filesystem_name,
base::File::Error result) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(root.is_valid() || result != base::File::FILE_OK);
std::move(callback).Run(filesystem_name, root, result);
// For OpenFileSystem we do not create a new operation, so no unregister here.
}
void FileSystemManagerImpl::DidResolveURL(
ResolveURLCallback callback,
base::File::Error result,
const storage::FileSystemInfo& info,
const base::FilePath& file_path,
storage::FileSystemContext::ResolvedEntryType type) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (result == base::File::FILE_OK &&
type == storage::FileSystemContext::RESOLVED_ENTRY_NOT_FOUND)
result = base::File::FILE_ERROR_NOT_FOUND;
base::FilePath normalized_path(
storage::VirtualPath::GetNormalizedFilePath(file_path));
std::move(callback).Run(
mojo::ConvertTo<blink::mojom::FileSystemInfoPtr>(info),
std::move(normalized_path),
type == storage::FileSystemContext::RESOLVED_ENTRY_DIRECTORY, result);
// For ResolveURL we do not create a new operation, so no unregister here.
}
void FileSystemManagerImpl::DidCreateSnapshot(
CreateSnapshotFileCallback callback,
const storage::FileSystemURL& url,
base::File::Error result,
const base::File::Info& info,
const base::FilePath& platform_path,
scoped_refptr<storage::ShareableFileReference> /* unused */) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (result != base::File::FILE_OK) {
std::move(callback).Run(base::File::Info(), base::FilePath(), result,
nullptr);
return;
}
scoped_refptr<storage::ShareableFileReference> file_ref =
storage::ShareableFileReference::Get(platform_path);
if (!security_policy_->CanReadFile(process_id_, platform_path)) {
// Give per-file read permission to the snapshot file if it hasn't it yet.
// In order for the renderer to be able to read the file via File object,
// it must be granted per-file read permission for the file's platform
// path. By now, it has already been verified that the renderer has
// sufficient permissions to read the file, so giving per-file permission
// here must be safe.
security_policy_->GrantReadFile(process_id_, platform_path);
// Revoke all permissions for the file when the last ref of the file
// is dropped.
if (!file_ref.get()) {
// Create a reference for temporary permission handling.
file_ref = storage::ShareableFileReference::GetOrCreate(
platform_path,
storage::ShareableFileReference::DONT_DELETE_ON_FINAL_RELEASE,
context_->default_file_task_runner());
}
file_ref->AddFinalReleaseCallback(
base::BindOnce(&RevokeFilePermission, process_id_));
}
if (file_ref.get()) {
// This ref is held until DidReceiveSnapshotFile is called.
int request_id = in_transit_snapshot_files_.Add(file_ref);
blink::mojom::ReceivedSnapshotListenerPtr listener_ptr;
snapshot_listeners_.AddBinding(
std::make_unique<ReceivedSnapshotListenerImpl>(request_id, this),
mojo::MakeRequest<blink::mojom::ReceivedSnapshotListener>(
&listener_ptr));
// Return the file info and platform_path.
std::move(callback).Run(info, platform_path, result,
std::move(listener_ptr));
return;
}
// Return the file info and platform_path.
std::move(callback).Run(info, platform_path, result, nullptr);
}
void FileSystemManagerImpl::DidGetPlatformPath(GetPlatformPathCallback callback,
base::FilePath platform_path) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
std::move(callback).Run(platform_path);
}
// static
void FileSystemManagerImpl::GetPlatformPathOnFileThread(
const GURL& path,
int process_id,
storage::FileSystemContext* context,
base::WeakPtr<FileSystemManagerImpl> file_system_manager,
GetPlatformPathCallback callback) {
DCHECK(context->default_file_task_runner()->RunsTasksInCurrentSequence());
SyncGetPlatformPath(
context, process_id, path,
base::BindOnce(
[](base::WeakPtr<FileSystemManagerImpl> file_system_manager,
GetPlatformPathCallback callback,
const base::FilePath& platform_path) {
base::PostTaskWithTraits(
FROM_HERE, {BrowserThread::IO},
base::BindOnce(&FileSystemManagerImpl::DidGetPlatformPath,
std::move(file_system_manager),
std::move(callback), platform_path));
},
std::move(file_system_manager), std::move(callback)));
}
base::Optional<base::File::Error> FileSystemManagerImpl::ValidateFileSystemURL(
const storage::FileSystemURL& url) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!FileSystemURLIsValid(context_, url))
return base::File::FILE_ERROR_INVALID_URL;
// Deny access to files in PluginPrivate FileSystem from JavaScript.
// TODO(nhiroki): Move this filter somewhere else since this is not for
// validation.
if (url.type() == storage::kFileSystemTypePluginPrivate)
return base::File::FILE_ERROR_SECURITY;
return base::nullopt;
}
FileSystemManagerImpl::OperationListenerID FileSystemManagerImpl::AddOpListener(
blink::mojom::FileSystemOperationListenerPtr listener) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
int op_id = next_operation_listener_id_++;
listener.set_connection_error_handler(
base::BindOnce(&FileSystemManagerImpl::OnConnectionErrorForOpListeners,
base::Unretained(this), op_id));
op_listeners_[op_id] = std::move(listener);
return op_id;
}
void FileSystemManagerImpl::RemoveOpListener(OperationListenerID listener_id) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(op_listeners_.find(listener_id) != op_listeners_.end());
op_listeners_.erase(listener_id);
}
blink::mojom::FileSystemOperationListener* FileSystemManagerImpl::GetOpListener(
OperationListenerID listener_id) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (op_listeners_.find(listener_id) == op_listeners_.end())
return nullptr;
return &*op_listeners_[listener_id];
}
void FileSystemManagerImpl::OnConnectionErrorForOpListeners(
OperationListenerID listener_id) {
RemoveOpListener(listener_id);
}
} // namespace content