blob: efa9225722aaea3709d647ce3e389f0e80a46f6c [file] [log] [blame]
// Copyright 2022 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/update_client/crx_cache.h"
#include <cstdint>
#include <optional>
#include <string>
#include <utility>
#include "base/check.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/notreached.h"
#include "base/path_service.h"
#include "base/sequence_checker.h"
#include "base/strings/strcat.h"
#include "base/strings/string_util.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/task_runner.h"
#include "base/task/thread_pool.h"
#include "base/types/expected.h"
#include "build/build_config.h"
#if BUILDFLAG(IS_WIN)
#include "base/strings/utf_string_conversions.h"
#endif
namespace update_client {
namespace {
void CleanUp(const base::FilePath& path, const std::string& id) {
base::FileEnumerator(path, false, base::FileEnumerator::FILES,
#if BUILDFLAG(IS_WIN)
base::UTF8ToWide(base::StrCat({id, "*"}))
#else
base::StrCat({id, "*"})
#endif
)
.ForEach(
[](const base::FilePath& file_path) { base::DeleteFile(file_path); });
}
} // namespace
CrxCache::CrxCache(std::optional<base::FilePath> crx_cache_root_path)
: crx_cache_root_path_(crx_cache_root_path) {}
CrxCache::~CrxCache() = default;
base::FilePath CrxCache::BuildCrxFilePath(const std::string& id,
const std::string& fp) {
CHECK(crx_cache_root_path_);
return crx_cache_root_path_->AppendASCII(base::JoinString({id, fp}, "_"));
}
void CrxCache::Get(
const std::string& id,
const std::string& fp,
base::OnceCallback<
void(const base::expected<base::FilePath, UnpackerError>)> callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(main_sequence_checker_);
if (!crx_cache_root_path_) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback),
base::unexpected(UnpackerError::kCrxCacheNotProvided)));
return;
}
const base::FilePath path = BuildCrxFilePath(id, fp);
task_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(
[](const base::FilePath& path)
-> base::expected<base::FilePath, UnpackerError> {
if (base::PathExists(path)) {
return path;
}
return base::unexpected(UnpackerError::kPuffinMissingPreviousCrx);
},
path),
std::move(callback));
}
void CrxCache::Put(
const base::FilePath& src,
const std::string& id,
const std::string& fp,
base::OnceCallback<
void(const base::expected<base::FilePath, UnpackerError>)> callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(main_sequence_checker_);
if (!crx_cache_root_path_) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback),
base::unexpected(UnpackerError::kCrxCacheNotProvided)));
return;
}
const base::FilePath dest = BuildCrxFilePath(id, fp);
task_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(
[](const base::FilePath& src, const base::FilePath& dest,
const std::string& id)
-> base::expected<base::FilePath, UnpackerError> {
// If already in cache, just return the path.
if (src == dest) {
return dest;
}
// Delete existing files for the app.
CleanUp(dest.DirName(), id);
// Move the file into cache.
if (!base::CreateDirectory(dest.DirName())) {
return base::unexpected(UnpackerError::kFailedToCreateCacheDir);
}
if (!base::Move(src, dest)) {
return base::unexpected(UnpackerError::kFailedToAddToCache);
}
return dest;
},
src, dest, id),
std::move(callback));
}
void CrxCache::RemoveAll(const std::string& id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(main_sequence_checker_);
if (!crx_cache_root_path_) {
return;
}
task_runner_->PostTask(FROM_HERE,
base::BindOnce(&CleanUp, *crx_cache_root_path_, id));
}
} // namespace update_client