blob: c3633afe53c76efb63758a2c874019128ac18092 [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/viz/host/persistent_cache_sandboxed_file_factory.h"
#include <utility>
#include "base/files/file_enumerator.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/hash/sha1.h"
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "components/base32/base32.h"
#include "components/persistent_cache/backend.h"
#include "components/persistent_cache/backend_storage.h"
namespace viz {
namespace {
PersistentCacheSandboxedFileFactory* g_instance = nullptr;
std::string GetVersionSuffix(const std::string& product) {
// The product's version string can be arbitrary long. So use SHA1 to reduce
// the length to avoid path length limit (260 on Windows and 4096 on Linux).
// The SHA1 is then encoded using a path-safe base32 (final length = 32
// characters).
// TODO(crbug.com/399642827): in future, we should be able to rely on
// auto-trimming ability of persistent caches, so even if there is a collision
// in version names, it would still be fine. It's still fine now because the
// collision probability of SHA1 is 1 in 2^80.
std::string sha1 = base::SHA1HashString(product);
return base32::Base32Encode(base::as_byte_span(sha1),
base32::Base32EncodePolicy::OMIT_PADDING);
}
// Returns the paths to the directory holding cache files. The format is:
// <cache_dir>/<cache_id>/<version>.
base::FilePath GetPersistentCacheDirectory(
const base::FilePath& cache_root_dir,
const base::FilePath::StringType& cache_id,
const std::string& product) {
return cache_root_dir.Append(cache_id).AppendASCII(GetVersionSuffix(product));
}
// Deletes all files in the cache directory that are associated with the given
// cache_id but are not the current database or journal file. This is to clean
// up stale cache files from previous runs or different product versions.
void DeleteStaleFiles(const base::FilePath& cache_root_dir,
const base::FilePath::StringType& cache_id,
const std::string& product) {
DCHECK(!cache_root_dir.empty());
const std::string version_suffix = GetVersionSuffix(product);
base::FilePath cache_dir = cache_root_dir.Append(cache_id);
if (!base::PathExists(cache_dir)) {
return;
}
base::FileEnumerator enumerator(cache_dir, false,
base::FileEnumerator::DIRECTORIES);
for (base::FilePath name = enumerator.Next(); !name.empty();
name = enumerator.Next()) {
if (name.BaseName().MaybeAsASCII() != version_suffix) {
base::DeletePathRecursively(name);
}
}
}
bool CreateCacheDirectory(const base::FilePath& cache_dir) {
if (!base::CreateDirectory(cache_dir)) {
PLOG(ERROR) << "Failed to create cache directory: " << cache_dir;
return false;
}
return true;
}
} // namespace
/* static */
void PersistentCacheSandboxedFileFactory::CreateInstance(
const base::FilePath& cache_root_dir) {
DCHECK(!g_instance);
g_instance = new PersistentCacheSandboxedFileFactory(
cache_root_dir,
/*task_runner=*/base::ThreadPool::CreateSequencedTaskRunner(
{base::MayBlock(), base::TaskPriority::USER_VISIBLE,
base::TaskShutdownBehavior::BLOCK_SHUTDOWN}));
g_instance->AddRef();
}
/* static */
PersistentCacheSandboxedFileFactory*
PersistentCacheSandboxedFileFactory::GetInstance() {
return g_instance;
}
/* static */
void PersistentCacheSandboxedFileFactory::SetInstanceForTesting(
PersistentCacheSandboxedFileFactory* factory) {
g_instance = factory;
}
PersistentCacheSandboxedFileFactory::PersistentCacheSandboxedFileFactory(
const base::FilePath& cache_root_dir,
scoped_refptr<base::SequencedTaskRunner> background_task_runner)
: cache_root_dir_(cache_root_dir),
background_task_runner_(std::move(background_task_runner)) {
CHECK(!cache_root_dir_.empty());
// TODO(crbug.com/399642827): We don't support relative path yet. The flags
// that are added by AddFlagsForPassingToUntrustedProcess() don't work with
// relative paths on Windows. See
// https://source.chromium.org/chromium/chromium/src/+/main:base/files/file_util_win.cc;drc=c99aa55ee638df4d6f0073c5d950acbda6ab4c6d;l=422
CHECK(cache_root_dir_.IsAbsolute());
background_task_runner_->PostTask(
FROM_HERE, base::BindOnce(base::IgnoreResult(&CreateCacheDirectory),
cache_root_dir_));
}
PersistentCacheSandboxedFileFactory::~PersistentCacheSandboxedFileFactory() =
default;
std::optional<persistent_cache::BackendParams>
PersistentCacheSandboxedFileFactory::CreateFiles(const CacheIdString& cache_id,
const std::string& product) {
background_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&DeleteStaleFiles, cache_root_dir_, cache_id, product));
base::FilePath cache_dir =
GetPersistentCacheDirectory(cache_root_dir_, cache_id, product);
auto backend = persistent_cache::BackendStorage(cache_dir).MakeBackend(
base::FilePath(FILE_PATH_LITERAL("cache")));
if (!backend) {
PLOG(ERROR) << "Failed to open persistent cache files in directory \""
<< cache_dir << "\"";
return std::nullopt;
}
return backend->ExportReadWriteParams();
}
void PersistentCacheSandboxedFileFactory::CreateFilesAsync(
const CacheIdString& cache_id,
const std::string& product,
CreateFilesCallback callback) {
// The reply will be posted to the current SequencedTaskRunner.
background_task_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&PersistentCacheSandboxedFileFactory::CreateFiles, this,
cache_id, product),
std::move(callback));
}
bool PersistentCacheSandboxedFileFactory::ClearFiles(
const CacheIdString& cache_id,
const std::string& product) {
// Delete the whole version directory.
return base::DeletePathRecursively(
GetPersistentCacheDirectory(cache_root_dir_, cache_id, product));
}
void PersistentCacheSandboxedFileFactory::ClearFilesAsync(
const CacheIdString& cache_id,
const std::string& product,
ClearFilesCallback callback) {
// The reply will be posted to the current SequencedTaskRunner.
background_task_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&PersistentCacheSandboxedFileFactory::ClearFiles, this,
cache_id, product),
std::move(callback));
}
} // namespace viz