Commit 5a4dee22 authored by Marijn Kruisselbrink's avatar Marijn Kruisselbrink Committed by Commit Bot

[NativeFS] Add hooks that will be used by IndexedDB to serialize handles.

This adds a new (internal) mojo interface that will be used by IndexedDB
code (in the Storage Service process) to talk to Native File System code
(in the browser process) to serialize and deserialize handles.

Bug: 955193
Tbr: pwnall@chromium.org
Change-Id: I01aad3db0787483caff11f94904eda00829c6033
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2025950
Commit-Queue: Marijn Kruisselbrink <mek@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarJoshua Bell <jsbell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#737517}
parent 830b4e93
......@@ -8,6 +8,7 @@ mojom("mojom") {
sources = [
"indexed_db_control.mojom",
"local_storage_control.mojom",
"native_file_system_context.mojom",
"origin_context.mojom",
"partition.mojom",
"storage_service.mojom",
......@@ -15,7 +16,10 @@ mojom("mojom") {
public_deps = [
"//mojo/public/mojom/base",
"//third_party/blink/public/mojom/dom_storage",
"//third_party/blink/public/mojom:mojom_platform",
"//url/mojom:url_mojom_origin",
]
overridden_deps = [ "//third_party/blink/public/mojom:mojom_platform" ]
component_deps = [ "//third_party/blink/public/common" ]
}
// Copyright 2020 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.
module storage.mojom;
import "third_party/blink/public/mojom/native_file_system/native_file_system_transfer_token.mojom";
import "url/mojom/origin.mojom";
// This interface is the primary access point to Chrome's Native File System API
// implementation. This interface lives in the browser process and is used by
// IndexedDB in the storage service to serialize and deserialize Native File
// System handles.
//
// This interface has capabilities that should NOT be accessible to a renderer.
// For example, DeserializeHandle can give access to arbitrary paths on disk.
interface NativeFileSystemContext {
// Serializes a handle represented by |token|. If token is not valid this
// returns an empty array, otherwise this returns a serialization that can
// be stored to disk by for example IndexedDB, and then later read back and
// passed to DeserializeHandle to turn it back into a token.
SerializeHandle(
pending_remote<blink.mojom.NativeFileSystemTransferToken> token)
=> (array<uint8> bits);
// Deserializes a handle. |bits| should be what was returned by
// SerializeHandle earlier. The resulting |token| can be used by renderers for
// |origin| to get access to the underlying file or directory.
// If deserialization fails, |token| is not bound, and attempts to use it will
// fail.
DeserializeHandle(
url.mojom.Origin origin,
array<uint8> bits,
pending_receiver<blink.mojom.NativeFileSystemTransferToken> token);
};
......@@ -91,6 +91,7 @@ jumbo_source_set("browser") {
"//content/browser/cookie_store:cookie_store_proto",
"//content/browser/devtools:devtools_background_services_proto",
"//content/browser/devtools:protocol_sources",
"//content/browser/native_file_system:native_file_system_proto",
"//content/browser/notifications:notification_proto",
"//content/browser/payments:payment_app_proto",
"//content/browser/process_internals:mojo_bindings",
......
# Copyright 2020 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.
import("//third_party/protobuf/proto_library.gni")
proto_library("native_file_system_proto") {
sources = [ "native_file_system.proto" ]
}
// Copyright 2020 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.
syntax = "proto2";
option optimize_for = LITE_RUNTIME;
package content;
// Used to serialize a Native File System handle representing a file or
// directory on the local native file system.
//
// Paths are stored as a serialized base::FilePath. I.e. on platforms where
// base::FilePath uses 8 bit characters this directly contains those
// characters, and on platforms where base::FilePath uses 16 bit characters
// those characters are serialized using the current platform's endianness.
// This means paths can't necesarilly be read back on different platforms
// or computers from where they were written, but since the path references
// something on the local file system that likely doesn't exist on a different
// computer anyway this should be no problem in practice.
message NativeFileData {
// The root path of a Native File System handle is the path that the user
// selected in a file or directory picker. All permissions related to the
// handle are based on this path.
required bytes root_path = 1;
// If |relative_path| is empty, |root_path| is the full path to the file or
// directory this object represents. If non-empty this path should be appended
// to |root_path| to get the full path. This can happen for example if a
// website has access to a directory, but stores references to files or
// directories inside that picked directory to IndexedDB. In that case we
// still want permissions to be based on the originally picked path, but also
// need to know the path to the actual file or directory that was stored.
required bytes relative_path = 2;
}
// Used to serialize a Native File System handle representing a file or
// directory in the origin scoped sandboxed file system.
//
// The same comment as for the above NativeFileData messages applies here as
// well regarding serialization format of the paths themselves.
message SandboxedFileData {
// The path to the file or directory relative to the root of the origin's
// sandboxed file system.
required bytes virtual_path = 1;
}
// Used to serialize any Native File System handle, for example when a handle is
// stored in IndexedDB.
message NativeFileSystemHandleData {
// Is this handle representing a file or a directory?
enum HandleType {
kFile = 0;
kDirectory = 1;
}
required HandleType handle_type = 1;
// The actual data, depending on the type of file system this handle is backed
// by.
oneof data {
SandboxedFileData sandboxed = 2;
NativeFileData native = 3;
}
}
\ No newline at end of file
......@@ -10,6 +10,7 @@
#include "base/memory/weak_ptr.h"
#include "components/services/filesystem/public/mojom/types.mojom.h"
#include "content/browser/native_file_system/native_file_system_handle_base.h"
#include "content/common/content_export.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "storage/browser/file_system/file_system_url.h"
#include "third_party/blink/public/mojom/native_file_system/native_file_system_directory_handle.mojom.h"
......@@ -22,7 +23,7 @@ namespace content {
//
// This class is not thread safe, all methods must be called from the same
// sequence.
class NativeFileSystemDirectoryHandleImpl
class CONTENT_EXPORT NativeFileSystemDirectoryHandleImpl
: public NativeFileSystemHandleBase,
public blink::mojom::NativeFileSystemDirectoryHandle {
public:
......
......@@ -7,9 +7,11 @@
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/strcat.h"
#include "base/task/post_task.h"
#include "content/browser/native_file_system/file_system_chooser.h"
#include "content/browser/native_file_system/fixed_native_file_system_permission_grant.h"
#include "content/browser/native_file_system/native_file_system.pb.h"
#include "content/browser/native_file_system/native_file_system_directory_handle_impl.h"
#include "content/browser/native_file_system/native_file_system_error.h"
#include "content/browser/native_file_system/native_file_system_file_handle_impl.h"
......@@ -21,6 +23,7 @@
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_switches.h"
#include "mojo/public/cpp/bindings/callback_helpers.h"
#include "net/base/escape.h"
#include "services/network/public/cpp/is_potentially_trustworthy.h"
#include "storage/browser/file_system/file_system_context.h"
#include "storage/browser/file_system/file_system_operation_runner.h"
......@@ -148,6 +151,13 @@ void NativeFileSystemManagerImpl::BindReceiver(
receivers_.Add(this, std::move(receiver), binding_context);
}
void NativeFileSystemManagerImpl::BindInternalsReceiver(
mojo::PendingReceiver<storage::mojom::NativeFileSystemContext> receiver) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
internals_receivers_.Add(this, std::move(receiver));
}
void NativeFileSystemManagerImpl::GetSandboxedFileSystem(
GetSandboxedFileSystemCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
......@@ -263,6 +273,163 @@ void NativeFileSystemManagerImpl::GetDirectoryHandleFromToken(
std::move(directory_handle_receiver)));
}
void NativeFileSystemManagerImpl::SerializeHandle(
mojo::PendingRemote<blink::mojom::NativeFileSystemTransferToken> token,
SerializeHandleCallback callback) {
ResolveTransferToken(
std::move(token),
base::BindOnce(&NativeFileSystemManagerImpl::DidResolveForSerializeHandle,
weak_factory_.GetWeakPtr(), std::move(callback)));
}
namespace {
std::string SerializePath(const base::FilePath& path) {
auto path_bytes = base::as_bytes(base::make_span(path.value()));
return std::string(path_bytes.begin(), path_bytes.end());
}
base::FilePath DeserializePath(const std::string& bytes) {
base::FilePath::StringType s;
s.resize(bytes.size() / sizeof(base::FilePath::CharType));
std::memcpy(&s[0], bytes.data(), s.size() * sizeof(base::FilePath::CharType));
return base::FilePath(s);
}
} // namespace
void NativeFileSystemManagerImpl::DidResolveForSerializeHandle(
SerializeHandleCallback callback,
NativeFileSystemTransferTokenImpl* resolved_token) {
if (!resolved_token) {
std::move(callback).Run({});
return;
}
NativeFileSystemHandleData data;
data.set_handle_type(
resolved_token->type() ==
NativeFileSystemTransferTokenImpl::HandleType::kFile
? NativeFileSystemHandleData::kFile
: NativeFileSystemHandleData::kDirectory);
switch (resolved_token->url().type()) {
case storage::kFileSystemTypeNativeLocal: {
DCHECK_EQ(resolved_token->url().mount_type(),
storage::kFileSystemTypeIsolated);
base::FilePath root_path;
storage::IsolatedContext::GetInstance()->GetRegisteredPath(
resolved_token->shared_handle_state().file_system.id(), &root_path);
data.mutable_native()->set_root_path(SerializePath(root_path));
base::FilePath relative_path;
// We want |relative_path| to be the path of the file or directory
// relative to |root_path|. FilePath::AppendRelativePath gets us that,
// but fails if the path we're looking for is equal to the |root_path|.
// So special case that case (in which case relative path would be empty
// anyway).
if (root_path != resolved_token->url().path()) {
bool relative_path_result = root_path.AppendRelativePath(
resolved_token->url().path(), &relative_path);
DCHECK(relative_path_result);
}
data.mutable_native()->set_relative_path(SerializePath(relative_path));
break;
}
case storage::kFileSystemTypeTemporary: {
base::FilePath virtual_path = resolved_token->url().virtual_path();
data.mutable_sandboxed()->set_virtual_path(SerializePath(virtual_path));
break;
}
default:
NOTREACHED();
}
std::string value;
bool success = data.SerializeToString(&value);
DCHECK(success);
std::vector<uint8_t> result(value.begin(), value.end());
std::move(callback).Run(result);
}
void NativeFileSystemManagerImpl::DeserializeHandle(
const url::Origin& origin,
const std::vector<uint8_t>& bits,
mojo::PendingReceiver<blink::mojom::NativeFileSystemTransferToken> token) {
DCHECK(!bits.empty());
std::string bits_as_string(bits.begin(), bits.end());
NativeFileSystemHandleData data;
if (!data.ParseFromString(bits_as_string)) {
// Drop |token|, and directly return.
return;
}
switch (data.data_case()) {
case NativeFileSystemHandleData::kSandboxed: {
base::FilePath virtual_path =
DeserializePath(data.sandboxed().virtual_path());
storage::FileSystemURL url = context()->CreateCrackedFileSystemURL(
origin.GetURL(), storage::kFileSystemTypeTemporary, virtual_path);
auto permission_grant =
base::MakeRefCounted<FixedNativeFileSystemPermissionGrant>(
PermissionStatus::GRANTED);
CreateTransferTokenImpl(
url, SharedHandleState(permission_grant, permission_grant, {}),
data.handle_type() == NativeFileSystemHandleData::kDirectory,
std::move(token));
break;
}
case NativeFileSystemHandleData::kNative: {
base::FilePath root_path = DeserializePath(data.native().root_path());
base::FilePath relative_path =
DeserializePath(data.native().relative_path());
auto root = CreateFileSystemURLFromPath(origin, root_path);
storage::FileSystemURL child = context()->CreateCrackedFileSystemURL(
origin.GetURL(), root.url.mount_type(),
root.url.virtual_path().Append(relative_path));
const bool is_directory =
data.handle_type() == NativeFileSystemHandleData::kDirectory;
scoped_refptr<NativeFileSystemPermissionGrant> read_grant, write_grant;
if (permission_context_) {
const bool permission_is_directory =
is_directory || !relative_path.empty();
read_grant = permission_context_->GetReadPermissionGrant(
origin, root_path, permission_is_directory,
/*process_id=*/ChildProcessHost::kInvalidUniqueID,
/*frame_id=*/MSG_ROUTING_NONE,
NativeFileSystemPermissionContext::UserAction::kLoadFromStorage);
write_grant = permission_context_->GetWritePermissionGrant(
origin, root_path, permission_is_directory,
/*process_id=*/ChildProcessHost::kInvalidUniqueID,
/*frame_id=*/MSG_ROUTING_NONE,
NativeFileSystemPermissionContext::UserAction::kLoadFromStorage);
} else {
// Auto-deny all grants if no permission context is available,
// unless Experimental Web Platform features are enabled.
// This happens for example in content_shell.
read_grant = write_grant =
base::MakeRefCounted<FixedNativeFileSystemPermissionGrant>(
base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableExperimentalWebPlatformFeatures)
? PermissionStatus::GRANTED
: PermissionStatus::DENIED);
}
CreateTransferTokenImpl(
child, SharedHandleState(read_grant, write_grant, root.file_system),
is_directory, std::move(token));
break;
}
case NativeFileSystemHandleData::DATA_NOT_SET:
NOTREACHED();
}
}
blink::mojom::NativeFileSystemEntryPtr
NativeFileSystemManagerImpl::CreateFileEntryFromPath(
const BindingContext& binding_context,
......
......@@ -8,6 +8,7 @@
#include "base/files/file_path.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/sequence_bound.h"
#include "components/services/storage/public/mojom/native_file_system_context.mojom.h"
#include "content/browser/blob_storage/chrome_blob_storage_context.h"
#include "content/browser/native_file_system/file_system_chooser.h"
#include "content/common/content_export.h"
......@@ -48,7 +49,8 @@ class StoragePartitionImpl;
// thread only.
class CONTENT_EXPORT NativeFileSystemManagerImpl
: public NativeFileSystemEntryFactory,
public blink::mojom::NativeFileSystemManager {
public blink::mojom::NativeFileSystemManager,
public storage::mojom::NativeFileSystemContext {
public:
using BindingContext = NativeFileSystemEntryFactory::BindingContext;
......@@ -85,6 +87,9 @@ class CONTENT_EXPORT NativeFileSystemManagerImpl
const BindingContext& binding_context,
mojo::PendingReceiver<blink::mojom::NativeFileSystemManager> receiver);
void BindInternalsReceiver(
mojo::PendingReceiver<storage::mojom::NativeFileSystemContext> receiver);
// blink::mojom::NativeFileSystemManager:
void GetSandboxedFileSystem(GetSandboxedFileSystemCallback callback) override;
void ChooseEntries(
......@@ -101,6 +106,16 @@ class CONTENT_EXPORT NativeFileSystemManagerImpl
mojo::PendingReceiver<blink::mojom::NativeFileSystemDirectoryHandle>
directory_handle_receiver) override;
// storage::mojom::NativeFileSystemContext:
void SerializeHandle(
mojo::PendingRemote<blink::mojom::NativeFileSystemTransferToken> token,
SerializeHandleCallback callback) override;
void DeserializeHandle(
const url::Origin& origin,
const std::vector<uint8_t>& bits,
mojo::PendingReceiver<blink::mojom::NativeFileSystemTransferToken> token)
override;
// NativeFileSystemEntryFactory:
blink::mojom::NativeFileSystemEntryPtr CreateFileEntryFromPath(
const BindingContext& binding_context,
......@@ -233,6 +248,10 @@ class CONTENT_EXPORT NativeFileSystemManagerImpl
ResolvedTokenCallback callback,
const base::UnguessableToken& token);
void DidResolveForSerializeHandle(
SerializeHandleCallback callback,
NativeFileSystemTransferTokenImpl* resolved_token);
// Creates a FileSystemURL which corresponds to a FilePath and Origin.
struct FileSystemURLAndFSHandle {
storage::FileSystemURL url;
......@@ -262,6 +281,9 @@ class CONTENT_EXPORT NativeFileSystemManagerImpl
mojo::ReceiverSet<blink::mojom::NativeFileSystemManager, BindingContext>
receivers_;
mojo::ReceiverSet<storage::mojom::NativeFileSystemContext>
internals_receivers_;
// All the receivers for file and directory handles that have references to
// them.
mojo::UniqueReceiverSet<blink::mojom::NativeFileSystemFileHandle>
......
......@@ -6,6 +6,7 @@
#define CONTENT_BROWSER_NATIVE_FILE_SYSTEM_NATIVE_FILE_SYSTEM_TRANSFER_TOKEN_IMPL_H_
#include "content/browser/native_file_system/native_file_system_manager_impl.h"
#include "content/common/content_export.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "storage/browser/file_system/file_system_url.h"
......@@ -19,7 +20,7 @@ namespace content {
//
// Instances of this class are immutable, but since this implements a mojo
// interface all its methods are called on the same sequence anyway.
class NativeFileSystemTransferTokenImpl
class CONTENT_EXPORT NativeFileSystemTransferTokenImpl
: public blink::mojom::NativeFileSystemTransferToken {
public:
using SharedHandleState = NativeFileSystemManagerImpl::SharedHandleState;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment