blob: 5b01929382f9d8206bfe5be18d5ccfcbf4496714 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/updater/cleanup_task.h"
#include <optional>
#include <utility>
#include "base/check_op.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/logging.h"
#include "base/memory/scoped_refptr.h"
#include "base/process/launch.h"
#include "base/process/process.h"
#include "base/sequence_checker.h"
#include "base/task/thread_pool.h"
#include "base/time/time.h"
#include "base/version.h"
#include "build/build_config.h"
#include "chrome/updater/app/app_uninstall.h"
#include "chrome/updater/configurator.h"
#include "chrome/updater/persisted_data.h"
#include "chrome/updater/updater_version.h"
#include "chrome/updater/util/util.h"
#include "components/update_client/crx_cache.h"
#if BUILDFLAG(IS_WIN)
#include "chrome/updater/util/win_util.h"
#endif
namespace updater {
namespace {
constexpr int kMilestoneDeletionThreshold = 8;
void CleanupGoogleUpdate(UpdaterScope scope) {
#if BUILDFLAG(IS_WIN)
// Delete anything other than `GoogleUpdate.exe` under `\Google\Update`.
bool deleted = DeleteExcept(GetGoogleUpdateExePath(scope));
VLOG_IF(1, !deleted) << "Failed to delete obsolete files near "
<< GetGoogleUpdateExePath(scope);
#endif // BUILDFLAG(IS_WIN)
}
void CleanupOldUpdaterVersions(UpdaterScope scope) {
base::Version cleanup_max =
base::Version({base::Version(kUpdaterVersion).components()[0] -
kMilestoneDeletionThreshold});
CHECK_GT(base::Version(kUpdaterVersion), cleanup_max);
std::optional<base::FilePath> dir = GetInstallDirectory(scope);
if (!dir) {
return;
}
base::FileEnumerator(*dir, false, base::FileEnumerator::DIRECTORIES)
.ForEach([&scope, &cleanup_max](const base::FilePath& item) {
base::Version version(item.BaseName().AsUTF8Unsafe());
if (!version.IsValid() || version.CompareTo(cleanup_max) > 0) {
return;
}
VLOG(1) << __func__ << " cleaning up " << item;
// Attempt a normal uninstall.
const base::Process process = base::LaunchProcess(
GetUninstallSelfCommandLine(
scope, item.Append(GetExecutableRelativePath())),
{});
if (process.IsValid()) {
process.WaitForExitWithTimeout(base::Minutes(5), nullptr);
}
// Recursively delete the directory in case uninstall fails.
base::DeletePathRecursively(item);
});
}
} // namespace
CleanupTask::CleanupTask(UpdaterScope scope, scoped_refptr<Configurator> config)
: scope_(scope), config_(config) {}
CleanupTask::~CleanupTask() = default;
void CleanupTask::Run(base::OnceClosure callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::ThreadPool::PostTaskAndReply(
FROM_HERE, {base::MayBlock(), base::WithBaseSyncPrimitives()},
base::BindOnce(
[](UpdaterScope scope) {
CleanupGoogleUpdate(scope);
CleanupOldUpdaterVersions(scope);
#if BUILDFLAG(IS_MAC)
// TODO(crbug.com/394302692): Delete after M140.
CleanOldCrxCache();
#endif // IS_MAC
},
scope_),
base::BindOnce(
[](scoped_refptr<Configurator> config, base::OnceClosure callback) {
config->GetCrxCache()->RemoveIfNot(
config->GetUpdaterPersistedData()->GetAppIds(),
std::move(callback));
},
config_, std::move(callback)));
}
} // namespace updater