Commit f5112cb4 authored by Sorin Jianu's avatar Sorin Jianu Committed by Commit Bot

Create a Windows installer for the Chrome Updater.

The required updater files are archived as an uncompressed LZMA archive
(similar to a tar file), which then is compressed using LZMA, and the
result of the compression is inserted as a binary resource into the
updater installer file.

updater.7z -> updater.packed.7z -> UpdaterSetup.exe

At the moment, the updater files are: updater.exe and uninstall.cmd.
However, in the debug component build, some other run time dependencies
are needed. The dependencies are collected at build time and archived
with the updater files.

The vast majority of this code has been lifted from
//chrome/installer/mini_installer. It is possible to further make the
code a shared library between the installer and updater projects.
This would be work for the future.


Bug: 989772
Change-Id: I9b84e8d1f4475b9977d5b7fd78f37272dd7c6212
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1730309
Commit-Queue: Sorin Jianu <sorin@chromium.org>
Reviewed-by: default avatarJoshua Pawlicki <waffles@chromium.org>
Cr-Commit-Position: refs/heads/master@{#684210}
parent 5d40ba71
......@@ -5,9 +5,11 @@
import("//chrome/process_version_rc_template.gni")
import("//testing/test.gni")
# This target build the updater executable and its installer.
group("win") {
deps = [
":updater",
"//chrome/updater/win/installer:installer",
]
}
......@@ -26,6 +28,10 @@ executable("updater") {
"//build/win:default_exe_manifest",
"//chrome/updater:common",
]
data_deps = [
":uninstall.cmd",
]
}
copy("uninstall.cmd") {
......@@ -67,14 +73,6 @@ source_set("code") {
"//chrome/installer/util:with_no_strings",
"//components/update_client",
]
data = [
"setup/uninstall.cmd",
]
data_deps = [
":uninstall.cmd",
]
}
source_set("unittest") {
......@@ -90,4 +88,8 @@ source_set("unittest") {
"//base/test:test_support",
"//testing/gtest",
]
data_deps = [
"//chrome/updater/win/installer:installer_unittest",
]
}
# Copyright 2019 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.
import("//chrome/process_version_rc_template.gni")
import("//testing/test.gni")
source_set("lib") {
sources = [
"configuration.cc",
"configuration.h",
"exit_code.h",
"installer.cc",
"installer.h",
"installer.rc",
"installer_constants.cc",
"installer_constants.h",
"installer_resource.h",
"pe_resource.cc",
"pe_resource.h",
"regkey.cc",
"regkey.h",
"string.cc",
"string.h",
]
}
process_version_rc_template("version") {
template_file = "installer_version.rc.version"
output = "$root_out_dir/installer_version.rc"
}
# This target creats a list of runtime dependencies for the component
# builds. This list is parsed by the |create_installer_archive| script, the
# DLL paths extracted out from the list, and included in the archive.
updater_runtime_deps = "$root_gen_dir/updater.runtime_deps"
group("updater_runtime_deps") {
write_runtime_deps = updater_runtime_deps
data_deps = [
"//chrome/updater/win:updater",
]
}
template("generate_installer") {
output_dir = invoker.out_dir
packed_files_rc_file = "$target_gen_dir/$target_name/packed_files.rc"
archive_name = target_name + "_archive"
staging_dir = "$target_gen_dir/$target_name"
action(archive_name) {
script = "create_installer_archive.py"
release_file = "updater.release"
inputs = [
release_file,
]
outputs = [
"$output_dir/updater.packed.7z",
packed_files_rc_file,
]
args = [
"--build_dir",
rebase_path(root_out_dir, root_build_dir),
"--staging_dir",
rebase_path(staging_dir, root_build_dir),
"--input_file",
rebase_path(release_file, root_build_dir),
"--resource_file_path",
rebase_path(packed_files_rc_file, root_build_dir),
"--output_dir",
rebase_path(output_dir, root_build_dir),
"--setup_runtime_deps",
rebase_path(updater_runtime_deps, root_build_dir),
"--output_name=updater",
"--verbose",
]
deps = [
":updater_runtime_deps",
"//chrome/updater/win:updater",
]
if (is_component_build) {
args += [ "--component_build=1" ]
}
}
executable(target_name) {
output_name = invoker.output_name
sources = [
"installer_main.cc",
packed_files_rc_file,
]
configs += [ "//build/config/win:windowed" ]
libs = [ "setupapi.lib" ]
deps = [
":$archive_name",
":lib",
":version",
"//build/win:default_exe_manifest",
"//chrome/installer/util:with_no_strings",
]
}
}
generate_installer("installer") {
out_dir = root_out_dir
output_name = "UpdaterSetup"
}
test("installer_unittest") {
testonly = true
output_name = "updater_installer_unittest"
sources = [
"configuration_unittest.cc",
"run_all_unittests.cc",
"string_unittest.cc",
]
public_deps = [
":lib",
]
deps = [
"//base",
"//base/test:test_support",
"//chrome/installer/util:with_no_strings",
"//testing/gtest",
]
}
// 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 "chrome/updater/win/installer/configuration.h"
#include <shellapi.h>
#include "chrome/updater/win/installer/string.h"
namespace updater {
namespace {
// Returns true if GoogleUpdateIsMachine=1 is present in the environment.
bool GetGoogleUpdateIsMachineEnvVar() {
constexpr DWORD kBufferSize = 2;
StackString<kBufferSize> value;
const auto length = ::GetEnvironmentVariableW(L"GoogleUpdateIsMachine",
value.get(), kBufferSize);
return length == 1 && *value.get() == L'1';
}
} // namespace
Configuration::Configuration() {
Clear();
}
Configuration::~Configuration() {
Clear();
}
bool Configuration::Initialize(HMODULE module) {
Clear();
return ParseCommandLine(::GetCommandLine());
}
void Configuration::Clear() {
if (args_ != nullptr) {
::LocalFree(args_);
args_ = nullptr;
}
command_line_ = nullptr;
operation_ = INSTALL_PRODUCT;
argument_count_ = 0;
is_system_level_ = false;
has_invalid_switch_ = false;
}
// |command_line| is shared with this instance in the sense that this
// instance may refer to it at will throughout its lifetime, yet it will
// not release it.
bool Configuration::ParseCommandLine(const wchar_t* command_line) {
command_line_ = command_line;
args_ = ::CommandLineToArgvW(command_line_, &argument_count_);
if (!args_)
return false;
for (int i = 1; i < argument_count_; ++i) {
if (0 == ::lstrcmpi(args_[i], L"--system-level"))
is_system_level_ = true;
else if (0 == ::lstrcmpi(args_[i], L"--cleanup"))
operation_ = CLEANUP;
}
if (!is_system_level_)
is_system_level_ = GetGoogleUpdateIsMachineEnvVar();
return true;
}
} // namespace updater
// Copyright (c) 2019 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.
#ifndef CHROME_UPDATER_WIN_INSTALLER_CONFIGURATION_H_
#define CHROME_UPDATER_WIN_INSTALLER_CONFIGURATION_H_
#include <windows.h>
namespace updater {
// A simple container of the updater's configuration, as defined by the
// command line used to invoke it.
class Configuration {
public:
enum Operation {
INSTALL_PRODUCT,
CLEANUP,
};
Configuration();
~Configuration();
// Initializes this instance on the basis of the process's command line.
bool Initialize(HMODULE module);
// Returns the desired operation dictated by the command line options.
Operation operation() const { return operation_; }
// Returns true if --system-level is on the command line or if
// GoogleUpdateIsMachine=1 is set in the process's environment.
bool is_system_level() const { return is_system_level_; }
// Returns true if any invalid switch is found on the command line.
bool has_invalid_switch() const { return has_invalid_switch_; }
protected:
void Clear();
bool ParseCommandLine(const wchar_t* command_line);
wchar_t** args_ = nullptr;
const wchar_t* command_line_ = nullptr;
int argument_count_ = 0;
Operation operation_ = INSTALL_PRODUCT;
bool is_system_level_ = false;
bool has_invalid_switch_ = false;
private:
Configuration(const Configuration&) = delete;
Configuration& operator=(const Configuration&) = delete;
};
} // namespace updater
#endif // CHROME_UPDATER_WIN_INSTALLER_CONFIGURATION_H_
// Copyright (c) 2019 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 "chrome/updater/win/installer/configuration.h"
#include <stddef.h>
#include <stdlib.h>
#include <memory>
#include "base/environment.h"
#include "base/macros.h"
#include "base/test/test_reg_util_win.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace updater {
namespace {
// A helper class to set the "GoogleUpdateIsMachine" environment variable.
class ScopedGoogleUpdateIsMachine {
public:
explicit ScopedGoogleUpdateIsMachine(bool value)
: env_(base::Environment::Create()) {
env_->SetVar("GoogleUpdateIsMachine", value ? "1" : "0");
}
~ScopedGoogleUpdateIsMachine() { env_->UnSetVar("GoogleUpdateIsMachine"); }
private:
std::unique_ptr<base::Environment> env_;
};
class TestConfiguration : public Configuration {
public:
explicit TestConfiguration(const wchar_t* command_line) {
EXPECT_TRUE(ParseCommandLine(command_line));
}
private:
DISALLOW_COPY_AND_ASSIGN(TestConfiguration);
};
} // namespace
class UpdaterInstallerConfigurationTest : public ::testing::Test {
protected:
UpdaterInstallerConfigurationTest() = default;
private:
DISALLOW_COPY_AND_ASSIGN(UpdaterInstallerConfigurationTest);
};
// Test that the operation type is CLEANUP iff --cleanup is on the cmdline.
TEST_F(UpdaterInstallerConfigurationTest, Operation) {
EXPECT_EQ(Configuration::INSTALL_PRODUCT,
TestConfiguration(L"spam.exe").operation());
EXPECT_EQ(Configuration::INSTALL_PRODUCT,
TestConfiguration(L"spam.exe --clean").operation());
EXPECT_EQ(Configuration::INSTALL_PRODUCT,
TestConfiguration(L"spam.exe --cleanupthis").operation());
EXPECT_EQ(Configuration::CLEANUP,
TestConfiguration(L"spam.exe --cleanup").operation());
EXPECT_EQ(Configuration::CLEANUP,
TestConfiguration(L"spam.exe --cleanup now").operation());
}
TEST_F(UpdaterInstallerConfigurationTest, IsSystemLevel) {
EXPECT_FALSE(TestConfiguration(L"spam.exe").is_system_level());
EXPECT_FALSE(TestConfiguration(L"spam.exe --chrome").is_system_level());
EXPECT_TRUE(TestConfiguration(L"spam.exe --system-level").is_system_level());
{
ScopedGoogleUpdateIsMachine env_setter(false);
EXPECT_FALSE(TestConfiguration(L"spam.exe").is_system_level());
}
{
ScopedGoogleUpdateIsMachine env_setter(true);
EXPECT_TRUE(TestConfiguration(L"spam.exe").is_system_level());
}
}
TEST_F(UpdaterInstallerConfigurationTest, HasInvalidSwitch) {
EXPECT_FALSE(TestConfiguration(L"spam.exe").has_invalid_switch());
EXPECT_TRUE(
TestConfiguration(L"spam.exe --chrome-frame").has_invalid_switch());
}
} // namespace updater
This diff is collapsed.
// Copyright 2019 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.
#ifndef CHROME_UPDATER_WIN_INSTALLER_EXIT_CODE_H_
#define CHROME_UPDATER_WIN_INSTALLER_EXIT_CODE_H_
namespace updater {
// Installer process exit codes (the underlying type is uint32_t).
enum ExitCode {
SUCCESS_EXIT_CODE = 0,
GENERIC_INITIALIZATION_FAILURE = 101,
COMMAND_STRING_OVERFLOW = 105,
WAIT_FOR_PROCESS_FAILED = 107,
PATH_STRING_OVERFLOW = 108,
UNABLE_TO_GET_WORK_DIRECTORY = 109,
UNABLE_TO_EXTRACT_ARCHIVE = 112,
UNABLE_TO_SET_DIRECTORY_ACL = 117,
INVALID_OPTION = 118,
RUN_SETUP_FAILED_FILE_NOT_FOUND = 122, // ERROR_FILE_NOT_FOUND.
RUN_SETUP_FAILED_PATH_NOT_FOUND = 123, // ERROR_PATH_NOT_FOUND.
RUN_SETUP_FAILED_COULD_NOT_CREATE_PROCESS = 124, // All other errors.
};
} // namespace updater
#endif // CHROME_UPDATER_WIN_INSTALLER_EXIT_CODE_H_
This diff is collapsed.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<!--
Have compatibility section here instead of using
build/win/compatibility.manifest
to work around crbug.com/272660.
TODO(yukawa): Use build/win/compatibility.manifest again.
-->
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!--The ID below indicates application support for Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
<!--The ID below indicates application support for Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<!--The ID below indicates application support for Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<!--The ID below indicates application support for Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!--The ID below indicates application support for Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
</application>
</compatibility>
<ms_asmv2:trustInfo xmlns:ms_asmv2="urn:schemas-microsoft-com:asm.v2">
<ms_asmv2:security>
<ms_asmv2:requestedPrivileges>
<ms_asmv2:requestedExecutionLevel level="asInvoker" />
</ms_asmv2:requestedPrivileges>
</ms_asmv2:security>
</ms_asmv2:trustInfo>
</assembly>
// Copyright 2019 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.
#ifndef CHROME_UPDATER_WIN_INSTALLER_INSTALLER_H_
#define CHROME_UPDATER_WIN_INSTALLER_INSTALLER_H_
#include <windows.h>
#include "chrome/updater/win/installer/exit_code.h"
#include "chrome/updater/win/installer/string.h"
namespace updater {
// A container of a process exit code (eventually passed to ExitProcess) and
// a Windows error code for cases where the exit code is non-zero.
struct ProcessExitResult {
DWORD exit_code;
DWORD windows_error;
explicit ProcessExitResult(DWORD exit) : exit_code(exit), windows_error(0) {}
ProcessExitResult(DWORD exit, DWORD win)
: exit_code(exit), windows_error(win) {}
bool IsSuccess() const { return exit_code == SUCCESS_EXIT_CODE; }
};
// A stack-based string large enough to hold an executable to run
// (which is a path), plus a few extra arguments.
using CommandString = StackString<MAX_PATH * 4>;
// Main function for the installer.
ProcessExitResult WMain(HMODULE module);
} // namespace updater
#endif // CHROME_UPDATER_WIN_INSTALLER_INSTALLER_H_
// Microsoft Visual C++ generated resource script.
//
#include "installer_resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#define APSTUDIO_HIDDEN_SYMBOLS
#include "windows.h"
#undef APSTUDIO_HIDDEN_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (U.S.) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#endif //_WIN32
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_MINI_INSTALLER ICON "installer.ico"
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"installer_resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#define APSTUDIO_HIDDEN_SYMBOLS\r\n"
"#include ""windows.h""\r\n"
"#undef APSTUDIO_HIDDEN_SYMBOL\0"
END
#endif // APSTUDIO_INVOKED
#endif // English (U.S.) resources
/////////////////////////////////////////////////////////////////////////////
// Copyright 2019 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 "chrome/updater/win/installer/installer_constants.h"
namespace updater {
// The prefix of the updater archive resource.
const wchar_t kUpdaterArchivePrefix[] = L"updater";
// Temp directory prefix that this process creates.
const wchar_t kTempPrefix[] = L"UPDATER";
// 7zip archive.
const wchar_t kLZMAResourceType[] = L"B7";
} // namespace updater
// Copyright 2019 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.
#ifndef CHROME_UPDATER_WIN_INSTALLER_INSTALLER_CONSTANTS_H_
#define CHROME_UPDATER_WIN_INSTALLER_INSTALLER_CONSTANTS_H_
namespace updater {
// Various filenames and prefixes.
extern const wchar_t kUpdaterArchivePrefix[];
extern const wchar_t kTempPrefix[];
// The resource types that would be unpacked from the mini installer.
extern const wchar_t kLZMAResourceType[];
} // namespace updater
#endif // CHROME_UPDATER_WIN_INSTALLER_INSTALLER_CONSTANTS_H_
// Copyright 2017 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 <windows.h>
#include "chrome/updater/win/installer/installer.h"
// http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx
extern "C" IMAGE_DOS_HEADER __ImageBase;
int WINAPI wWinMain(HINSTANCE /* instance */,
HINSTANCE /* previous_instance */,
LPWSTR /* command_line */,
int /* command_show */) {
updater::ProcessExitResult result =
updater::WMain(reinterpret_cast<HMODULE>(&__ImageBase));
return result.exit_code;
}
// Copyright (c) 2019 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.
#ifndef CHROME_UPDATER_WIN_INSTALLER_INSTALLER_RESOURCE_H_
#define CHROME_UPDATER_WIN_INSTALLER_INSTALLER_RESOURCE_H_
#define IDC_STATIC -1
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NO_MFC 1
#define _APS_NEXT_RESOURCE_VALUE 129
#define _APS_NEXT_COMMAND_VALUE 32771
#define _APS_NEXT_CONTROL_VALUE 1000
#define _APS_NEXT_SYMED_VALUE 110
#endif
#endif
#endif // CHROME_UPDATER_WIN_INSTALLER_INSTALLER_RESOURCE_H_
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
// Use the ordinal 1 here, to avoid needing to #include a header file
// to use the VS_VERSION_INFO macro. This header file changes with different
// SDK versions which causes headaches building in some environments. The
// VERSIONINFO resource will always be at index 1.
1 VERSIONINFO
FILEVERSION @MAJOR@,@MINOR@,@BUILD@,@PATCH@
PRODUCTVERSION @MAJOR@,@MINOR@,@BUILD@,@PATCH@
FILEFLAGSMASK 0x17L
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x4L
FILETYPE 0x1L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "@COMPANY_FULLNAME@"
VALUE "FileDescription", "@PRODUCT_INSTALLER_FULLNAME@"
VALUE "FileVersion", "@MAJOR@.@MINOR@.@BUILD@.@PATCH@"
VALUE "InternalName", "Chrome Updater"
VALUE "LegalCopyright", "@COPYRIGHT@"
VALUE "ProductName", "@PRODUCT_INSTALLER_FULLNAME@"
VALUE "ProductVersion", "@MAJOR@.@MINOR@.@BUILD@.@PATCH@"
VALUE "CompanyShortName", "@COMPANY_SHORTNAME@"
VALUE "ProductShortName", "@PRODUCT_INSTALLER_SHORTNAME@"
VALUE "LastChange", "@LASTCHANGE@"
VALUE "Official Build", "@OFFICIAL_BUILD@"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END
// Copyright 2019 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 "chrome/updater/win/installer/pe_resource.h"
namespace updater {
PEResource::PEResource(const wchar_t* name, const wchar_t* type, HMODULE module)
: resource_(nullptr), module_(module) {
resource_ = ::FindResource(module, name, type);
}
bool PEResource::IsValid() {
return nullptr != resource_;
}
size_t PEResource::Size() {
return ::SizeofResource(module_, resource_);
}
bool PEResource::WriteToDisk(const wchar_t* full_path) {
// Resource handles are not real HGLOBALs so do not attempt to close them.
// Windows frees them whenever there is memory pressure.
HGLOBAL data_handle = ::LoadResource(module_, resource_);
if (nullptr == data_handle)
return false;
void* data = ::LockResource(data_handle);
if (nullptr == data)
return false;
size_t resource_size = Size();
HANDLE out_file = ::CreateFile(full_path, GENERIC_WRITE, 0, nullptr,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (INVALID_HANDLE_VALUE == out_file)
return false;
DWORD written = 0;
if (!::WriteFile(out_file, data, static_cast<DWORD>(resource_size), &written,
nullptr)) {
::CloseHandle(out_file);
return false;
}
return ::CloseHandle(out_file) ? true : false;
}
} // namespace updater
// Copyright 2019 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.
#ifndef CHROME_UPDATER_WIN_INSTALLER_PE_RESOURCE_H_
#define CHROME_UPDATER_WIN_INSTALLER_PE_RESOURCE_H_
#include <stddef.h>
#include <windows.h>
namespace updater {
// This class models a windows PE resource. It does not pretend to be a full
// API wrapper and it is just concerned with loading it to memory and writing
// it to disk. Each resource is unique only in the context of a loaded module,
// that is why you need to specify one on each constructor.
class PEResource {
public:
// Takes the resource name, the resource type, and the module where
// to look for the resource. If the resource is found IsValid() returns true.
PEResource(const wchar_t* name, const wchar_t* type, HMODULE module);
// Returns true if the resource is valid.
bool IsValid();
// Returns the size in bytes of the resource. Returns zero if the resource is
// not valid.
size_t Size();
// Creates a file in |path| with a copy of the resource. If the resource can
// not be loaded into memory or if it cannot be written to disk it returns
// false.
bool WriteToDisk(const wchar_t* path);
private:
HRSRC resource_ = nullptr;
HMODULE module_ = nullptr;
};
} // namespace updater
#endif // CHROME_UPDATER_WIN_INSTALLER_PE_RESOURCE_H_
// Copyright 2019 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 "chrome/updater/win/installer/regkey.h"
#include "chrome/updater/win/installer/installer_constants.h"
#include "chrome/updater/win/installer/string.h"
namespace updater {
LONG RegKey::Open(HKEY key, const wchar_t* sub_key, REGSAM access) {
Close();
return ::RegOpenKeyEx(key, sub_key, 0, access, &key_);
}
LONG RegKey::ReadSZValue(const wchar_t* value_name,
wchar_t* value,
size_t value_size) const {
DWORD type = 0;
DWORD byte_length = static_cast<DWORD>(value_size * sizeof(wchar_t));
LONG result = ::RegQueryValueEx(key_, value_name, nullptr, &type,
reinterpret_cast<BYTE*>(value), &byte_length);
if (result == ERROR_SUCCESS) {
if (type != REG_SZ) {
result = ERROR_NOT_SUPPORTED;
} else if (byte_length < 2) {
*value = L'\0';
} else if (value[byte_length / sizeof(wchar_t) - 1] != L'\0') {
if ((byte_length / sizeof(wchar_t)) < value_size)
value[byte_length / sizeof(wchar_t)] = L'\0';
else
result = ERROR_MORE_DATA;
}
}
return result;
}
LONG RegKey::ReadDWValue(const wchar_t* value_name, DWORD* value) const {
DWORD type = 0;
DWORD byte_length = sizeof(*value);
LONG result = ::RegQueryValueEx(key_, value_name, nullptr, &type,
reinterpret_cast<BYTE*>(value), &byte_length);
if (result == ERROR_SUCCESS) {
if (type != REG_DWORD) {
result = ERROR_NOT_SUPPORTED;
} else if (byte_length != sizeof(*value)) {
result = ERROR_NO_DATA;
}
}
return result;
}
LONG RegKey::WriteSZValue(const wchar_t* value_name, const wchar_t* value) {
return ::RegSetValueEx(key_, value_name, 0, REG_SZ,
reinterpret_cast<const BYTE*>(value),
(lstrlen(value) + 1) * sizeof(wchar_t));
}
LONG RegKey::WriteDWValue(const wchar_t* value_name, DWORD value) {
return ::RegSetValueEx(key_, value_name, 0, REG_DWORD,
reinterpret_cast<const BYTE*>(&value), sizeof(value));
}
void RegKey::Close() {
if (key_ != nullptr) {
::RegCloseKey(key_);
key_ = nullptr;
}
}
// static
bool RegKey::ReadSZValue(HKEY root_key,
const wchar_t* sub_key,
const wchar_t* value_name,
wchar_t* value,
size_t size) {
RegKey key;
return (key.Open(root_key, sub_key, KEY_QUERY_VALUE) == ERROR_SUCCESS &&
key.ReadSZValue(value_name, value, size) == ERROR_SUCCESS);
}
} // namespace updater
// Copyright 2019 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.
#ifndef CHROME_UPDATER_WIN_INSTALLER_REGKEY_H_
#define CHROME_UPDATER_WIN_INSTALLER_REGKEY_H_
#include <stddef.h>
#include <windows.h>
namespace updater {
// A helper class used to manipulate the Windows registry.
class RegKey {
public:
RegKey() : key_(nullptr) {}
~RegKey() { Close(); }
// Opens the key named |sub_key| with given |access| rights. Returns
// ERROR_SUCCESS or some other error.
LONG Open(HKEY key, const wchar_t* sub_key, REGSAM access);
// Returns true if a key is open.
bool is_valid() const { return key_ != nullptr; }
// Read a value from the registry into the memory indicated by |value|
// (of |value_size| wchar_t units). Returns ERROR_SUCCESS,
// ERROR_FILE_NOT_FOUND, ERROR_MORE_DATA, or some other error. |value| is
// guaranteed to be null-terminated on success.
LONG ReadSZValue(const wchar_t* value_name,
wchar_t* value,
size_t value_size) const;
LONG ReadDWValue(const wchar_t* value_name, DWORD* value) const;
// Write a value to the registry. SZ |value| must be null-terminated.
// Returns ERROR_SUCCESS or an error code.
LONG WriteSZValue(const wchar_t* value_name, const wchar_t* value);
LONG WriteDWValue(const wchar_t* value_name, DWORD value);
// Closes the key if it was open.
void Close();
// Helper function to read a value from registry. Returns true if value
// is read successfully and stored in parameter value. Returns false
// otherwise. |size| is measured in wchar_t units.
static bool ReadSZValue(HKEY root_key,
const wchar_t* sub_key,
const wchar_t* value_name,
wchar_t* value,
size_t value_size);
private:
RegKey(const RegKey&);
RegKey& operator=(const RegKey&);
HKEY key_;
};
} // namespace updater
#endif // CHROME_UPDATER_WIN_INSTALLER_REGKEY_H_
// Copyright (c) 2019 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 "base/bind.h"
#include "base/test/launcher/unit_test_launcher.h"
#include "base/test/test_suite.h"
int main(int argc, char** argv) {
base::TestSuite test_suite(argc, argv);
return base::LaunchUnitTestsSerially(
argc, argv,
base::Bind(&base::TestSuite::Run, base::Unretained(&test_suite)));
}
// Copyright 2019 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 "chrome/updater/win/installer/string.h"
#include <windows.h>
namespace {
// Returns true if the given two ASCII characters are same (ignoring case).
bool EqualASCIICharI(wchar_t a, wchar_t b) {
if (a >= L'A' && a <= L'Z')
a += (L'a' - L'A');
if (b >= L'A' && b <= L'Z')
b += (L'a' - L'A');
return (a == b);
}
} // namespace
namespace updater {
// Formats a sequence of |bytes| as hex. The |str| buffer must have room for
// at least 2*|size| + 1.
bool HexEncode(const void* bytes, size_t size, wchar_t* str, size_t str_size) {
if (str_size <= (size * 2))
return false;
static const wchar_t kHexChars[] = L"0123456789ABCDEF";
str[size * 2] = L'\0';
for (size_t i = 0; i < size; ++i) {
char b = reinterpret_cast<const char*>(bytes)[i];
str[(i * 2)] = kHexChars[(b >> 4) & 0xf];
str[(i * 2) + 1] = kHexChars[b & 0xf];
}
return true;
}
size_t SafeStrLen(const wchar_t* str, size_t alloc_size) {
if (!str || !alloc_size)
return 0;
size_t len = 0;
while (--alloc_size && str[len] != L'\0')
++len;
return len;
}
bool SafeStrCopy(wchar_t* dest, size_t dest_size, const wchar_t* src) {
if (!dest || !dest_size)
return false;
wchar_t* write = dest;
for (size_t remaining = dest_size; remaining != 0; --remaining) {
if ((*write++ = *src++) == L'\0')
return true;
}
// If we fail, we do not want to leave the string with partially copied
// contents. The reason for this is that we use these strings mostly for
// named objects such as files. If we copy a partial name, then that could
// match with something we do not want it to match with.
// Furthermore, since SafeStrCopy is called from SafeStrCat, we do not
// want to mutate the string in case the caller handles the error of a
// failed concatenation. For example:
//
// wchar_t buf[5] = {0};
// if (!SafeStrCat(buf, _countof(buf), kLongName))
// SafeStrCat(buf, _countof(buf), kShortName);
//
// If we were to return false in the first call to SafeStrCat but still
// mutate the buffer, the buffer will be in an unexpected state.
*dest = L'\0';
return false;
}
// Safer replacement for lstrcat function.
bool SafeStrCat(wchar_t* dest, size_t dest_size, const wchar_t* src) {
// Use SafeStrLen instead of lstrlen just in case the |dest| buffer isn't
// terminated.
size_t str_len = SafeStrLen(dest, dest_size);
return SafeStrCopy(dest + str_len, dest_size - str_len, src);
}
bool StrStartsWith(const wchar_t* str, const wchar_t* start_str) {
if (str == nullptr || start_str == nullptr)
return false;
for (int i = 0; start_str[i] != L'\0'; ++i) {
if (!EqualASCIICharI(str[i], start_str[i]))
return false;
}
return true;
}
const wchar_t* GetNameFromPathExt(const wchar_t* path, size_t size) {
if (!size)
return path;
const wchar_t* current = &path[size - 1];
while (current != path && L'\\' != *current)
--current;
// If no path separator found, just return |path|.
// Otherwise, return a pointer right after the separator.
return ((current == path) && (L'\\' != *current)) ? current : (current + 1);
}
wchar_t* GetNameFromPathExt(wchar_t* path, size_t size) {
return const_cast<wchar_t*>(
GetNameFromPathExt(const_cast<const wchar_t*>(path), size));
}
} // namespace updater
// Copyright 2019 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.
#ifndef CHROME_UPDATER_WIN_INSTALLER_STRING_H_
#define CHROME_UPDATER_WIN_INSTALLER_STRING_H_
#include <stddef.h>
namespace updater {
// NOTE: Do not assume that these string functions support UTF encoding.
// This is fine for the purposes of the mini_installer, but you have
// been warned!
// Formats a sequence of |bytes| as hex. The |str| buffer must have room for
// at least 2*|size| + 1.
bool HexEncode(const void* bytes, size_t size, wchar_t* str, size_t str_size);
// Counts the number of characters in the string up to a maximum of
// alloc_size. The highest return value from this function can therefore be
// alloc_size - 1 since |alloc_size| includes the \0 terminator.
size_t SafeStrLen(const wchar_t* str, size_t alloc_size);
// Simple replacement for CRT string copy method that does not overflow.
// Returns true if the source was copied successfully otherwise returns false.
// Parameter src is assumed to be nullptr terminated and the nullptr character
// is copied over to string dest.
bool SafeStrCopy(wchar_t* dest, size_t dest_size, const wchar_t* src);
// Simple replacement for CRT string copy method that does not overflow.
// Returns true if the source was copied successfully otherwise returns false.
// Parameter src is assumed to be nullptr terminated and the nullptr character
// is copied over to string dest. If the return value is false, the |dest|
// string should be the same as it was before.
bool SafeStrCat(wchar_t* dest, size_t dest_size, const wchar_t* src);
// Function to check if a string (specified by str) starts with another string
// (specified by start_str). The comparison is string insensitive.
bool StrStartsWith(const wchar_t* str, const wchar_t* start_str);
// Takes the path to file and returns a pointer to the basename component.
// Example input -> output:
// c:\full\path\to\file.ext -> file.ext
// file.ext -> file.ext
// Note: |size| is the number of characters in |path| not including the string
// terminator.
const wchar_t* GetNameFromPathExt(const wchar_t* path, size_t size);
wchar_t* GetNameFromPathExt(wchar_t* path, size_t size);
// A string class that manages a fixed size buffer on the stack.
// The methods in the class are based on the above string methods and the
// class additionally is careful about proper buffer termination.
template <size_t kCapacity>
class StackString {
public:
StackString() {
static_assert(kCapacity != 0, "invalid buffer size");
buffer_[kCapacity] = L'\0'; // We always reserve 1 more than asked for.
clear();
}
// We do not expose a constructor that accepts a string pointer on purpose.
// We expect the caller to call assign() and handle failures.
// Returns the number of reserved characters in this buffer, _including_
// the reserved char for the terminator.
size_t capacity() const { return kCapacity; }
wchar_t* get() { return buffer_; }
bool assign(const wchar_t* str) {
return SafeStrCopy(buffer_, kCapacity, str);
}
bool append(const wchar_t* str) {
return SafeStrCat(buffer_, kCapacity, str);
}
void clear() { buffer_[0] = L'\0'; }
size_t length() const { return SafeStrLen(buffer_, kCapacity); }
// Does a case insensitive search for a substring.
const wchar_t* findi(const wchar_t* find) const {
return SearchStringI(buffer_, find);
}
// Case insensitive string compare.
int comparei(const wchar_t* str) const { return lstrcmpiW(buffer_, str); }
// Case sensitive string compare.
int compare(const wchar_t* str) const { return lstrcmpW(buffer_, str); }
// Terminates the string at the specified location.
// Note: this method has no effect if this object's length is less than
// |location|.
bool truncate_at(size_t location) {
if (location >= kCapacity)
return false;
buffer_[location] = L'\0';
return true;
}
protected:
// We reserve 1 more than what is asked for as a safeguard against
// off-by-one errors.
wchar_t buffer_[kCapacity + 1];
private:
StackString(const StackString&);
StackString& operator=(const StackString&);
};
} // namespace updater
#endif // CHROME_UPDATER_WIN_INSTALLER_STRING_H_
// Copyright 2019 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 <stddef.h>
#include <stdlib.h>
#include <windows.h>
#include <string>
#include "chrome/updater/win/installer/string.h"
#include "testing/gtest/include/gtest/gtest.h"
using updater::StackString;
namespace {
class InstallerStringTest : public testing::Test {
protected:
void SetUp() override {}
void TearDown() override {}
};
} // namespace
// Tests the strcat/strcpy/length support of the StackString class.
TEST_F(InstallerStringTest, StackStringOverflow) {
static const wchar_t kTestString[] = L"1234567890";
StackString<MAX_PATH> str;
EXPECT_EQ(static_cast<size_t>(MAX_PATH), str.capacity());
std::wstring compare_str;
EXPECT_EQ(str.length(), compare_str.length());
EXPECT_EQ(0, compare_str.compare(str.get()));
size_t max_chars = str.capacity() - 1;
while ((str.length() + (_countof(kTestString) - 1)) <= max_chars) {
EXPECT_TRUE(str.append(kTestString));
compare_str.append(kTestString);
EXPECT_EQ(str.length(), compare_str.length());
EXPECT_EQ(0, compare_str.compare(str.get()));
}
EXPECT_GT(static_cast<size_t>(MAX_PATH), str.length());
// Now we've exhausted the space we allocated for the string,
// so append should fail.
EXPECT_FALSE(str.append(kTestString));
// ...and remain unchanged.
EXPECT_EQ(0, compare_str.compare(str.get()));
EXPECT_EQ(str.length(), compare_str.length());
// Last test for fun.
str.clear();
compare_str.clear();
EXPECT_EQ(0, compare_str.compare(str.get()));
EXPECT_EQ(str.length(), compare_str.length());
}
[GENERAL]
updater.exe: %(UpdaterDir)s\
gen\chrome\updater\win\uninstall.cmd: %(UpdaterDir)s\
......@@ -21,14 +21,21 @@ namespace updater {
namespace {
constexpr char kBuildGenWinDir[] = "gen\\chrome\\updater\\win";
const base::char16* kUpdaterFiles[] = {
L"updater.exe",
L"uninstall.cmd",
#if defined(COMPONENT_BUILD)
L"base.dll", L"boringssl.dll", L"crcrypto.dll", L"icuuc.dll",
L"libc++.dll", L"prefs.dll", L"protobuf_lite.dll", L"updater.exe",
L"url_lib.dll", L"zlib.dll",
// TODO(sorin): get the list of component dependencies from a build-time
// file instead of hardcoding the names of the components here.
L"base.dll",
L"boringssl.dll",
L"crcrypto.dll",
L"icuuc.dll",
L"libc++.dll",
L"prefs.dll",
L"protobuf_lite.dll",
L"url_lib.dll",
L"zlib.dll",
#endif
};
......@@ -69,10 +76,6 @@ int Setup() {
WorkItem::CreateCopyTreeWorkItem(source_path, target_path, temp_dir,
WorkItem::ALWAYS, base::FilePath()));
}
install_list->AddWorkItem(WorkItem::CreateCopyTreeWorkItem(
source_dir.AppendASCII(kBuildGenWinDir).AppendASCII(kUninstallScript),
product_dir.AppendASCII(kUninstallScript), temp_dir, WorkItem::ALWAYS,
base::FilePath()));
if (!install_list->Do()) {
LOG(ERROR) << "Install failed, rolling back...";
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment