0

dlp: Pass file access callback to file stream reader

The callback is passed through the calls from the mojo interface to the file stream reader. Where it is used to gain a ScopedFileAccess token to read files.

The callback is not yet created for the calls depending on the destination URL. That will happen with a CL for b/262203074.

There are different cases for default behavior, where we do not need to query the daemon with a specific URL. Only files explicitly known/downloaded to the user can be protected. That excludes e.g. caches. The daemon would not deny those accesses, so we do not have to ask for permission. The other default are cases, where we want to allow access regardless of the rules. Here we have to query the daemon with a request for the system component (e.g. by the callback returned by ScopedFileAccessDelegate::GetCallbackForSystemIO).

Design doc: go/access-to-dlp-restricted-files-upload

BUG=b:262199707

Change-Id: Id71ac3463aae4c0d0b02bb3a264c2a8d05798f22
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4197601
Reviewed-by: Sergey Poromov <poromov@chromium.org>
Reviewed-by: Austin Sullivan <asully@chromium.org>
Commit-Queue: Daniel Brinkers <brinky@google.com>
Reviewed-by: Marcello Salomao <msalomao@google.com>
Reviewed-by: Marijn Kruisselbrink <mek@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1114995}
This commit is contained in:
Daniel Brinkers
2023-03-09 07:39:05 +00:00
committed by Chromium LUCI CQ
parent 9df0c47961
commit 9dacf9e173
42 changed files with 509 additions and 97 deletions

@ -12,6 +12,7 @@
#include "ash/webui/file_manager/url_constants.h"
#include "base/check_op.h"
#include "base/command_line.h"
#include "base/functional/callback_helpers.h"
#include "base/notreached.h"
#include "base/strings/escape.h"
#include "base/task/task_traits.h"
@ -24,6 +25,7 @@
#include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h"
#include "chrome/common/url_constants.h"
#include "chromeos/ash/components/dbus/cros_disks/cros_disks_client.h"
#include "components/file_access/scoped_file_access_delegate.h"
#include "components/user_manager/user.h"
#include "storage/browser/file_system/async_file_util.h"
#include "storage/browser/file_system/external_mount_points.h"
@ -421,7 +423,9 @@ FileSystemBackend::CreateFileStreamReader(
int64_t offset,
int64_t max_bytes_to_read,
const base::Time& expected_modification_time,
storage::FileSystemContext* context) const {
storage::FileSystemContext* context,
file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
file_access) const {
DCHECK(url.is_valid());
if (!IsAccessAllowed(url))
@ -431,8 +435,15 @@ FileSystemBackend::CreateFileStreamReader(
case storage::kFileSystemTypeProvided:
return file_system_provider_delegate_->CreateFileStreamReader(
url, offset, max_bytes_to_read, expected_modification_time, context);
// The dlp file_access callback is needed for the local filesystem only.
case storage::kFileSystemTypeLocal:
case storage::kFileSystemTypeRestrictedLocal:
return storage::FileStreamReader::CreateForLocalFile(
base::ThreadPool::CreateTaskRunner(
{base::MayBlock(), base::TaskPriority::USER_VISIBLE})
.get(),
url.path(), offset, expected_modification_time,
std::move(file_access));
case storage::kFileSystemTypeDriveFs:
case storage::kFileSystemTypeSmbFs:
case storage::kFileSystemTypeFuseBox:

@ -14,6 +14,7 @@
#include "base/files/file_path.h"
#include "base/memory/ref_counted.h"
#include "components/account_id/account_id.h"
#include "components/file_access/scoped_file_access_delegate.h"
#include "storage/browser/file_system/file_system_backend.h"
#include "storage/browser/file_system/task_runner_bound_observer_list.h"
#include "storage/common/file_system/file_system_types.h"
@ -125,7 +126,9 @@ class FileSystemBackend : public storage::ExternalFileSystemBackend {
int64_t offset,
int64_t max_bytes_to_read,
const base::Time& expected_modification_time,
storage::FileSystemContext* context) const override;
storage::FileSystemContext* context,
file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
file_access) const override;
std::unique_ptr<storage::FileStreamWriter> CreateFileStreamWriter(
const storage::FileSystemURL& url,
int64_t offset,

@ -4,12 +4,16 @@
#include "chrome/browser/chromeos/policy/dlp/dlp_scoped_file_access_delegate.h"
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/functional/callback_forward.h"
#include "base/process/process_handle.h"
#include "base/task/bind_post_task.h"
#include "chrome/browser/chromeos/policy/dlp/dlp_file_access_copy_or_move_delegate_factory.h"
#include "chromeos/dbus/dlp/dlp_client.h"
#include "components/file_access/scoped_file_access.h"
#include "components/file_access/scoped_file_access_copy.h"
#include "components/file_access/scoped_file_access_delegate.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
@ -56,7 +60,7 @@ void DlpScopedFileAccessDelegate::Initialize(chromeos::DlpClient* client) {
if (!request_files_access_for_system_io_callback_) {
request_files_access_for_system_io_callback_ =
new file_access::ScopedFileAccessDelegate::
RequestFilesAccessForSystemIOCallback(base::BindPostTask(
RequestFilesAccessIOCallback(base::BindPostTask(
content::GetUIThreadTaskRunner({}),
base::BindRepeating(&RequestFileAccessForSystem)));
}
@ -115,6 +119,29 @@ void DlpScopedFileAccessDelegate::RequestFilesAccessForSystem(
PostRequestFileAccessToDaemon(request, std::move(callback));
}
DlpScopedFileAccessDelegate::RequestFilesAccessIOCallback
DlpScopedFileAccessDelegate::CreateFileAccessCallback(
const GURL& destination) const {
return base::BindPostTask(
content::GetUIThreadTaskRunner({}),
base::BindRepeating(
[](const GURL& destination, const std::vector<base::FilePath>& files,
base::OnceCallback<void(file_access::ScopedFileAccess)> callback) {
if (file_access::ScopedFileAccessDelegate::HasInstance()) {
file_access::ScopedFileAccessDelegate::Get()->RequestFilesAccess(
files, destination,
base::BindPostTask(content::GetIOThreadTaskRunner({}),
std::move(callback)));
} else {
content::GetIOThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback),
file_access::ScopedFileAccess::Allowed()));
}
},
destination));
}
void DlpScopedFileAccessDelegate::PostRequestFileAccessToDaemon(
const ::dlp::RequestFileAccessRequest request,
base::OnceCallback<void(file_access::ScopedFileAccess)> callback) {

@ -44,6 +44,8 @@ class DlpScopedFileAccessDelegate
const std::vector<base::FilePath>& files,
base::OnceCallback<void(file_access::ScopedFileAccess)> callback)
override;
RequestFilesAccessIOCallback CreateFileAccessCallback(
const GURL& destination) const override;
protected:
explicit DlpScopedFileAccessDelegate(chromeos::DlpClient* client);

@ -36,7 +36,8 @@ class DlpScopedFileAccessDelegateTest : public testing::Test {
protected:
content::BrowserTaskEnvironment task_environment_;
chromeos::FakeDlpClient fake_dlp_client_;
DlpScopedFileAccessDelegate delegate_{&fake_dlp_client_};
std::unique_ptr<DlpScopedFileAccessDelegate> delegate_{
new DlpScopedFileAccessDelegate(&fake_dlp_client_)};
};
TEST_F(DlpScopedFileAccessDelegateTest, TestNoSingleton) {
@ -44,14 +45,14 @@ TEST_F(DlpScopedFileAccessDelegateTest, TestNoSingleton) {
base::CreateTemporaryFile(&file_path);
base::test::TestFuture<file_access::ScopedFileAccess> future1;
delegate_.RequestFilesAccess({file_path}, GURL("example.com"),
future1.GetCallback());
delegate_->RequestFilesAccess({file_path}, GURL("example.com"),
future1.GetCallback());
EXPECT_TRUE(future1.Get<0>().is_allowed());
fake_dlp_client_.SetFileAccessAllowed(false);
base::test::TestFuture<file_access::ScopedFileAccess> future2;
delegate_.RequestFilesAccess({file_path}, GURL("example.com"),
future2.GetCallback());
delegate_->RequestFilesAccess({file_path}, GURL("example.com"),
future2.GetCallback());
EXPECT_FALSE(future2.Get<0>().is_allowed());
}
@ -87,6 +88,84 @@ TEST_F(DlpScopedFileAccessDelegateTest,
EXPECT_TRUE(future1.Get<0>().is_allowed());
}
TEST_F(DlpScopedFileAccessDelegateTest, CreateFileAccessCallbackAllowTest) {
base::FilePath file_path;
base::CreateTemporaryFile(&file_path);
DlpScopedFileAccessDelegate::Initialize(&fake_dlp_client_);
fake_dlp_client_.SetFileAccessAllowed(true);
base::test::TestFuture<file_access::ScopedFileAccess> future;
auto* delegate = file_access::ScopedFileAccessDelegate::Get();
auto cb = delegate->CreateFileAccessCallback(GURL("https://google.com"));
cb.Run({file_path}, future.GetCallback());
EXPECT_TRUE(future.Get<0>().is_allowed());
}
TEST_F(DlpScopedFileAccessDelegateTest, CreateFileAccessCallbackDenyTest) {
base::FilePath file_path;
base::CreateTemporaryFile(&file_path);
DlpScopedFileAccessDelegate::Initialize(&fake_dlp_client_);
fake_dlp_client_.SetFileAccessAllowed(false);
base::test::TestFuture<file_access::ScopedFileAccess> future;
auto* delegate = file_access::ScopedFileAccessDelegate::Get();
auto cb = delegate->CreateFileAccessCallback(GURL("https://google.com"));
cb.Run({file_path}, future.GetCallback());
EXPECT_FALSE(future.Get<0>().is_allowed());
}
TEST_F(DlpScopedFileAccessDelegateTest,
CreateFileAccessCallbackLostInstanceTest) {
base::FilePath file_path;
base::CreateTemporaryFile(&file_path);
DlpScopedFileAccessDelegate::Initialize(&fake_dlp_client_);
fake_dlp_client_.SetFileAccessAllowed(false);
base::test::TestFuture<file_access::ScopedFileAccess> future;
auto* delegate = file_access::ScopedFileAccessDelegate::Get();
auto cb = delegate->CreateFileAccessCallback(GURL("https://google.com"));
delegate_.reset();
cb.Run({file_path}, future.GetCallback());
EXPECT_TRUE(future.Get<0>().is_allowed());
}
TEST_F(DlpScopedFileAccessDelegateTest, GetCallbackSystemTest) {
base::FilePath file_path;
base::CreateTemporaryFile(&file_path);
DlpScopedFileAccessDelegate::Initialize(&fake_dlp_client_);
// Post a task on IO thread to sync with to be sure the IO task setting
// `request_files_access_for_system_io_callback_` has run.
base::RunLoop init;
auto io_thread = content::GetIOThreadTaskRunner({});
io_thread->PostTask(
FROM_HERE, base::BindOnce(&base::RunLoop::Quit, base::Unretained(&init)));
init.Run();
base::test::TestFuture<file_access::ScopedFileAccess> future;
auto* delegate = file_access::ScopedFileAccessDelegate::Get();
auto cb = delegate->GetCallbackForSystem();
EXPECT_TRUE(cb);
cb.Run({file_path}, future.GetCallback());
EXPECT_TRUE(future.Get<0>().is_allowed());
}
TEST_F(DlpScopedFileAccessDelegateTest, GetCallbackSystemNoSingeltonTest) {
base::FilePath file_path;
base::CreateTemporaryFile(&file_path);
base::test::TestFuture<file_access::ScopedFileAccess> future;
auto* delegate = file_access::ScopedFileAccessDelegate::Get();
auto cb = delegate->GetCallbackForSystem();
EXPECT_TRUE(cb);
cb.Run({file_path}, future.GetCallback());
EXPECT_TRUE(future.Get<0>().is_allowed());
}
TEST_F(DlpScopedFileAccessDelegateTest, TestMultipleInstances) {
DlpScopedFileAccessDelegate::Initialize(nullptr);
EXPECT_NO_FATAL_FAILURE(DlpScopedFileAccessDelegate::Initialize(nullptr));
@ -209,7 +288,6 @@ TEST_F(DlpScopedFileAccessDelegateTaskTest,
std::move(callback));
}),
false /* = restore_original_callback*/);
// The request for file access should be granted as that is the default
// behaviour for no running dlp (no rules).
io_thread_->PostTask(

@ -316,7 +316,9 @@ MediaFileSystemBackend::CreateFileStreamReader(
int64_t offset,
int64_t max_bytes_to_read,
const base::Time& expected_modification_time,
FileSystemContext* context) const {
FileSystemContext* context,
file_access::ScopedFileAccessDelegate::
RequestFilesAccessIOCallback /*file_access*/) const {
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS_ASH)
if (url.type() == storage::kFileSystemTypeDeviceMedia) {
std::unique_ptr<storage::FileStreamReader> reader =

@ -17,6 +17,7 @@
#include "build/chromeos_buildflags.h"
#include "chrome/browser/media_galleries/media_galleries_preferences.h"
#include "components/download/public/common/quarantine_connection.h"
#include "components/file_access/scoped_file_access_delegate.h"
#include "storage/browser/file_system/file_system_backend.h"
#include "storage/browser/file_system/file_system_request_info.h"
#include "storage/browser/file_system/task_runner_bound_observer_list.h"
@ -85,7 +86,9 @@ class MediaFileSystemBackend : public storage::FileSystemBackend {
int64_t offset,
int64_t max_bytes_to_read,
const base::Time& expected_modification_time,
storage::FileSystemContext* context) const override;
storage::FileSystemContext* context,
file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
file_access) const override;
std::unique_ptr<storage::FileStreamWriter> CreateFileStreamWriter(
const storage::FileSystemURL& url,
int64_t offset,

@ -163,7 +163,9 @@ SyncFileSystemBackend::CreateFileStreamReader(
int64_t offset,
int64_t max_bytes_to_read,
const base::Time& expected_modification_time,
storage::FileSystemContext* context) const {
storage::FileSystemContext* context,
file_access::ScopedFileAccessDelegate::
RequestFilesAccessIOCallback /*file_access*/) const {
DCHECK(CanHandleType(url.type()));
return GetDelegate()->CreateFileStreamReader(
url, offset, expected_modification_time, context);

@ -12,6 +12,7 @@
#include "base/memory/raw_ptr.h"
#include "chrome/browser/sync_file_system/sync_callbacks.h"
#include "chrome/browser/sync_file_system/sync_status_code.h"
#include "components/file_access/scoped_file_access_delegate.h"
#include "storage/browser/file_system/file_system_backend.h"
#include "storage/browser/file_system/file_system_quota_util.h"
#include "storage/browser/file_system/sandbox_file_system_backend_delegate.h"
@ -60,7 +61,9 @@ class SyncFileSystemBackend : public storage::FileSystemBackend {
int64_t offset,
int64_t max_bytes_to_read,
const base::Time& expected_modification_time,
storage::FileSystemContext* context) const override;
storage::FileSystemContext* context,
file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
file_access) const override;
std::unique_ptr<storage::FileStreamWriter> CreateFileStreamWriter(
const storage::FileSystemURL& url,
int64_t offset,

@ -3,6 +3,9 @@
// found in the LICENSE file.
#include "components/file_access/scoped_file_access_delegate.h"
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "components/file_access/scoped_file_access.h"
namespace file_access {
// static
@ -35,6 +38,21 @@ void ScopedFileAccessDelegate::RequestFilesAccessForSystemIO(
}
}
// static
ScopedFileAccessDelegate::RequestFilesAccessIOCallback
ScopedFileAccessDelegate::GetCallbackForSystem() {
return base::BindRepeating(
[](const std::vector<base::FilePath>& file_paths,
base::OnceCallback<void(ScopedFileAccess)> callback) {
if (request_files_access_for_system_io_callback_) {
request_files_access_for_system_io_callback_->Run(
file_paths, std::move(callback));
} else {
std::move(callback).Run(ScopedFileAccess::Allowed());
}
});
}
ScopedFileAccessDelegate::ScopedFileAccessDelegate() {
if (scoped_file_access_delegate_) {
delete scoped_file_access_delegate_;
@ -53,18 +71,18 @@ ScopedFileAccessDelegate*
ScopedFileAccessDelegate::scoped_file_access_delegate_ = nullptr;
// static
ScopedFileAccessDelegate::RequestFilesAccessForSystemIOCallback*
ScopedFileAccessDelegate::RequestFilesAccessIOCallback*
ScopedFileAccessDelegate::request_files_access_for_system_io_callback_ =
nullptr;
ScopedFileAccessDelegate::ScopedRequestFilesAccessCallbackForTesting::
ScopedRequestFilesAccessCallbackForTesting(
RequestFilesAccessForSystemIOCallback callback,
RequestFilesAccessIOCallback callback,
bool restore_original_callback)
: restore_original_callback_(restore_original_callback) {
original_callback_ = request_files_access_for_system_io_callback_;
request_files_access_for_system_io_callback_ =
new RequestFilesAccessForSystemIOCallback(std::move(callback));
new RequestFilesAccessIOCallback(std::move(callback));
}
ScopedFileAccessDelegate::ScopedRequestFilesAccessCallbackForTesting::

@ -29,7 +29,7 @@ namespace file_access {
// to packages without direct access to the UI thread.
class COMPONENT_EXPORT(FILE_ACCESS) ScopedFileAccessDelegate {
public:
using RequestFilesAccessForSystemIOCallback =
using RequestFilesAccessIOCallback =
base::RepeatingCallback<void(const std::vector<base::FilePath>&,
base::OnceCallback<void(ScopedFileAccess)>)>;
@ -62,6 +62,12 @@ class COMPONENT_EXPORT(FILE_ACCESS) ScopedFileAccessDelegate {
const std::vector<base::FilePath>& files,
base::OnceCallback<void(file_access::ScopedFileAccess)> callback) = 0;
// Creates a callback to gain file access for the given `destination`. The
// callback should be called on the IO thread. The method itself from the UI
// thread.
virtual RequestFilesAccessIOCallback CreateFileAccessCallback(
const GURL& destination) const = 0;
// Called from the IO thread. Switches to the UI thread and calls
// RequestFilesAccessForSystem there. The `callback` is run on the IO thread
// again.
@ -105,7 +111,7 @@ class COMPONENT_EXPORT(FILE_ACCESS) ScopedFileAccessDelegate {
// Otherwise, it destroys the original callback when this class is
// destroyed.
explicit ScopedRequestFilesAccessCallbackForTesting(
RequestFilesAccessForSystemIOCallback callback,
RequestFilesAccessIOCallback callback,
bool restore_original_callback = true);
virtual ~ScopedRequestFilesAccessCallbackForTesting();
@ -121,8 +127,12 @@ class COMPONENT_EXPORT(FILE_ACCESS) ScopedFileAccessDelegate {
private:
bool restore_original_callback_;
RequestFilesAccessForSystemIOCallback* original_callback_ = nullptr;
RequestFilesAccessIOCallback* original_callback_ = nullptr;
};
// Get a callback to get file access to files for system component
// destination. Can be called from IO or UI thread. The callback should be
// called on IO thread only.
static RequestFilesAccessIOCallback GetCallbackForSystem();
protected:
ScopedFileAccessDelegate();
@ -136,7 +146,7 @@ class COMPONENT_EXPORT(FILE_ACCESS) ScopedFileAccessDelegate {
// A single instance for a callback living on the IO thread which switches to
// the UI thread to call RequestFilesAccessForSystem from there and switch
// back to IO thread handing the ScopedFileAccess to another (given) callback.
static RequestFilesAccessForSystemIOCallback*
static RequestFilesAccessIOCallback*
request_files_access_for_system_io_callback_;
};

@ -8,6 +8,7 @@
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/functional/callback_forward.h"
#include "base/functional/callback_helpers.h"
#include "base/location.h"
#include "base/test/mock_callback.h"
#include "base/test/task_environment.h"
@ -30,6 +31,10 @@ class ScopedFileAccessDelegateTestInstance : public ScopedFileAccessDelegate {
void RequestFilesAccessForSystem(
const std::vector<base::FilePath>& files,
base::OnceCallback<void(ScopedFileAccess)> callback) override {}
RequestFilesAccessIOCallback CreateFileAccessCallback(
const GURL& destination) const override {
return base::DoNothing();
}
};
int ScopedFileAccessDelegateTestInstance::instance_counter = 0;

@ -25,6 +25,10 @@ class MockScopedFileAccessDelegate : public ScopedFileAccessDelegate {
(const std::vector<base::FilePath>&,
base::OnceCallback<void(ScopedFileAccess)>),
(override));
MOCK_METHOD((RequestFilesAccessIOCallback),
CreateFileAccessCallback,
(const GURL& destination),
(const override));
};
} // namespace file_access

@ -6,6 +6,7 @@
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "content/browser/blob_storage/chrome_blob_storage_context.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "content/public/browser/browser_task_traits.h"
@ -31,6 +32,11 @@ class BindingDelegate : public storage::BlobRegistryImpl::Delegate {
bool CanAccessDataForOrigin(const url::Origin& origin) override {
return security_policy_handle_.CanAccessDataForOrigin(origin);
}
file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
GetAccessCallback() override {
// TODO (b/262203074) create actual callback
return base::NullCallback();
}
private:
ChildProcessSecurityPolicyImpl::Handle security_policy_handle_;

@ -12,11 +12,13 @@
#include "base/files/file_enumerator.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/guid.h"
#include "base/supports_user_data.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/task_runner.h"
#include "base/task/thread_pool.h"
#include "components/file_access/scoped_file_access_delegate.h"
#include "content/browser/storage_partition_impl.h"
#include "content/public/browser/blob_handle.h"
#include "content/public/browser/browser_context.h"
@ -214,14 +216,16 @@ std::unique_ptr<BlobHandle> ChromeBlobStorageContext::CreateMemoryBackedBlob(
return blob_handle;
}
void ChromeBlobStorageContext::CreateFileSystemBlob(
void ChromeBlobStorageContext::CreateFileSystemBlobWithFileAccess(
scoped_refptr<storage::FileSystemContext> file_system_context,
mojo::PendingReceiver<blink::mojom::Blob> blob_receiver,
const storage::FileSystemURL& url,
const std::string& blob_uuid,
const std::string& content_type,
const uint64_t file_size,
const base::Time& file_modification_time) {
const base::Time& file_modification_time,
file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
file_access) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
auto blob_builder = std::make_unique<storage::BlobDataBuilder>(blob_uuid);
@ -229,9 +233,9 @@ void ChromeBlobStorageContext::CreateFileSystemBlob(
// Use AppendFileSystemFile here, since we're streaming the file directly
// from the file system backend, and the file thus might not actually be
// backed by a file on disk.
blob_builder->AppendFileSystemFile(url, 0, file_size,
file_modification_time,
std::move(file_system_context));
blob_builder->AppendFileSystemFile(
url, 0, file_size, file_modification_time,
std::move(file_system_context), std::move(file_access));
}
blob_builder->set_content_type(content_type);
@ -245,6 +249,19 @@ void ChromeBlobStorageContext::CreateFileSystemBlob(
storage::BlobImpl::Create(std::move(blob_handle), std::move(blob_receiver));
}
void ChromeBlobStorageContext::CreateFileSystemBlob(
scoped_refptr<storage::FileSystemContext> file_system_context,
mojo::PendingReceiver<blink::mojom::Blob> blob_receiver,
const storage::FileSystemURL& url,
const std::string& blob_uuid,
const std::string& content_type,
const uint64_t file_size,
const base::Time& file_modification_time) {
CreateFileSystemBlobWithFileAccess(
file_system_context, std::move(blob_receiver), url, blob_uuid,
content_type, file_size, file_modification_time, base::NullCallback());
}
// static
scoped_refptr<network::SharedURLLoaderFactory>
ChromeBlobStorageContext::URLLoaderFactoryForToken(

@ -12,10 +12,12 @@
#include <vector>
#include "base/files/file_path.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_refptr.h"
#include "base/task/sequenced_task_runner_helpers.h"
#include "base/time/time.h"
#include "components/file_access/scoped_file_access_delegate.h"
#include "content/common/content_export.h"
#include "content/public/browser/browser_thread.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
@ -78,6 +80,23 @@ class CONTENT_EXPORT ChromeBlobStorageContext
base::span<const uint8_t> data,
const std::string& content_type);
// Creates a FileSystem File blob accessible by the renderer via the blob
// remote corresponding to `blob_receiver`. The callback can grant or deny the
// read access to the file. The callback `file_access` is used to grant or
// deny access to files under dlp restrictions. Leaving it at NullCallback
// will lead to default behaviour, which currently is granting it (until
// b/265908846 is done).
void CreateFileSystemBlobWithFileAccess(
scoped_refptr<storage::FileSystemContext> file_system_context,
mojo::PendingReceiver<blink::mojom::Blob> blob_receiver,
const storage::FileSystemURL& url,
const std::string& blob_uuid,
const std::string& content_type,
const uint64_t file_size,
const base::Time& file_modification_time,
file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
file_access);
// Creates a FileSystem File blob accessible by the renderer via the blob
// remote corresponding to `blob_receiver`.
void CreateFileSystemBlob(

@ -5,6 +5,7 @@ import("//testing/test.gni")
test("storage_unittests") {
deps = [
"//components/file_access:test_support",
"//storage/browser:unittests",
"//storage/common:unittests",
]

@ -351,6 +351,7 @@ source_set("unittests") {
"//base/test:test_support",
"//build:chromeos_buildflags",
"//components/file_access:file_access",
"//components/file_access:test_support",
"//components/services/filesystem/public/mojom",
"//components/services/storage/public/cpp",
"//mojo/public/cpp/system",
@ -420,6 +421,7 @@ static_library("test_support") {
":browser",
"//base/test:test_support",
"//build:chromeos_buildflags",
"//components/file_access:file_access",
"//components/services/storage/public/cpp",
"//components/services/storage/public/mojom",
"//net:test_support",

@ -17,6 +17,7 @@
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/time/time.h"
#include "components/file_access/scoped_file_access_delegate.h"
#include "storage/browser/blob/blob_entry.h"
#include "storage/browser/blob/blob_storage_registry.h"
#include "storage/browser/blob/shareable_blob_data_item.h"
@ -146,13 +147,16 @@ BlobDataBuilder::FutureFile BlobDataBuilder::AppendFutureFile(
return FutureFile(std::move(item));
}
void BlobDataBuilder::AppendFile(const FilePath& file_path,
uint64_t offset,
uint64_t length,
const base::Time& expected_modification_time) {
auto item = BlobDataItem::CreateFile(file_path, offset, length,
expected_modification_time,
ShareableFileReference::Get(file_path));
void BlobDataBuilder::AppendFile(
const FilePath& file_path,
uint64_t offset,
uint64_t length,
const base::Time& expected_modification_time,
file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
file_access) {
auto item = BlobDataItem::CreateFile(
file_path, offset, length, expected_modification_time,
ShareableFileReference::Get(file_path), std::move(file_access));
DCHECK(!item->IsFutureFileItem()) << file_path.value();
auto shareable_item = base::MakeRefCounted<ShareableBlobDataItem>(
@ -277,7 +281,8 @@ void BlobDataBuilder::SliceBlob(const BlobEntry* source,
case BlobDataItem::Type::kFile: {
data_item = BlobDataItem::CreateFile(
source_item->path(), source_item->offset() + item_offset, read_size,
source_item->expected_modification_time(), source_item->file_ref_);
source_item->expected_modification_time(), source_item->file_ref_,
source_item->file_access_);
if (source_item->IsFutureFileItem()) {
// The source file isn't a real file yet (path is fake), so store the
@ -290,7 +295,7 @@ void BlobDataBuilder::SliceBlob(const BlobEntry* source,
data_item = BlobDataItem::CreateFileFilesystem(
source_item->filesystem_url(), source_item->offset() + item_offset,
read_size, source_item->expected_modification_time(),
source_item->file_system_context());
source_item->file_system_context(), source_item->file_access_);
break;
}
case BlobDataItem::Type::kReadableDataHandle: {
@ -320,11 +325,13 @@ void BlobDataBuilder::AppendFileSystemFile(
uint64_t offset,
uint64_t length,
const base::Time& expected_modification_time,
scoped_refptr<FileSystemContext> file_system_context) {
scoped_refptr<FileSystemContext> file_system_context,
file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
file_access) {
DCHECK_GT(length, 0ul);
auto item = BlobDataItem::CreateFileFilesystem(
url, offset, length, expected_modification_time,
std::move(file_system_context));
std::move(file_system_context), std::move(file_access));
auto shareable_item = base::MakeRefCounted<ShareableBlobDataItem>(
std::move(item), ShareableBlobDataItem::POPULATED_WITHOUT_QUOTA);

@ -13,8 +13,10 @@
#include "base/component_export.h"
#include "base/files/file_path.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/scoped_refptr.h"
#include "base/numerics/checked_math.h"
#include "components/file_access/scoped_file_access_delegate.h"
#include "components/services/storage/public/mojom/blob_storage_context.mojom.h"
#include "storage/browser/blob/blob_data_item.h"
#include "storage/browser/blob/blob_data_snapshot.h"
@ -132,11 +134,17 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) BlobDataBuilder {
// You must know the length of the file, you cannot use kuint64max to specify
// the whole file. This method creates a ShareableFileReference to the given
// file, which is stored in this builder.
void AppendFile(const base::FilePath& file_path,
uint64_t offset,
uint64_t length,
const base::Time& expected_modification_time);
// file, which is stored in this builder. The callback `file_access` is used
// to grant or deny access to files under dlp restrictions. Leaving it at
// NullCallback will lead to default behaviour, which currently is granting it
// (until b/265908846 is done).
void AppendFile(
const base::FilePath& file_path,
uint64_t offset,
uint64_t length,
const base::Time& expected_modification_time,
file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
file_access = base::NullCallback());
void AppendBlob(const std::string& uuid,
uint64_t offset,
@ -150,7 +158,9 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) BlobDataBuilder {
uint64_t offset,
uint64_t length,
const base::Time& expected_modification_time,
scoped_refptr<FileSystemContext> file_system_context);
scoped_refptr<FileSystemContext> file_system_context,
file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
file_access = base::NullCallback());
void AppendReadableDataHandle(scoped_refptr<DataHandle> data_handle) {
auto length = data_handle->GetSize();

@ -10,6 +10,8 @@
#include "base/ranges/algorithm.h"
#include "base/strings/string_number_conversions.h"
#include "base/time/time.h"
#include "components/file_access/scoped_file_access_delegate.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "net/base/net_errors.h"
#include "services/network/public/cpp/data_pipe_to_source_stream.h"
@ -80,8 +82,12 @@ scoped_refptr<BlobDataItem> BlobDataItem::CreateBytesDescription(
}
// static
scoped_refptr<BlobDataItem> BlobDataItem::CreateFile(base::FilePath path) {
return CreateFile(path, 0, blink::BlobUtils::kUnknownSize);
scoped_refptr<BlobDataItem> BlobDataItem::CreateFile(
base::FilePath path,
file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
file_access) {
return CreateFile(path, 0, blink::BlobUtils::kUnknownSize, base::Time(),
nullptr, std::move(file_access));
}
// static
@ -90,12 +96,15 @@ scoped_refptr<BlobDataItem> BlobDataItem::CreateFile(
uint64_t offset,
uint64_t length,
base::Time expected_modification_time,
scoped_refptr<ShareableFileReference> file_ref) {
scoped_refptr<ShareableFileReference> file_ref,
file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
file_access) {
auto item =
base::WrapRefCounted(new BlobDataItem(Type::kFile, offset, length));
item->path_ = std::move(path);
item->expected_modification_time_ = std::move(expected_modification_time);
item->file_ref_ = std::move(file_ref);
item->file_access_ = std::move(file_access);
// TODO(mek): DCHECK(!item->IsFutureFileItem()) when BlobDataBuilder has some
// other way of slicing a future file.
return item;
@ -120,12 +129,15 @@ scoped_refptr<BlobDataItem> BlobDataItem::CreateFileFilesystem(
uint64_t offset,
uint64_t length,
base::Time expected_modification_time,
scoped_refptr<FileSystemContext> file_system_context) {
scoped_refptr<FileSystemContext> file_system_context,
file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
file_access) {
auto item = base::WrapRefCounted(
new BlobDataItem(Type::kFileFilesystem, offset, length));
item->filesystem_url_ = url;
item->expected_modification_time_ = std::move(expected_modification_time);
item->file_system_context_ = std::move(file_system_context);
item->file_access_ = std::move(file_access);
return item;
}

@ -14,8 +14,11 @@
#include "base/component_export.h"
#include "base/containers/span.h"
#include "base/files/file_path.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/ref_counted.h"
#include "base/time/time.h"
#include "components/file_access/scoped_file_access.h"
#include "components/file_access/scoped_file_access_delegate.h"
#include "components/services/storage/public/mojom/blob_storage_context.mojom.h"
#include "net/base/io_buffer.h"
#include "storage/browser/blob/shareable_file_reference.h"
@ -83,13 +86,18 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) BlobDataItem
static scoped_refptr<BlobDataItem> CreateBytes(
base::span<const uint8_t> bytes);
static scoped_refptr<BlobDataItem> CreateBytesDescription(size_t length);
static scoped_refptr<BlobDataItem> CreateFile(base::FilePath path);
static scoped_refptr<BlobDataItem> CreateFile(
base::FilePath path,
file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
file_access = base::NullCallback());
static scoped_refptr<BlobDataItem> CreateFile(
base::FilePath path,
uint64_t offset,
uint64_t length,
base::Time expected_modification_time = base::Time(),
scoped_refptr<ShareableFileReference> file_ref = nullptr);
scoped_refptr<ShareableFileReference> file_ref = nullptr,
file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
file_access = base::NullCallback());
static scoped_refptr<BlobDataItem> CreateFutureFile(uint64_t offset,
uint64_t length,
uint64_t file_id);
@ -98,7 +106,9 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) BlobDataItem
uint64_t offset,
uint64_t length,
base::Time expected_modification_time,
scoped_refptr<FileSystemContext> file_system_context);
scoped_refptr<FileSystemContext> file_system_context,
file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
file_access = base::NullCallback());
static scoped_refptr<BlobDataItem> CreateReadableDataHandle(
scoped_refptr<DataHandle> data_handle,
uint64_t offset,
@ -146,6 +156,12 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) BlobDataItem
// Returns |file_id| given to CreateFutureFile.
uint64_t GetFutureFileID() const;
file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
file_access() const {
DCHECK(type_ == Type::kFile || type_ == Type::kFileFilesystem);
return file_access_;
}
private:
friend class BlobBuilderFromStream;
friend class BlobDataBuilder;
@ -195,6 +211,9 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) BlobDataItem
scoped_refptr<FileSystemContext>
file_system_context_; // For Type::kFileFilesystem.
file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
file_access_; // For Type::kFile and kFileFilesystem.
};
COMPONENT_EXPORT(STORAGE_BROWSER)

@ -17,6 +17,7 @@
#include "base/memory/ptr_util.h"
#include "base/task/thread_pool.h"
#include "base/trace_event/trace_event.h"
#include "components/file_access/scoped_file_access_delegate.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "storage/browser/blob/blob_data_handle.h"
@ -515,8 +516,7 @@ BlobReader::Status BlobReader::ReadItem() {
GetOrCreateFileReaderAtIndex(current_item_index_);
if (!reader)
return ReportError(net::ERR_FILE_NOT_FOUND);
return ReadFileItem(reader, bytes_to_read);
return ReadFileItem(reader, bytes_to_read, item.file_access());
}
void BlobReader::AdvanceItem() {
@ -560,8 +560,11 @@ void BlobReader::ReadBytesItem(const BlobDataItem& item, int bytes_to_read) {
AdvanceBytesRead(bytes_to_read);
}
BlobReader::Status BlobReader::ReadFileItem(FileStreamReader* reader,
int bytes_to_read) {
BlobReader::Status BlobReader::ReadFileItem(
FileStreamReader* reader,
int bytes_to_read,
file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
file_access) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!io_pending_)
<< "Can't begin IO while another IO operation is pending.";
@ -725,7 +728,8 @@ std::unique_ptr<FileStreamReader> BlobReader::CreateFileStreamReader(
}
return FileStreamReader::CreateForLocalFile(
file_task_runner_.get(), item.path(),
item.offset() + additional_offset, item.expected_modification_time());
item.offset() + additional_offset, item.expected_modification_time(),
item.file_access());
case BlobDataItem::Type::kFileFilesystem: {
int64_t max_bytes_to_read =
item.length() == std::numeric_limits<uint64_t>::max()
@ -738,7 +742,8 @@ std::unique_ptr<FileStreamReader> BlobReader::CreateFileStreamReader(
}
return item.file_system_context()->CreateFileStreamReader(
item.filesystem_url(), item.offset() + additional_offset,
max_bytes_to_read, item.expected_modification_time());
max_bytes_to_read, item.expected_modification_time(),
item.file_access());
}
case BlobDataItem::Type::kBytes:
case BlobDataItem::Type::kBytesDescription:

@ -13,9 +13,13 @@
#include <vector>
#include "base/component_export.h"
#include "base/functional/callback_forward.h"
#include "base/functional/callback_helpers.h"
#include "base/gtest_prod_util.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "components/file_access/scoped_file_access.h"
#include "components/file_access/scoped_file_access_delegate.h"
#include "mojo/public/cpp/base/big_buffer.h"
#include "mojo/public/cpp/system/data_pipe.h"
#include "net/base/completion_once_callback.h"
@ -168,7 +172,7 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) BlobReader {
FRIEND_TEST_ALL_PREFIXES(BlobReaderTest, HandleBeforeAsyncCancel);
FRIEND_TEST_ALL_PREFIXES(BlobReaderTest, ReadFromIncompleteBlob);
BlobReader(const BlobDataHandle* blob_handle);
explicit BlobReader(const BlobDataHandle* blob_handle);
bool total_size_calculated() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@ -208,7 +212,11 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) BlobReader {
void AdvanceItem();
void AdvanceBytesRead(int result);
void ReadBytesItem(const BlobDataItem& item, int bytes_to_read);
BlobReader::Status ReadFileItem(FileStreamReader* reader, int bytes_to_read);
BlobReader::Status ReadFileItem(
FileStreamReader* reader,
int bytes_to_read,
file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
file_access);
void DidReadFile(int result);
void DeleteItemReaders();
Status ReadReadableDataHandle(const BlobDataItem& item, int bytes_to_read);

@ -8,9 +8,12 @@
#include "base/barrier_closure.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/task/sequenced_task_runner.h"
#include "components/file_access/scoped_file_access.h"
#include "components/file_access/scoped_file_access_delegate.h"
#include "net/base/features.h"
#include "storage/browser/blob/blob_builder_from_stream.h"
#include "storage/browser/blob/blob_data_builder.h"
@ -57,17 +60,21 @@ class BlobRegistryImpl::BlobUnderConstruction {
mojo::Remote<blink::mojom::Blob> blob;
};
BlobUnderConstruction(BlobRegistryImpl* blob_registry,
const std::string& uuid,
const std::string& content_type,
const std::string& content_disposition,
std::vector<ElementEntry> elements,
mojo::ReportBadMessageCallback bad_message_callback)
BlobUnderConstruction(
BlobRegistryImpl* blob_registry,
const std::string& uuid,
const std::string& content_type,
const std::string& content_disposition,
std::vector<ElementEntry> elements,
mojo::ReportBadMessageCallback bad_message_callback,
file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
file_access)
: blob_registry_(blob_registry),
uuid_(uuid),
builder_(std::make_unique<BlobDataBuilder>(uuid)),
elements_(std::move(elements)),
bad_message_callback_(std::move(bad_message_callback)) {
bad_message_callback_(std::move(bad_message_callback)),
file_access_(std::move(file_access)) {
builder_->set_content_type(content_type);
builder_->set_content_disposition(content_disposition);
}
@ -213,6 +220,10 @@ class BlobRegistryImpl::BlobUnderConstruction {
// Number of dependent blobs that have started constructing.
size_t ready_dependent_blob_count_ = 0;
// Callback to gain dlp file access.
file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
file_access_;
base::WeakPtrFactory<BlobUnderConstruction> weak_ptr_factory_{this};
};
@ -383,9 +394,9 @@ void BlobRegistryImpl::BlobUnderConstruction::ResolvedAllBlobDependencies() {
}
} else if (element->is_file()) {
const auto& f = element->get_file();
builder_->AppendFile(
f->path, f->offset, f->length,
f->expected_modification_time.value_or(base::Time()));
builder_->AppendFile(f->path, f->offset, f->length,
f->expected_modification_time.value_or(base::Time()),
file_access_);
} else if (element->is_blob()) {
DCHECK(blob_uuid_it != referenced_blob_uuids_.end());
const std::string& blob_uuid = *blob_uuid_it++;
@ -565,7 +576,7 @@ void BlobRegistryImpl::Register(
blobs_under_construction_[uuid] = std::make_unique<BlobUnderConstruction>(
this, uuid, content_type, content_disposition, std::move(element_entries),
receivers_.GetBadMessageCallback());
receivers_.GetBadMessageCallback(), delegate->GetAccessCallback());
std::unique_ptr<BlobDataHandle> handle = context_->AddFutureBlob(
uuid, content_type, content_disposition,

@ -9,6 +9,9 @@
#include "base/component_export.h"
#include "base/containers/flat_set.h"
#include "base/containers/unique_ptr_adapters.h"
#include "base/functional/callback_forward.h"
#include "components/file_access/scoped_file_access.h"
#include "components/file_access/scoped_file_access_delegate.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "mojo/public/cpp/bindings/self_owned_associated_receiver.h"
#include "storage/browser/blob/blob_url_registry.h"
@ -35,6 +38,8 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) BlobRegistryImpl
virtual ~Delegate() {}
virtual bool CanReadFile(const base::FilePath& file) = 0;
virtual bool CanAccessDataForOrigin(const url::Origin& origin) = 0;
virtual file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
GetAccessCallback() = 0;
};
BlobRegistryImpl(base::WeakPtr<BlobStorageContext> context,

@ -530,8 +530,8 @@ void BlobStorageContext::FinishBuilding(BlobEntry* entry) {
scoped_refptr<BlobDataItem> new_item = BlobDataItem::CreateFile(
source_item->path(),
source_item->offset() + copy.source_item_offset, dest_size,
source_item->expected_modification_time(),
source_item->file_ref_);
source_item->expected_modification_time(), source_item->file_ref_,
source_item->file_access_);
copy.dest_item->set_item(std::move(new_item));
break;
}

@ -12,7 +12,11 @@
#include "base/compiler_specific.h"
#include "base/component_export.h"
#include "base/files/file.h"
#include "base/functional/callback_forward.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/weak_ptr.h"
#include "components/file_access/scoped_file_access.h"
#include "components/file_access/scoped_file_access_delegate.h"
#include "components/services/storage/public/cpp/filesystem/filesystem_proxy.h"
#include "net/base/completion_once_callback.h"
@ -45,7 +49,9 @@ class FileStreamReader {
scoped_refptr<base::TaskRunner> task_runner,
const base::FilePath& file_path,
int64_t initial_offset,
const base::Time& expected_modification_time);
const base::Time& expected_modification_time,
file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
file_access = base::NullCallback());
// Creates a new FileReader for a local file |file_path|, which is a
// relative path into |filesystem_proxy|. This function's behavior

@ -16,7 +16,9 @@
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/functional/callback_forward.h"
#include "components/file_access/scoped_file_access_delegate.h"
#include "storage/browser/file_system/file_permission_policy.h"
#include "storage/browser/file_system/file_stream_reader.h"
#include "storage/browser/file_system/open_file_system_mode.h"
#include "storage/browser/file_system/task_runner_bound_observer_list.h"
#include "storage/common/file_system/file_system_types.h"
@ -121,13 +123,17 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) FileSystemBackend {
// ERR_UPLOAD_FILE_CHANGED error.
// This method itself does *not* check if the given path exists and is a
// regular file. At most |max_bytes_to_read| can be fetched from the file
// stream reader.
// stream reader. The callback `file_access` grants access to dlp restricted
// files. If it is a NullCallback currently the access will be granted. This
// will change to being denied after b/265908846
virtual std::unique_ptr<FileStreamReader> CreateFileStreamReader(
const FileSystemURL& url,
int64_t offset,
int64_t max_bytes_to_read,
const base::Time& expected_modification_time,
FileSystemContext* context) const = 0;
FileSystemContext* context,
file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
file_access) const = 0;
// Creates a new file stream writer for a given filesystem URL |url| with an
// offset |offset|.

@ -19,6 +19,7 @@
#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"
@ -595,14 +596,17 @@ std::unique_ptr<FileStreamReader> FileSystemContext::CreateFileStreamReader(
const FileSystemURL& url,
int64_t offset,
int64_t max_bytes_to_read,
const base::Time& expected_modification_time) {
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);
expected_modification_time, this,
std::move(file_access));
}
std::unique_ptr<FileStreamWriter> FileSystemContext::CreateFileStreamWriter(

@ -15,6 +15,7 @@
#include "base/component_export.h"
#include "base/files/file.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/ref_counted_delete_on_sequence.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
@ -22,6 +23,7 @@
#include "base/threading/sequence_bound.h"
#include "base/types/pass_key.h"
#include "build/build_config.h"
#include "components/file_access/scoped_file_access_delegate.h"
#include "components/services/storage/public/cpp/quota_error_or.h"
#include "components/services/storage/public/mojom/quota_client.mojom.h"
#include "mojo/public/cpp/bindings/receiver.h"
@ -281,7 +283,9 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) FileSystemContext
const FileSystemURL& url,
int64_t offset,
int64_t max_bytes_to_read,
const base::Time& expected_modification_time);
const base::Time& expected_modification_time,
file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
file_access = base::NullCallback());
// Creates new FileStreamWriter instance to write into a file pointed by
// `url` from `offset`.

@ -129,10 +129,12 @@ IsolatedFileSystemBackend::CreateFileStreamReader(
int64_t offset,
int64_t max_bytes_to_read,
const base::Time& expected_modification_time,
FileSystemContext* context) const {
FileSystemContext* context,
file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
file_access) const {
return FileStreamReader::CreateForLocalFile(
context->default_file_task_runner(), url.path(), offset,
expected_modification_time);
expected_modification_time, std::move(file_access));
}
std::unique_ptr<FileStreamWriter>

@ -9,6 +9,7 @@
#include <memory>
#include "components/file_access/scoped_file_access_delegate.h"
#include "storage/browser/file_system/file_system_backend.h"
#include "storage/browser/file_system/task_runner_bound_observer_list.h"
@ -44,7 +45,9 @@ class IsolatedFileSystemBackend : public FileSystemBackend {
int64_t offset,
int64_t max_bytes_to_read,
const base::Time& expected_modification_time,
FileSystemContext* context) const override;
FileSystemContext* context,
file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
file_access) const override;
std::unique_ptr<FileStreamWriter> CreateFileStreamWriter(
const FileSystemURL& url,
int64_t offset,

@ -12,11 +12,14 @@
#include "base/check_op.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/location.h"
#include "base/task/bind_post_task.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/task_runner.h"
#include "base/types/pass_key.h"
#include "components/file_access/scoped_file_access.h"
#include "components/file_access/scoped_file_access_delegate.h"
#include "net/base/file_stream.h"
#include "net/base/io_buffer.h"
@ -46,10 +49,13 @@ std::unique_ptr<FileStreamReader> FileStreamReader::CreateForLocalFile(
scoped_refptr<base::TaskRunner> task_runner,
const base::FilePath& file_path,
int64_t initial_offset,
const base::Time& expected_modification_time) {
const base::Time& expected_modification_time,
file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
file_access) {
return std::make_unique<LocalFileStreamReader>(
std::move(task_runner), file_path, initial_offset,
expected_modification_time, base::PassKey<FileStreamReader>());
expected_modification_time, base::PassKey<FileStreamReader>(),
std::move(file_access));
}
LocalFileStreamReader::~LocalFileStreamReader() = default;
@ -84,11 +90,14 @@ LocalFileStreamReader::LocalFileStreamReader(
const base::FilePath& file_path,
int64_t initial_offset,
const base::Time& expected_modification_time,
base::PassKey<FileStreamReader> /*pass_key*/)
base::PassKey<FileStreamReader> /*pass_key*/,
file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
file_access)
: task_runner_(std::move(task_runner)),
file_path_(file_path),
initial_offset_(initial_offset),
expected_modification_time_(expected_modification_time) {}
expected_modification_time_(expected_modification_time),
file_access_(std::move(file_access)) {}
void LocalFileStreamReader::Open(net::CompletionOnceCallback callback) {
DCHECK(!has_pending_open_);
@ -98,8 +107,12 @@ void LocalFileStreamReader::Open(net::CompletionOnceCallback callback) {
base::OnceCallback<void(file_access::ScopedFileAccess)> open_cb =
base::BindOnce(&LocalFileStreamReader::OnScopedFileAccessRequested,
weak_factory_.GetWeakPtr(), std::move(callback));
if (file_access_) {
file_access_.Run({file_path_}, std::move(open_cb));
return;
}
// TODO(b/262199707 b/265908846): Replace with getting access through a
// TODO(b/265908846): Replace with getting access through a
// callback.
file_access::ScopedFileAccessDelegate::RequestFilesAccessForSystemIO(
{file_path_}, std::move(open_cb));

@ -12,10 +12,13 @@
#include "base/component_export.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/functional/callback_forward.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "base/types/pass_key.h"
#include "components/file_access/scoped_file_access.h"
#include "components/file_access/scoped_file_access_delegate.h"
#include "net/base/completion_once_callback.h"
#include "storage/browser/file_system/file_stream_reader.h"
@ -34,11 +37,14 @@ namespace storage {
class COMPONENT_EXPORT(STORAGE_BROWSER) LocalFileStreamReader
: public FileStreamReader {
public:
LocalFileStreamReader(scoped_refptr<base::TaskRunner> task_runner,
const base::FilePath& file_path,
int64_t initial_offset,
const base::Time& expected_modification_time,
base::PassKey<FileStreamReader> pass_key);
LocalFileStreamReader(
scoped_refptr<base::TaskRunner> task_runner,
const base::FilePath& file_path,
int64_t initial_offset,
const base::Time& expected_modification_time,
base::PassKey<FileStreamReader> pass_key,
file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
file_access);
~LocalFileStreamReader() override;
// FileStreamReader overrides.
@ -76,6 +82,8 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) LocalFileStreamReader
const int64_t initial_offset_;
const base::Time expected_modification_time_;
bool has_pending_open_ = false;
file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
file_access_;
base::WeakPtrFactory<LocalFileStreamReader> weak_factory_{this};
};

@ -15,6 +15,7 @@
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/bind.h"
#include "base/functional/callback_forward.h"
#include "base/functional/callback_helpers.h"
#include "base/location.h"
#include "base/run_loop.h"
@ -70,14 +71,23 @@ class LocalFileStreamReaderTest : public FileStreamReaderTest {
file_thread_.Stop();
base::RunLoop().RunUntilIdle();
}
std::unique_ptr<FileStreamReader> CreateFileReader(
const std::string& file_name,
int64_t initial_offset,
const base::Time& expected_modification_time) override {
return CreateFileReader(file_name, initial_offset,
expected_modification_time, base::NullCallback());
}
std::unique_ptr<FileStreamReader> CreateFileReader(
const std::string& file_name,
int64_t initial_offset,
const base::Time& expected_modification_time,
file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
file_access) {
return FileStreamReader::CreateForLocalFile(
file_task_runner(), test_dir().AppendASCII(file_name), initial_offset,
expected_modification_time);
expected_modification_time, std::move(file_access));
}
void WriteFile(const std::string& file_name,
@ -124,7 +134,7 @@ INSTANTIATE_TYPED_TEST_SUITE_P(Local,
FileStreamReaderTypedTest,
LocalFileStreamReaderTest);
// TODO(b/262199707 b/265908846): Replace direct call to
// TODO(b/265908846): Replace direct call to
// file_access::ScopedFileAccessDelegate with getting access through a callback.
TEST_F(LocalFileStreamReaderTest, ReadAllowedByDataLeakPrevention) {
this->WriteTestFile();
@ -151,7 +161,7 @@ TEST_F(LocalFileStreamReaderTest, ReadAllowedByDataLeakPrevention) {
ASSERT_EQ(this->kTestData, data);
}
// TODO(b/262199707 b/265908846): Replace direct call to
// TODO(b/265908846): Replace direct call to
// file_access::ScopedFileAccessDelegate with getting access through a callback.
TEST_F(LocalFileStreamReaderTest, ReadBlockedByDataLeakPrevention) {
this->WriteTestFile();
@ -178,4 +188,50 @@ TEST_F(LocalFileStreamReaderTest, ReadBlockedByDataLeakPrevention) {
ASSERT_EQ("", data);
}
TEST_F(LocalFileStreamReaderTest, ReadAllowedByDataLeakPreventionCallback) {
this->WriteTestFile();
base::MockRepeatingCallback<void(
const std::vector<base::FilePath>&,
base::OnceCallback<void(file_access::ScopedFileAccess)>)>
callback;
EXPECT_CALL(
callback,
Run(testing::ElementsAre(test_dir().AppendASCII(kTestFileName)), _))
.WillOnce(base::test::RunOnceCallback<1>(CreateScopedFileAccess(true)));
std::unique_ptr<FileStreamReader> reader(this->CreateFileReader(
std::string(this->kTestFileName), 0, this->test_file_modification_time(),
callback.Get()));
int result = 0;
std::string data;
ReadFromReader(reader.get(), &data, this->kTestData.size(), &result);
ASSERT_EQ(net::OK, result);
ASSERT_EQ(this->kTestData, data);
}
TEST_F(LocalFileStreamReaderTest, ReadBlockedByDataLeakPreventionCallback) {
this->WriteTestFile();
base::MockRepeatingCallback<void(
const std::vector<base::FilePath>&,
base::OnceCallback<void(file_access::ScopedFileAccess)>)>
callback;
file_access::ScopedFileAccessDelegate::
ScopedRequestFilesAccessCallbackForTesting file_access_callback(
callback.Get());
EXPECT_CALL(
callback,
Run(testing::ElementsAre(test_dir().AppendASCII(kTestFileName)), _))
.WillOnce(base::test::RunOnceCallback<1>(CreateScopedFileAccess(false)));
std::unique_ptr<FileStreamReader> reader(this->CreateFileReader(
std::string(this->kTestFileName), 0, this->test_file_modification_time(),
callback.Get()));
int result = 0;
std::string data;
ReadFromReader(reader.get(), &data, this->kTestData.size(), &result);
ASSERT_EQ(net::ERR_ACCESS_DENIED, result);
ASSERT_EQ("", data);
}
} // namespace storage

@ -13,6 +13,7 @@
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/metrics/histogram.h"
#include "components/file_access/scoped_file_access_delegate.h"
#include "storage/browser/file_system/async_file_util_adapter.h"
#include "storage/browser/file_system/copy_or_move_file_validator.h"
#include "storage/browser/file_system/file_stream_reader.h"
@ -137,7 +138,9 @@ SandboxFileSystemBackend::CreateFileStreamReader(
int64_t offset,
int64_t max_bytes_to_read,
const base::Time& expected_modification_time,
FileSystemContext* context) const {
FileSystemContext* context,
file_access::ScopedFileAccessDelegate::
RequestFilesAccessIOCallback /*file_access*/) const {
DCHECK(CanHandleType(url.type()));
DCHECK(delegate_);
return delegate_->CreateFileStreamReader(url, offset,

@ -14,6 +14,7 @@
#include "base/compiler_specific.h"
#include "base/component_export.h"
#include "base/memory/raw_ptr.h"
#include "components/file_access/scoped_file_access_delegate.h"
#include "storage/browser/file_system/file_system_backend.h"
#include "storage/browser/file_system/file_system_quota_util.h"
#include "storage/browser/file_system/sandbox_file_system_backend_delegate.h"
@ -58,7 +59,9 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) SandboxFileSystemBackend
int64_t offset,
int64_t max_bytes_to_read,
const base::Time& expected_modification_time,
FileSystemContext* context) const override;
FileSystemContext* context,
file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
file_access) const override;
std::unique_ptr<FileStreamWriter> CreateFileStreamWriter(
const FileSystemURL& url,
int64_t offset,

@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "storage/browser/test/mock_blob_registry_delegate.h"
#include "base/functional/callback_helpers.h"
namespace storage {
@ -15,4 +16,9 @@ bool MockBlobRegistryDelegate::CanAccessDataForOrigin(
return can_access_data_for_origin;
}
file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
MockBlobRegistryDelegate::GetAccessCallback() {
return base::DoNothing();
}
} // namespace storage

@ -19,6 +19,8 @@ class MockBlobRegistryDelegate
bool CanReadFile(const base::FilePath& file) override;
bool CanAccessDataForOrigin(const url::Origin& origin) override;
file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
GetAccessCallback() override;
bool can_read_file_result = true;
bool can_access_data_for_origin = true;

@ -13,6 +13,7 @@
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/task/sequenced_task_runner.h"
#include "components/file_access/scoped_file_access_delegate.h"
#include "storage/browser/file_system/copy_or_move_file_validator.h"
#include "storage/browser/file_system/file_observers.h"
#include "storage/browser/file_system/file_system_operation.h"
@ -216,7 +217,9 @@ std::unique_ptr<FileStreamReader> TestFileSystemBackend::CreateFileStreamReader(
int64_t offset,
int64_t max_bytes_to_read,
const base::Time& expected_modification_time,
FileSystemContext* context) const {
FileSystemContext* context,
file_access::ScopedFileAccessDelegate::
RequestFilesAccessIOCallback /*file_access*/) const {
return std::make_unique<SandboxFileStreamReader>(context, url, offset,
expected_modification_time);
}

@ -11,6 +11,7 @@
#include "base/files/file_path.h"
#include "base/memory/scoped_refptr.h"
#include "components/file_access/scoped_file_access_delegate.h"
#include "storage/browser/file_system/async_file_util_adapter.h"
#include "storage/browser/file_system/file_system_backend.h"
#include "storage/browser/file_system/task_runner_bound_observer_list.h"
@ -61,7 +62,9 @@ class TestFileSystemBackend : public FileSystemBackend {
int64_t offset,
int64_t max_bytes_to_read,
const base::Time& expected_modification_time,
FileSystemContext* context) const override;
FileSystemContext* context,
file_access::ScopedFileAccessDelegate::RequestFilesAccessIOCallback
file_access) const override;
std::unique_ptr<FileStreamWriter> CreateFileStreamWriter(
const FileSystemURL& url,
int64_t offset,