Skip to content

Commit

Permalink
Introduce BackendFileOperations::EnumerateFiles
Browse files Browse the repository at this point in the history
Introduce a method to enumerate files in a sandboxed process. This will
be used in a subsequence CL.

Bug: 1289542
Change-Id: If65a456b16ba21563d97dc24b0521e6f012bf939
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3568764
Reviewed-by: Maks Orlovich <morlovich@chromium.org>
Reviewed-by: Robert Sesek <rsesek@chromium.org>
Commit-Queue: Yutaka Hirano <yhirano@chromium.org>
Cr-Commit-Position: refs/heads/main@{#990290}
  • Loading branch information
yutakahirano authored and Chromium LUCI CQ committed Apr 8, 2022
1 parent c64c65c commit 361b99b
Show file tree
Hide file tree
Showing 20 changed files with 432 additions and 25 deletions.
36 changes: 36 additions & 0 deletions content/browser/net/http_cache_backend_file_operations_factory.cc
Expand Up @@ -8,6 +8,7 @@
#include "base/files/file_util.h"
#include "base/task/thread_pool.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "net/disk_cache/simple/simple_file_enumerator.h"

namespace content {

Expand All @@ -21,6 +22,30 @@ static_assert(static_cast<uint32_t>(OpenFileFlags::kCreateAndWrite) ==
(base::File::FLAG_CREATE | base::File::FLAG_WRITE),
"kCreateAndWrite");

class FileEnumerator final : public network::mojom::FileEnumerator {
public:
explicit FileEnumerator(const base::FilePath& path) : enumerator_(path) {}
~FileEnumerator() override = default;

void GetNext(uint32_t num_entries, GetNextCallback callback) override {
std::vector<disk_cache::BackendFileOperations::FileEnumerationEntry>
entries;
bool end = false;
for (uint32_t i = 0; i < num_entries; ++i) {
if (auto entry = enumerator_.Next()) {
entries.push_back(std::move(*entry));
} else {
end = true;
break;
}
}
std::move(callback).Run(entries, end, enumerator_.HasError());
}

private:
disk_cache::SimpleFileEnumerator enumerator_;
};

class HttpCacheBackendFileOperations final
: public network::mojom::HttpCacheBackendFileOperations {
public:
Expand Down Expand Up @@ -119,6 +144,17 @@ class HttpCacheBackendFileOperations final
: absl::nullopt);
}

void EnumerateFiles(
const base::FilePath& path,
mojo::PendingReceiver<network::mojom::FileEnumerator> receiver) override {
if (!IsValid(path, "EnumerateFiles")) {
return;
}
DVLOG(1) << "EnumerateFiles: path = " << path;
mojo::MakeSelfOwnedReceiver(std::make_unique<FileEnumerator>(path),
std::move(receiver));
}

private:
bool IsValid(const base::FilePath& path, base::StringPiece tag) const {
if (!path.IsAbsolute()) {
Expand Down
72 changes: 72 additions & 0 deletions content/browser/net/sandboxed_http_cache_browsertest.cc
Expand Up @@ -4,15 +4,18 @@

#include "base/feature_list.h"
#include "base/files/scoped_temp_dir.h"
#include "base/path_service.h"
#include "base/test/bind.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
#include "content/browser/net/http_cache_backend_file_operations_factory.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/network_service_instance.h"
#include "content/public/browser/page.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_paths.h"
#include "content/public/common/network_service_util.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
Expand All @@ -32,6 +35,8 @@ namespace {

using network::mojom::SimpleCache;
using network::mojom::SimpleCacheEntry;
using FileEnumerationEntry =
disk_cache::BackendFileOperations::FileEnumerationEntry;

class SandboxedHttpCacheBrowserTest : public ContentBrowserTest {
public:
Expand Down Expand Up @@ -116,6 +121,73 @@ IN_PROC_BROWSER_TEST_F(SandboxedHttpCacheBrowserTest, OpeningFileIsProhibited) {
EXPECT_EQ(result, absl::make_optional(false));
}

IN_PROC_BROWSER_TEST_F(SandboxedHttpCacheBrowserTest,
EnumerateFilesOnNonExistingDirectory) {
base::ScopedAllowBlockingForTesting allow_blocking;
base::RunLoop run_loop;

base::FilePath root;
base::PathService::Get(content::DIR_TEST_DATA, &root);
root =
root.Append(FILE_PATH_LITERAL("net")).Append(FILE_PATH_LITERAL("cache"));
mojo::PendingRemote<network::mojom::HttpCacheBackendFileOperationsFactory>
factory_remote;
HttpCacheBackendFileOperationsFactory factory(
factory_remote.InitWithNewPipeAndPassReceiver(), root);

network_service_test().set_disconnect_handler(run_loop.QuitClosure());
const base::FilePath path = root.Append(FILE_PATH_LITERAL("not-found"));
std::vector<FileEnumerationEntry> entries;
bool has_error = false;

network_service_test()->EnumerateFiles(
path, std::move(factory_remote),
base::BindLambdaForTesting(
[&](const std::vector<FileEnumerationEntry>& in_entries,
bool in_has_error) {
entries = in_entries;
has_error = in_has_error;
run_loop.Quit();
}));
run_loop.Run();
EXPECT_TRUE(entries.empty());
}

IN_PROC_BROWSER_TEST_F(SandboxedHttpCacheBrowserTest, EnumerateFiles) {
base::ScopedAllowBlockingForTesting allow_blocking;
base::RunLoop run_loop;

base::FilePath root;
base::PathService::Get(content::DIR_TEST_DATA, &root);
root =
root.Append(FILE_PATH_LITERAL("net")).Append(FILE_PATH_LITERAL("cache"));
mojo::PendingRemote<network::mojom::HttpCacheBackendFileOperationsFactory>
factory_remote;
HttpCacheBackendFileOperationsFactory factory(
factory_remote.InitWithNewPipeAndPassReceiver(), root);

network_service_test().set_disconnect_handler(run_loop.QuitClosure());
const base::FilePath path = root.Append(FILE_PATH_LITERAL("file_enumerator"));
std::vector<FileEnumerationEntry> entries;
bool has_error = false;

network_service_test()->EnumerateFiles(
path, std::move(factory_remote),
base::BindLambdaForTesting(
[&](const std::vector<FileEnumerationEntry>& in_entries,
bool in_has_error) {
entries = in_entries;
has_error = in_has_error;
run_loop.Quit();
}));
run_loop.Run();
ASSERT_EQ(1u, entries.size());
EXPECT_FALSE(has_error);
const FileEnumerationEntry entry = entries[0];
EXPECT_EQ(entry.path, path.Append(FILE_PATH_LITERAL("test.txt")));
EXPECT_EQ(entry.size, 13);
}

IN_PROC_BROWSER_TEST_F(SandboxedHttpCacheBrowserTest, CreateSimpleCache) {
base::RunLoop run_loop;

Expand Down
38 changes: 38 additions & 0 deletions content/public/test/network_service_test_helper.cc
Expand Up @@ -17,6 +17,8 @@
#include "base/metrics/field_trial.h"
#include "base/process/process.h"
#include "base/task/current_thread.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "build/build_config.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
Expand Down Expand Up @@ -362,6 +364,42 @@ class NetworkServiceTestHelper::NetworkServiceTestImpl
std::move(callback).Run(file.IsValid());
}

void EnumerateFiles(
const base::FilePath& path,
mojo::PendingRemote<network::mojom::HttpCacheBackendFileOperationsFactory>
factory_remote,
EnumerateFilesCallback callback) override {
auto task_runner =
base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()});
auto factory =
base::MakeRefCounted<network::MojoBackendFileOperationsFactory>(
std::move(factory_remote));
auto ops = factory->Create(task_runner);

using Entry = disk_cache::BackendFileOperations::FileEnumerationEntry;

task_runner->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(
[](const base::FilePath& path,
std::unique_ptr<disk_cache::BackendFileOperations> ops) {
base::ScopedAllowBaseSyncPrimitivesForTesting scope;
auto enumerator = ops->EnumerateFiles(path);
std::vector<Entry> entries;
while (auto entry = enumerator->Next()) {
entries.push_back(std::move(*entry));
}
return std::make_pair(entries, enumerator->HasError());
},
path, std::move(ops)),
base::BindOnce(
[](EnumerateFilesCallback callback,
std::pair<std::vector<Entry>, bool> arg) {
std::move(callback).Run(arg.first, arg.second);
},
std::move(callback)));
}

void CreateSimpleCache(
mojo::PendingRemote<network::mojom::HttpCacheBackendFileOperationsFactory>
factory,
Expand Down
@@ -0,0 +1 @@
hello
1 change: 1 addition & 0 deletions content/test/data/net/cache/file_enumerator/test.txt
@@ -0,0 +1 @@
hello, world
26 changes: 26 additions & 0 deletions net/disk_cache/disk_cache.cc
Expand Up @@ -22,9 +22,12 @@
#include "net/disk_cache/disk_cache.h"
#include "net/disk_cache/memory/mem_backend_impl.h"
#include "net/disk_cache/simple/simple_backend_impl.h"
#include "net/disk_cache/simple/simple_file_enumerator.h"

namespace {

using FileEnumerator = disk_cache::BackendFileOperations::FileEnumerator;

// Builds an instance of the backend depending on platform, type, experiments
// etc. Takes care of the retry state. This object will self-destroy when
// finished.
Expand Down Expand Up @@ -217,6 +220,24 @@ void CacheCreator::OnIOComplete(int result) {
DCHECK_EQ(net::ERR_IO_PENDING, rv);
}

class TrivialFileEnumerator final : public FileEnumerator {
public:
using FileEnumerationEntry =
disk_cache::BackendFileOperations::FileEnumerationEntry;

explicit TrivialFileEnumerator(const base::FilePath& path)
: enumerator_(path) {}
~TrivialFileEnumerator() override = default;

absl::optional<FileEnumerationEntry> Next() override {
return enumerator_.Next();
}
bool HasError() const override { return enumerator_.HasError(); }

private:
disk_cache::SimpleFileEnumerator enumerator_;
};

} // namespace

namespace disk_cache {
Expand Down Expand Up @@ -483,4 +504,9 @@ absl::optional<base::File::Info> TrivialFileOperations::GetFileInfo(
return file_info;
}

std::unique_ptr<FileEnumerator> TrivialFileOperations::EnumerateFiles(
const base::FilePath& path) {
return std::make_unique<TrivialFileEnumerator>(path);
}

} // namespace disk_cache
39 changes: 39 additions & 0 deletions net/disk_cache/disk_cache.h
Expand Up @@ -563,6 +563,38 @@ constexpr int kMaxWebUICodeCacheSize = 5 * 1024 * 1024;
// All the paths must be absolute paths.
class BackendFileOperations {
public:
struct FileEnumerationEntry {
FileEnumerationEntry() = default;
FileEnumerationEntry(base::FilePath path,
int64_t size,
base::Time last_accessed,
base::Time last_modified)
: path(std::move(path)),
size(size),
last_accessed(last_accessed),
last_modified(last_modified) {}

base::FilePath path;
int64_t size = 0;
base::Time last_accessed;
base::Time last_modified;
};

// An interface to enumerate files in a directory.
// Indirect descendants are not listed, and directories are not listed.
class FileEnumerator {
public:
virtual ~FileEnumerator() = default;

// Returns the next file in the directory, if any. Returns nullopt if there
// are no further files (including the error case). The path of the
// returned entry should be a full path.
virtual absl::optional<FileEnumerationEntry> Next() = 0;

// Returns true if we've found an error during traversal.
virtual bool HasError() const = 0;
};

virtual ~BackendFileOperations() = default;

// Creates a directory with the given path and returns whether that succeeded.
Expand All @@ -585,6 +617,11 @@ class BackendFileOperations {
// Returns information about the given path.
virtual absl::optional<base::File::Info> GetFileInfo(
const base::FilePath& path) = 0;

// Creates an object that can be used to enumerate files in the specified
// directory.
virtual std::unique_ptr<FileEnumerator> EnumerateFiles(
const base::FilePath& path) = 0;
};

// A factory interface that creates BackendFileOperations.
Expand Down Expand Up @@ -617,6 +654,8 @@ class NET_EXPORT TrivialFileOperations final : public BackendFileOperations {
base::File::Error* error) override;
absl::optional<base::File::Info> GetFileInfo(
const base::FilePath& path) override;
std::unique_ptr<FileEnumerator> EnumerateFiles(
const base::FilePath& path) override;

private:
SEQUENCE_CHECKER(sequence_checker_);
Expand Down
6 changes: 2 additions & 4 deletions net/disk_cache/simple/simple_file_enumerator.cc
Expand Up @@ -8,8 +8,6 @@
#include "base/files/file_util.h"
#include "base/logging.h"

using Entry = disk_cache::SimpleFileEnumerator::Entry;

// We have an optimized implementation for POSIX, and a fallback
// implementation for other platforms.

Expand All @@ -29,7 +27,7 @@ bool SimpleFileEnumerator::HasError() const {
return has_error_;
}

absl::optional<Entry> SimpleFileEnumerator::Next() {
absl::optional<SimpleFileEnumerator::Entry> SimpleFileEnumerator::Next() {
if (!dir_) {
return absl::nullopt;
}
Expand Down Expand Up @@ -82,7 +80,7 @@ bool SimpleFileEnumerator::HasError() const {
return enumerator_.GetError() != base::File::FILE_OK;
}

absl::optional<Entry> SimpleFileEnumerator::Next() {
absl::optional<SimpleFileEnumerator::Entry> SimpleFileEnumerator::Next() {
base::FilePath path = enumerator_.Next();
if (path.empty()) {
return absl::nullopt;
Expand Down
19 changes: 3 additions & 16 deletions net/disk_cache/simple/simple_file_enumerator.h
Expand Up @@ -11,6 +11,7 @@
#include "base/time/time.h"
#include "build/build_config.h"
#include "net/base/net_export.h"
#include "net/disk_cache/disk_cache.h"
#include "third_party/abseil-cpp/absl/types/optional.h"

#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
Expand All @@ -27,25 +28,11 @@ namespace disk_cache {
// https://crbug.com/270762 and https://codereview.chromium.org/22927018.
class NET_EXPORT SimpleFileEnumerator final {
public:
using Entry = BackendFileOperations::FileEnumerationEntry;

explicit SimpleFileEnumerator(const base::FilePath& root_path);
~SimpleFileEnumerator();

struct Entry {
Entry(base::FilePath path,
int64_t size,
base::Time last_accessed,
base::Time last_modified)
: path(std::move(path)),
size(size),
last_accessed(last_accessed),
last_modified(last_modified) {}

base::FilePath path;
int64_t size;
base::Time last_accessed;
base::Time last_modified;
};

// Returns true if we've found an error during enumeration.
bool HasError() const;

Expand Down
2 changes: 0 additions & 2 deletions net/disk_cache/simple/simple_file_enumerator_unittest.cc
Expand Up @@ -12,8 +12,6 @@
namespace disk_cache {
namespace {

using Entry = SimpleFileEnumerator::Entry;

base::FilePath GetRoot() {
base::FilePath root;
base::PathService::Get(base::DIR_SOURCE_ROOT, &root);
Expand Down

0 comments on commit 361b99b

Please sign in to comment.