123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668 |
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include <utility>
- #include "base/barrier_closure.h"
- #include "base/bind.h"
- #include "base/callback.h"
- #include "base/files/file_path.h"
- #include "base/files/file_util.h"
- #include "base/memory/raw_ptr.h"
- #include "base/metrics/field_trial.h"
- #include "base/task/bind_post_task.h"
- #include "base/task/single_thread_task_runner.h"
- #include "base/task/thread_pool/thread_pool_instance.h"
- #include "base/threading/sequenced_task_runner_handle.h"
- #include "build/build_config.h"
- #include "net/base/cache_type.h"
- #include "net/base/net_errors.h"
- #include "net/disk_cache/backend_cleanup_tracker.h"
- #include "net/disk_cache/blockfile/backend_impl.h"
- #include "net/disk_cache/cache_util.h"
- #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"
- #include "net/disk_cache/simple/simple_util.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.
- class CacheCreator {
- public:
- CacheCreator(const base::FilePath& path,
- disk_cache::ResetHandling reset_handling,
- int64_t max_bytes,
- net::CacheType type,
- net::BackendType backend_type,
- scoped_refptr<disk_cache::BackendFileOperationsFactory>
- file_operations_factory,
- #if BUILDFLAG(IS_ANDROID)
- base::android::ApplicationStatusListener* app_status_listener,
- #endif
- net::NetLog* net_log,
- base::OnceClosure post_cleanup_callback,
- disk_cache::BackendResultCallback callback);
- CacheCreator(const CacheCreator&) = delete;
- CacheCreator& operator=(const CacheCreator&) = delete;
- // Wait for any previous backends for given path to finish clean up and then
- // attempt to create a new one. This will never succeed synchronously, though
- // it may fail synchronously.
- net::Error TryCreateCleanupTrackerAndRun();
- // Creates the backend, the cleanup context for it having been already
- // established... or purposefully left as null. This will never succeed
- // synchronously, though it may fail synchronously.
- net::Error Run();
- private:
- ~CacheCreator();
- void DoCallback(int result);
- void OnIOComplete(int result);
- void OnCacheCleanupComplete(int original_error, bool cleanup_result);
- const base::FilePath path_;
- disk_cache::ResetHandling reset_handling_;
- bool retry_ = false;
- int64_t max_bytes_;
- net::CacheType type_;
- net::BackendType backend_type_;
- scoped_refptr<disk_cache::BackendFileOperationsFactory>
- file_operations_factory_;
- std::unique_ptr<disk_cache::BackendFileOperations> file_operations_;
- #if BUILDFLAG(IS_ANDROID)
- raw_ptr<base::android::ApplicationStatusListener> app_status_listener_;
- #endif
- base::OnceClosure post_cleanup_callback_;
- disk_cache::BackendResultCallback callback_;
- std::unique_ptr<disk_cache::Backend> created_cache_;
- raw_ptr<net::NetLog> net_log_;
- scoped_refptr<disk_cache::BackendCleanupTracker> cleanup_tracker_;
- };
- CacheCreator::CacheCreator(
- const base::FilePath& path,
- disk_cache::ResetHandling reset_handling,
- int64_t max_bytes,
- net::CacheType type,
- net::BackendType backend_type,
- scoped_refptr<disk_cache::BackendFileOperationsFactory> file_operations,
- #if BUILDFLAG(IS_ANDROID)
- base::android::ApplicationStatusListener* app_status_listener,
- #endif
- net::NetLog* net_log,
- base::OnceClosure post_cleanup_callback,
- disk_cache::BackendResultCallback callback)
- : path_(path),
- reset_handling_(reset_handling),
- max_bytes_(max_bytes),
- type_(type),
- backend_type_(backend_type),
- file_operations_factory_(std::move(file_operations)),
- #if BUILDFLAG(IS_ANDROID)
- app_status_listener_(app_status_listener),
- #endif
- post_cleanup_callback_(std::move(post_cleanup_callback)),
- callback_(std::move(callback)),
- net_log_(net_log) {
- }
- CacheCreator::~CacheCreator() = default;
- net::Error CacheCreator::Run() {
- #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA)
- static const bool kSimpleBackendIsDefault = true;
- #else
- static const bool kSimpleBackendIsDefault = false;
- #endif
- if (!retry_ && reset_handling_ == disk_cache::ResetHandling::kReset) {
- // Pretend that we failed to create a cache, so that we can handle `kReset`
- // and `kResetOnError` in a unified way, in CacheCreator::OnIOComplete.
- base::SequencedTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(&CacheCreator::OnIOComplete,
- base::Unretained(this), net::ERR_FAILED));
- return net::ERR_IO_PENDING;
- }
- if (backend_type_ == net::CACHE_BACKEND_SIMPLE ||
- (backend_type_ == net::CACHE_BACKEND_DEFAULT &&
- kSimpleBackendIsDefault)) {
- auto cache = std::make_unique<disk_cache::SimpleBackendImpl>(
- file_operations_factory_, path_, cleanup_tracker_.get(),
- /* file_tracker = */ nullptr, max_bytes_, type_, net_log_);
- disk_cache::SimpleBackendImpl* simple_cache = cache.get();
- created_cache_ = std::move(cache);
- #if BUILDFLAG(IS_ANDROID)
- if (app_status_listener_)
- simple_cache->set_app_status_listener(app_status_listener_);
- #endif
- simple_cache->Init(
- base::BindOnce(&CacheCreator::OnIOComplete, base::Unretained(this)));
- return net::ERR_IO_PENDING;
- }
- // Avoid references to blockfile functions on Android to reduce binary size.
- #if BUILDFLAG(IS_ANDROID)
- return net::ERR_FAILED;
- #else
- auto cache = std::make_unique<disk_cache::BackendImpl>(
- path_, cleanup_tracker_.get(),
- /*cache_thread = */ nullptr, type_, net_log_);
- disk_cache::BackendImpl* new_cache = cache.get();
- created_cache_ = std::move(cache);
- new_cache->SetMaxSize(max_bytes_);
- new_cache->Init(
- base::BindOnce(&CacheCreator::OnIOComplete, base::Unretained(this)));
- return net::ERR_IO_PENDING;
- #endif
- }
- net::Error CacheCreator::TryCreateCleanupTrackerAndRun() {
- // Before creating a cache Backend, a BackendCleanupTracker object is needed
- // so there is a place to keep track of outstanding I/O even after the backend
- // object itself is destroyed, so that further use of the directory
- // doesn't race with those outstanding disk I/O ops.
- // This method's purpose it to grab exlusive ownership of a fresh
- // BackendCleanupTracker for the cache path, and then move on to Run(),
- // which will take care of creating the actual cache backend. It's possible
- // that something else is currently making use of the directory, in which
- // case BackendCleanupTracker::TryCreate will fail, but will just have
- // TryCreateCleanupTrackerAndRun run again at an opportune time to make
- // another attempt.
- // The resulting BackendCleanupTracker is stored into a scoped_refptr member
- // so that it's kept alive while |this| CacheCreator exists , so that in the
- // case Run() needs to retry Backend creation the same BackendCleanupTracker
- // is used for both attempts, and |post_cleanup_callback_| gets called after
- // the second try, not the first one.
- cleanup_tracker_ = disk_cache::BackendCleanupTracker::TryCreate(
- path_, base::BindOnce(base::IgnoreResult(
- &CacheCreator::TryCreateCleanupTrackerAndRun),
- base::Unretained(this)));
- if (!cleanup_tracker_)
- return net::ERR_IO_PENDING;
- if (!post_cleanup_callback_.is_null())
- cleanup_tracker_->AddPostCleanupCallback(std::move(post_cleanup_callback_));
- return Run();
- }
- void CacheCreator::DoCallback(int net_error) {
- DCHECK_NE(net::ERR_IO_PENDING, net_error);
- disk_cache::BackendResult result;
- if (net_error == net::OK) {
- result = disk_cache::BackendResult::Make(std::move(created_cache_));
- } else {
- LOG(ERROR) << "Unable to create cache";
- result = disk_cache::BackendResult::MakeError(
- static_cast<net::Error>(net_error));
- created_cache_.reset();
- }
- std::move(callback_).Run(std::move(result));
- delete this;
- }
- // If the initialization of the cache fails, and |reset_handling| isn't set to
- // kNeverReset, we will discard the whole cache and create a new one.
- void CacheCreator::OnIOComplete(int result) {
- DCHECK_NE(result, net::ERR_IO_PENDING);
- if (result == net::OK ||
- reset_handling_ == disk_cache::ResetHandling::kNeverReset || retry_) {
- return DoCallback(result);
- }
- // We are supposed to try again, so delete the object and all files and do so.
- retry_ = true;
- created_cache_.reset();
- if (!file_operations_) {
- if (file_operations_factory_) {
- file_operations_ = file_operations_factory_->Create(
- base::SequencedTaskRunnerHandle::Get());
- } else {
- file_operations_ = std::make_unique<disk_cache::TrivialFileOperations>();
- }
- }
- file_operations_->CleanupDirectory(
- path_, base::BindOnce(&CacheCreator::OnCacheCleanupComplete,
- base::Unretained(this), result));
- }
- void CacheCreator::OnCacheCleanupComplete(int original_result,
- bool cleanup_result) {
- if (!cleanup_result) {
- // Cleaning up the cache directory fails, so this operation should be
- // considered failed.
- DCHECK_NE(original_result, net::OK);
- DCHECK_NE(original_result, net::ERR_IO_PENDING);
- DoCallback(original_result);
- return;
- }
- // The worker thread may be deleting files, but the original folder
- // is not there anymore... let's create a new set of files.
- int rv = Run();
- 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_;
- };
- class UnboundTrivialFileOperations
- : public disk_cache::UnboundBackendFileOperations {
- public:
- std::unique_ptr<disk_cache::BackendFileOperations> Bind(
- scoped_refptr<base::SequencedTaskRunner> task_runner) override {
- return std::make_unique<disk_cache::TrivialFileOperations>();
- }
- };
- } // namespace
- namespace disk_cache {
- BackendResult::BackendResult() = default;
- BackendResult::~BackendResult() = default;
- BackendResult::BackendResult(BackendResult&&) = default;
- BackendResult& BackendResult::operator=(BackendResult&&) = default;
- // static
- BackendResult BackendResult::MakeError(net::Error error_in) {
- DCHECK_NE(error_in, net::OK);
- BackendResult result;
- result.net_error = error_in;
- return result;
- }
- // static
- BackendResult BackendResult::Make(std::unique_ptr<Backend> backend_in) {
- DCHECK(backend_in);
- BackendResult result;
- result.net_error = net::OK;
- result.backend = std::move(backend_in);
- return result;
- }
- BackendResult CreateCacheBackendImpl(
- net::CacheType type,
- net::BackendType backend_type,
- scoped_refptr<BackendFileOperationsFactory> file_operations,
- const base::FilePath& path,
- int64_t max_bytes,
- ResetHandling reset_handling,
- #if BUILDFLAG(IS_ANDROID)
- base::android::ApplicationStatusListener* app_status_listener,
- #endif
- net::NetLog* net_log,
- base::OnceClosure post_cleanup_callback,
- BackendResultCallback callback) {
- DCHECK(!callback.is_null());
- if (type == net::MEMORY_CACHE) {
- std::unique_ptr<MemBackendImpl> mem_backend_impl =
- disk_cache::MemBackendImpl::CreateBackend(max_bytes, net_log);
- if (mem_backend_impl) {
- mem_backend_impl->SetPostCleanupCallback(
- std::move(post_cleanup_callback));
- return BackendResult::Make(std::move(mem_backend_impl));
- } else {
- if (!post_cleanup_callback.is_null())
- base::SequencedTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, std::move(post_cleanup_callback));
- return BackendResult::MakeError(net::ERR_FAILED);
- }
- }
- bool had_post_cleanup_callback = !post_cleanup_callback.is_null();
- CacheCreator* creator = new CacheCreator(
- path, reset_handling, max_bytes, type, backend_type,
- std::move(file_operations),
- #if BUILDFLAG(IS_ANDROID)
- std::move(app_status_listener),
- #endif
- net_log, std::move(post_cleanup_callback), std::move(callback));
- if (type == net::DISK_CACHE) {
- DCHECK(!had_post_cleanup_callback);
- return BackendResult::MakeError(creator->Run());
- }
- return BackendResult::MakeError(creator->TryCreateCleanupTrackerAndRun());
- }
- BackendResult CreateCacheBackend(
- net::CacheType type,
- net::BackendType backend_type,
- scoped_refptr<BackendFileOperationsFactory> file_operations,
- const base::FilePath& path,
- int64_t max_bytes,
- ResetHandling reset_handling,
- net::NetLog* net_log,
- BackendResultCallback callback) {
- return CreateCacheBackendImpl(type, backend_type, std::move(file_operations),
- path, max_bytes, reset_handling,
- #if BUILDFLAG(IS_ANDROID)
- nullptr,
- #endif
- net_log, base::OnceClosure(),
- std::move(callback));
- }
- #if BUILDFLAG(IS_ANDROID)
- NET_EXPORT BackendResult CreateCacheBackend(
- net::CacheType type,
- net::BackendType backend_type,
- scoped_refptr<BackendFileOperationsFactory> file_operations,
- const base::FilePath& path,
- int64_t max_bytes,
- ResetHandling reset_handling,
- net::NetLog* net_log,
- BackendResultCallback callback,
- base::android::ApplicationStatusListener* app_status_listener) {
- return CreateCacheBackendImpl(type, backend_type, std::move(file_operations),
- path, max_bytes, reset_handling,
- std::move(app_status_listener), net_log,
- base::OnceClosure(), std::move(callback));
- }
- #endif
- BackendResult CreateCacheBackend(
- net::CacheType type,
- net::BackendType backend_type,
- scoped_refptr<BackendFileOperationsFactory> file_operations,
- const base::FilePath& path,
- int64_t max_bytes,
- ResetHandling reset_handling,
- net::NetLog* net_log,
- base::OnceClosure post_cleanup_callback,
- BackendResultCallback callback) {
- return CreateCacheBackendImpl(type, backend_type, std::move(file_operations),
- path, max_bytes, reset_handling,
- #if BUILDFLAG(IS_ANDROID)
- nullptr,
- #endif
- net_log, std::move(post_cleanup_callback),
- std::move(callback));
- }
- void FlushCacheThreadForTesting() {
- // For simple backend.
- base::ThreadPoolInstance::Get()->FlushForTesting();
- // Block backend.
- BackendImpl::FlushForTesting();
- }
- void FlushCacheThreadAsynchronouslyForTesting(base::OnceClosure callback) {
- auto repeating_callback = base::BarrierClosure(2, std::move(callback));
- // For simple backend.
- base::ThreadPoolInstance::Get()->FlushAsyncForTesting( // IN-TEST
- base::BindPostTask(base::SequencedTaskRunnerHandle::Get(),
- repeating_callback));
- // Block backend.
- BackendImpl::FlushAsynchronouslyForTesting(repeating_callback);
- }
- int64_t Backend::CalculateSizeOfEntriesBetween(
- base::Time initial_time,
- base::Time end_time,
- Int64CompletionOnceCallback callback) {
- return net::ERR_NOT_IMPLEMENTED;
- }
- uint8_t Backend::GetEntryInMemoryData(const std::string& key) {
- return 0;
- }
- void Backend::SetEntryInMemoryData(const std::string& key, uint8_t data) {}
- EntryResult::EntryResult() = default;
- EntryResult::~EntryResult() = default;
- EntryResult::EntryResult(EntryResult&& other) {
- net_error_ = other.net_error_;
- entry_ = std::move(other.entry_);
- opened_ = other.opened_;
- other.net_error_ = net::ERR_FAILED;
- other.opened_ = false;
- }
- EntryResult& EntryResult::operator=(EntryResult&& other) {
- net_error_ = other.net_error_;
- entry_ = std::move(other.entry_);
- opened_ = other.opened_;
- other.net_error_ = net::ERR_FAILED;
- other.opened_ = false;
- return *this;
- }
- // static
- EntryResult EntryResult::MakeOpened(Entry* new_entry) {
- DCHECK(new_entry);
- EntryResult result;
- result.net_error_ = net::OK;
- result.entry_.reset(new_entry);
- result.opened_ = true;
- return result;
- }
- // static
- EntryResult EntryResult::MakeCreated(Entry* new_entry) {
- DCHECK(new_entry);
- EntryResult result;
- result.net_error_ = net::OK;
- result.entry_.reset(new_entry);
- result.opened_ = false;
- return result;
- }
- // static
- EntryResult EntryResult::MakeError(net::Error status) {
- DCHECK_NE(status, net::OK);
- EntryResult result;
- result.net_error_ = status;
- return result;
- }
- Entry* EntryResult::ReleaseEntry() {
- Entry* ret = entry_.release();
- net_error_ = net::ERR_FAILED;
- opened_ = false;
- return ret;
- }
- TrivialFileOperations::TrivialFileOperations() {
- DETACH_FROM_SEQUENCE(sequence_checker_);
- }
- TrivialFileOperations::~TrivialFileOperations() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- }
- bool TrivialFileOperations::CreateDirectory(const base::FilePath& path) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- #if DCHECK_IS_ON()
- DCHECK(bound_);
- #endif
- // This is needed for some unittests.
- if (path.empty()) {
- return false;
- }
- DCHECK(path.IsAbsolute());
- bool result = base::CreateDirectory(path);
- return result;
- }
- bool TrivialFileOperations::PathExists(const base::FilePath& path) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- #if DCHECK_IS_ON()
- DCHECK(bound_);
- #endif
- // This is needed for some unittests.
- if (path.empty()) {
- return false;
- }
- DCHECK(path.IsAbsolute());
- bool result = base::PathExists(path);
- return result;
- }
- bool TrivialFileOperations::DirectoryExists(const base::FilePath& path) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(path.IsAbsolute());
- #if DCHECK_IS_ON()
- DCHECK(bound_);
- #endif
- bool result = base::DirectoryExists(path);
- return result;
- }
- base::File TrivialFileOperations::OpenFile(const base::FilePath& path,
- uint32_t flags) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(path.IsAbsolute());
- #if DCHECK_IS_ON()
- DCHECK(bound_);
- #endif
- base::File file(path, flags);
- return file;
- }
- bool TrivialFileOperations::DeleteFile(const base::FilePath& path,
- DeleteFileMode mode) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(path.IsAbsolute());
- #if DCHECK_IS_ON()
- DCHECK(bound_);
- #endif
- bool result = false;
- switch (mode) {
- case DeleteFileMode::kDefault:
- result = base::DeleteFile(path);
- break;
- case DeleteFileMode::kEnsureImmediateAvailability:
- result = disk_cache::simple_util::SimpleCacheDeleteFile(path);
- break;
- }
- return result;
- }
- bool TrivialFileOperations::ReplaceFile(const base::FilePath& from_path,
- const base::FilePath& to_path,
- base::File::Error* error) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(from_path.IsAbsolute());
- DCHECK(to_path.IsAbsolute());
- #if DCHECK_IS_ON()
- DCHECK(bound_);
- #endif
- return base::ReplaceFile(from_path, to_path, error);
- }
- absl::optional<base::File::Info> TrivialFileOperations::GetFileInfo(
- const base::FilePath& path) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(path.IsAbsolute());
- #if DCHECK_IS_ON()
- DCHECK(bound_);
- #endif
- base::File::Info file_info;
- if (!base::GetFileInfo(path, &file_info)) {
- return absl::nullopt;
- }
- return file_info;
- }
- std::unique_ptr<FileEnumerator> TrivialFileOperations::EnumerateFiles(
- const base::FilePath& path) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(path.IsAbsolute());
- #if DCHECK_IS_ON()
- DCHECK(bound_);
- #endif
- return std::make_unique<TrivialFileEnumerator>(path);
- }
- void TrivialFileOperations::CleanupDirectory(
- const base::FilePath& path,
- base::OnceCallback<void(bool)> callback) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- // This is needed for some unittests.
- if (path.empty()) {
- base::SequencedTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(std::move(callback), false));
- return;
- }
- DCHECK(path.IsAbsolute());
- #if DCHECK_IS_ON()
- DCHECK(bound_);
- #endif
- disk_cache::CleanupDirectory(path, std::move(callback));
- }
- std::unique_ptr<UnboundBackendFileOperations> TrivialFileOperations::Unbind() {
- #if DCHECK_IS_ON()
- DCHECK(bound_);
- bound_ = false;
- #endif
- return std::make_unique<UnboundTrivialFileOperations>();
- }
- TrivialFileOperationsFactory::TrivialFileOperationsFactory() = default;
- TrivialFileOperationsFactory::~TrivialFileOperationsFactory() = default;
- std::unique_ptr<BackendFileOperations> TrivialFileOperationsFactory::Create(
- scoped_refptr<base::SequencedTaskRunner> task_runner) {
- return std::make_unique<TrivialFileOperations>();
- }
- std::unique_ptr<UnboundBackendFileOperations>
- TrivialFileOperationsFactory::CreateUnbound() {
- return std::make_unique<UnboundTrivialFileOperations>();
- }
- } // namespace disk_cache
|