// Copyright 2019 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/win/setup/setup.h"
#include <shlobj.h>
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include "base/check.h"
#include "base/command_line.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/scoped_com_initializer.h"
#include "chrome/installer/util/self_cleaning_temp_dir.h"
#include "chrome/installer/util/work_item_list.h"
#include "chrome/updater/constants.h"
#include "chrome/updater/updater_branding.h"
#include "chrome/updater/updater_scope.h"
#include "chrome/updater/updater_version.h"
#include "chrome/updater/util/util.h"
#include "chrome/updater/util/win_util.h"
#include "chrome/updater/win/setup/setup_util.h"
#include "chrome/updater/win/win_constants.h"
namespace updater {
namespace {
// Returns a list of base file names which the setup copies to the install
// directory. The source of these files is either the unpacked metainstaller
// archive, or the `out` directory of the build, if a command line argument is
// present. In the former case, which is the normal execution flow, the files
// are enumerated from the directory where the metainstaller unpacked its
// contents. In the latter case, the file containing the run time dependencies
// of the updater (which is generated by GN at build time) is parsed, and the
// relevant file names are extracted.
std::vector<base::FilePath> GetSetupFiles(const base::FilePath& source_dir) {
std::vector<base::FilePath> result;
base::FileEnumerator it(
source_dir, false, base::FileEnumerator::FileType::FILES,
FILE_PATH_LITERAL("*"), base::FileEnumerator::FolderSearchPolicy::ALL,
base::FileEnumerator::ErrorPolicy::STOP_ENUMERATION);
it.ForEach([&result](const base::FilePath& file) {
result.push_back(file.BaseName());
});
if (it.GetError() != base::File::Error::FILE_OK) {
VLOG(2) << __func__ << " could not enumerate files : " << it.GetError();
return {};
}
return result;
}
} // namespace
int Setup(UpdaterScope scope) {
VLOG(1) << __func__ << ", scope: " << scope;
CHECK(!IsSystemInstall(scope) || ::IsUserAnAdmin());
auto scoped_com_initializer =
std::make_unique<base::win::ScopedCOMInitializer>(
base::win::ScopedCOMInitializer::kMTA);
base::FilePath temp_dir;
if (!base::GetTempDir(&temp_dir)) {
LOG(ERROR) << "GetTempDir failed.";
return kErrorCreatingTempDir;
}
const std::optional<base::FilePath> versioned_dir =
GetVersionedInstallDirectory(scope);
if (!versioned_dir) {
LOG(ERROR) << "GetVersionedInstallDirectory failed.";
return kErrorNoVersionedDirectory;
}
// Stop any processes that may be running under the versioned path before
// installation.
StopProcessesUnderPath(*versioned_dir, base::Seconds(15));
base::FilePath exe_path;
if (!base::PathService::Get(base::FILE_EXE, &exe_path)) {
LOG(ERROR) << "PathService failed.";
return kErrorPathServiceFailed;
}
installer::SelfCleaningTempDir backup_dir;
if (!backup_dir.Initialize(temp_dir, L"updater-backup")) {
LOG(ERROR) << "Failed to initialize the backup dir.";
return kErrorInitializingBackupDir;
}
const auto source_dir = exe_path.DirName();
const auto setup_files = GetSetupFiles(source_dir);
if (setup_files.empty()) {
LOG(ERROR) << "No files to set up.";
return kErrorFailedToGetSetupFiles;
}
// All source files are installed in a flat directory structure inside the
// versioned directory, hence the BaseName function call below.
std::unique_ptr<WorkItemList> install_list(WorkItem::CreateWorkItemList());
for (const auto& file : setup_files) {
const base::FilePath target_path = versioned_dir->Append(file.BaseName());
const base::FilePath source_path = source_dir.Append(file);
install_list->AddCopyTreeWorkItem(source_path, target_path, temp_dir,
WorkItem::ALWAYS);
}
const HKEY key = UpdaterScopeToHKeyRoot(scope);
for (const auto& key_path :
{GetRegistryKeyClientsUpdater(), GetRegistryKeyClientStateUpdater()}) {
install_list->AddCreateRegKeyWorkItem(key, key_path, KEY_WOW64_32KEY);
install_list->AddSetRegValueWorkItem(key, key_path, KEY_WOW64_32KEY,
kRegValuePV, kUpdaterVersionUtf16,
true);
install_list->AddSetRegValueWorkItem(
key, key_path, KEY_WOW64_32KEY, kRegValueName,
base::ASCIIToWide(PRODUCT_FULLNAME_STRING), true);
}
const base::FilePath updater_exe = GetExecutableRelativePath();
switch (scope) {
case UpdaterScope::kUser:
AddComServerWorkItems(versioned_dir->Append(updater_exe), true,
install_list.get());
break;
case UpdaterScope::kSystem:
AddComServiceWorkItems(versioned_dir->Append(updater_exe), true,
install_list.get());
break;
}
base::CommandLine run_updater_wake_command(
versioned_dir->Append(updater_exe));
run_updater_wake_command.AppendSwitch(kWakeSwitch);
if (IsSystemInstall(scope)) {
run_updater_wake_command.AppendSwitch(kSystemSwitch);
}
if (!IsSystemInstall(scope)) {
RegisterUserRunAtStartup(GetTaskNamePrefix(scope), run_updater_wake_command,
install_list.get());
}
install_list->AddWorkItem(
new RegisterWakeTaskWorkItem(run_updater_wake_command, scope));
if (!install_list->Do()) {
LOG(ERROR) << "Install failed, rolling back...";
install_list->Rollback();
LOG(ERROR) << "Rollback complete.";
return kErrorFailedToRunInstallList;
}
VLOG(1) << "Setup succeeded.";
return kErrorOk;
}
} // namespace updater