diff options
author | grt@chromium.org <grt@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-19 12:19:20 +0000 |
---|---|---|
committer | grt@chromium.org <grt@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-19 12:19:20 +0000 |
commit | 7f6b60b45eee92072b5fad615e1ad760462d19bb (patch) | |
tree | e4563b7f9d1814a7010293c7352872dee2023737 /chrome/installer | |
parent | 5a8229a6ecc9199153716959e9d6b0f35d9fa673 (diff) | |
download | chromium_src-7f6b60b45eee92072b5fad615e1ad760462d19bb.zip chromium_src-7f6b60b45eee92072b5fad615e1ad760462d19bb.tar.gz chromium_src-7f6b60b45eee92072b5fad615e1ad760462d19bb.tar.bz2 |
Fix diff updates for MSI installs. Also makes diff updates possible for GCF and pure multi-install updates. While in there, I also removed the old (and no longer used) "-multifail" ap value tag.
BUG=72899,89315
TEST=run a diff installer (chrome_updater.exe) and see that it works to update an MSI-based installation of Chrome.
R=robertshield@chromium.org
Review URL: http://codereview.chromium.org/7401014
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@93005 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/installer')
-rw-r--r-- | chrome/installer/mini_installer.gyp | 2 | ||||
-rw-r--r-- | chrome/installer/mini_installer/configuration.cc | 77 | ||||
-rw-r--r-- | chrome/installer/mini_installer/configuration.h | 77 | ||||
-rw-r--r-- | chrome/installer/mini_installer/configuration_test.cc | 118 | ||||
-rw-r--r-- | chrome/installer/mini_installer/mini_installer.cc | 203 | ||||
-rw-r--r-- | chrome/installer/mini_installer/mini_installer.h | 13 | ||||
-rw-r--r-- | chrome/installer/setup/install_worker.cc | 29 |
7 files changed, 400 insertions, 119 deletions
diff --git a/chrome/installer/mini_installer.gyp b/chrome/installer/mini_installer.gyp index 493e77c..4f870d0 100644 --- a/chrome/installer/mini_installer.gyp +++ b/chrome/installer/mini_installer.gyp @@ -25,6 +25,8 @@ ], 'sources': [ 'mini_installer/appid.h', + 'mini_installer/configuration.cc', + 'mini_installer/configuration.h', 'mini_installer/decompress.cc', 'mini_installer/decompress.h', 'mini_installer/mini_installer.cc', diff --git a/chrome/installer/mini_installer/configuration.cc b/chrome/installer/mini_installer/configuration.cc new file mode 100644 index 0000000..cebe50c --- /dev/null +++ b/chrome/installer/mini_installer/configuration.cc @@ -0,0 +1,77 @@ +// Copyright (c) 2011 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/installer/mini_installer/configuration.h" + +#include <windows.h> +#include <shellapi.h> // NOLINT + +#include "chrome/installer/mini_installer/appid.h" + +namespace mini_installer { + +Configuration::Configuration() : args_(NULL) { + Clear(); +} + +Configuration::~Configuration() { + Clear(); +} + +const wchar_t* Configuration::program() const { + return args_ == NULL || argument_count_ < 1 ? NULL : args_[0]; +} + +void Configuration::Clear() { + if (args_ != NULL) { + ::LocalFree(args_); + args_ = NULL; + } + chrome_app_guid_ = google_update::kAppGuid; + command_line_ = NULL; + operation_ = INSTALL_PRODUCT; + argument_count_ = 0; + has_chrome_ = false; + has_chrome_frame_ = false; + is_multi_install_ = false; + is_system_level_ = false; +} + +bool Configuration::Initialize() { + return InitializeFromCommandLine(::GetCommandLine()); +} + +// This is its own function so that unit tests can provide their own command +// lines. |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::InitializeFromCommandLine(const wchar_t* command_line) { + Clear(); + + command_line_ = command_line; + args_ = ::CommandLineToArgvW(command_line_, &argument_count_); + if (args_ != NULL) { + for (int i = 1; i < argument_count_; ++i) { + if (0 == ::lstrcmpi(args_[i], L"--chrome-sxs")) + chrome_app_guid_ = google_update::kSxSAppGuid; + else if (0 == ::lstrcmpi(args_[i], L"--chrome")) + has_chrome_ = true; + else if (0 == ::lstrcmpi(args_[i], L"--chrome-frame")) + has_chrome_frame_ = true; + else if (0 == ::lstrcmpi(args_[i], L"--multi-install")) + is_multi_install_ = true; + else if (0 == ::lstrcmpi(args_[i], L"--system-level")) + is_system_level_ = true; + else if (0 == ::lstrcmpi(args_[i], L"--cleanup")) + operation_ = CLEANUP; + } + + // Single-install is either Chrome or Chrome Frame. + if (!is_multi_install_) + has_chrome_ = !has_chrome_frame_; + } + return args_ != NULL; +} + +} // namespace mini_installer diff --git a/chrome/installer/mini_installer/configuration.h b/chrome/installer/mini_installer/configuration.h new file mode 100644 index 0000000..b328871 --- /dev/null +++ b/chrome/installer/mini_installer/configuration.h @@ -0,0 +1,77 @@ +// Copyright (c) 2011 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_INSTALLER_MINI_INSTALLER_CONFIGURATION_H_ +#define CHROME_INSTALLER_MINI_INSTALLER_CONFIGURATION_H_ +#pragma once + +namespace mini_installer { + +// A simple container of the mini_installer's configuration, as dictated 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(); + + // Returns the desired operation dictated by the command line options. + Operation operation() const { return operation_; } + + // Returns the program portion of the command line, or NULL if it cannot be + // determined (e.g., by misuse). + const wchar_t* program() const; + + // Returns the number of arguments specified on the command line, including + // the program itself. + int argument_count() const { return argument_count_; } + + // Returns the original command line. + const wchar_t* command_line() const { return command_line_; } + + // Returns the app guid to be used for Chrome. --chrome-sxs on the command + // line makes this the canary's app guid. + const wchar_t* chrome_app_guid() const { return chrome_app_guid_; } + + // Returns true if --chrome is explicitly or implicitly on the command line. + bool has_chrome() const { return has_chrome_; } + + // Returns true if --chrome-frame is on the command line. + bool has_chrome_frame() const { return has_chrome_frame_; } + + // Returns true if --multi-install is on the command line. + bool is_multi_install() const { return is_multi_install_; } + + // Returns true if --system-level is on the command line. + bool is_system_level() const { return is_system_level_; } + + protected: + void Clear(); + bool InitializeFromCommandLine(const wchar_t* command_line); + + wchar_t** args_; + const wchar_t* chrome_app_guid_; + const wchar_t* command_line_; + int argument_count_; + Operation operation_; + bool has_chrome_; + bool has_chrome_frame_; + bool is_multi_install_; + bool is_system_level_; + + private: + Configuration(const Configuration&); + Configuration& operator=(const Configuration&); +}; + +} // namespace mini_installer + +#endif // CHROME_INSTALLER_MINI_INSTALLER_CONFIGURATION_H_ diff --git a/chrome/installer/mini_installer/configuration_test.cc b/chrome/installer/mini_installer/configuration_test.cc new file mode 100644 index 0000000..22333a1 --- /dev/null +++ b/chrome/installer/mini_installer/configuration_test.cc @@ -0,0 +1,118 @@ +// Copyright (c) 2011 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/basictypes.h" +#include "chrome/installer/mini_installer/appid.h" +#include "chrome/installer/mini_installer/configuration.h" +#include "testing/gtest/include/gtest/gtest.h" + +using mini_installer::Configuration; + +class TestConfiguration : public Configuration { + public: + explicit TestConfiguration(const wchar_t* command_line) : Configuration() { + Initialize(command_line); + } + private: + void Initialize(const wchar_t* command_line) { + ASSERT_TRUE(InitializeFromCommandLine(command_line)); + } +}; + +// Test that the operation type is CLEANUP iff --cleanup is on the cmdline. +TEST(MiniInstallerConfigurationTest, 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(MiniInstallerConfigurationTest, Program) { + EXPECT_TRUE(NULL == mini_installer::Configuration().program()); + EXPECT_TRUE(std::wstring(L"spam.exe") == + TestConfiguration(L"spam.exe").program()); + EXPECT_TRUE(std::wstring(L"spam.exe") == + TestConfiguration(L"spam.exe --with args").program()); + EXPECT_TRUE(std::wstring(L"c:\\blaz\\spam.exe") == + TestConfiguration(L"c:\\blaz\\spam.exe --with args").program()); +} + +TEST(MiniInstallerConfigurationTest, ArgumentCount) { + EXPECT_EQ(1, TestConfiguration(L"spam.exe").argument_count()); + EXPECT_EQ(2, TestConfiguration(L"spam.exe --foo").argument_count()); + EXPECT_EQ(3, TestConfiguration(L"spam.exe --foo --bar").argument_count()); +} + +TEST(MiniInstallerConfigurationTest, CommandLine) { + static const wchar_t* const kCommandLines[] = { + L"", + L"spam.exe", + L"spam.exe --foo", + }; + for (size_t i = 0; i < arraysize(kCommandLines); ++i) { + EXPECT_TRUE(std::wstring(kCommandLines[i]) == + TestConfiguration(kCommandLines[i]).command_line()); + } +} + +TEST(MiniInstallerConfigurationTest, ChromeAppGuid) { + EXPECT_TRUE(std::wstring(google_update::kAppGuid) == + TestConfiguration(L"spam.exe").chrome_app_guid()); + EXPECT_TRUE(std::wstring(google_update::kAppGuid) == + TestConfiguration(L"spam.exe --chrome").chrome_app_guid()); + EXPECT_TRUE(std::wstring(google_update::kAppGuid) == + TestConfiguration(L"spam.exe --multi-install --chrome") + .chrome_app_guid()); + EXPECT_TRUE(std::wstring(google_update::kAppGuid) == + TestConfiguration(L"spam.exe --chrome-frame").chrome_app_guid()); + EXPECT_TRUE(std::wstring(google_update::kSxSAppGuid) == + TestConfiguration(L"spam.exe --chrome-sxs").chrome_app_guid()); +} + +TEST(MiniInstallerConfigurationTest, HasChrome) { + EXPECT_TRUE(TestConfiguration(L"spam.exe").has_chrome()); + EXPECT_TRUE(TestConfiguration(L"spam.exe --chrome").has_chrome()); + EXPECT_TRUE(TestConfiguration(L"spam.exe --multi-install --chrome") + .has_chrome()); + EXPECT_FALSE(TestConfiguration(L"spam.exe --chrome-frame").has_chrome()); + EXPECT_FALSE(TestConfiguration(L"spam.exe --multi-install").has_chrome()); +} + +TEST(MiniInstallerConfigurationTest, HasChromeFrame) { + EXPECT_FALSE(TestConfiguration(L"spam.exe").has_chrome_frame()); + EXPECT_FALSE(TestConfiguration(L"spam.exe --chrome").has_chrome_frame()); + EXPECT_FALSE(TestConfiguration(L"spam.exe --multi-install --chrome") + .has_chrome_frame()); + EXPECT_TRUE(TestConfiguration(L"spam.exe --chrome-frame").has_chrome_frame()); + EXPECT_TRUE(TestConfiguration(L"spam.exe --multi-install --chrome-frame") + .has_chrome_frame()); + EXPECT_FALSE(TestConfiguration(L"spam.exe --multi-install") + .has_chrome_frame()); +} + +TEST(MiniInstallerConfigurationTest, IsMultiInstall) { + EXPECT_FALSE(TestConfiguration(L"spam.exe").is_multi_install()); + EXPECT_FALSE(TestConfiguration(L"spam.exe --chrome").is_multi_install()); + EXPECT_TRUE(TestConfiguration(L"spam.exe --multi-install --chrome") + .is_multi_install()); + EXPECT_FALSE(TestConfiguration(L"spam.exe --chrome-frame") + .is_multi_install()); + EXPECT_TRUE(TestConfiguration(L"spam.exe --multi-install --chrome-frame") + .is_multi_install()); + EXPECT_TRUE(TestConfiguration(L"spam.exe --multi-install") + .is_multi_install()); +} + +TEST(MiniInstallerConfigurationTest, 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()); +} diff --git a/chrome/installer/mini_installer/mini_installer.cc b/chrome/installer/mini_installer/mini_installer.cc index c74738a..44a17e2 100644 --- a/chrome/installer/mini_installer/mini_installer.cc +++ b/chrome/installer/mini_installer/mini_installer.cc @@ -25,6 +25,7 @@ #include <shellapi.h> #include "chrome/installer/mini_installer/appid.h" +#include "chrome/installer/mini_installer/configuration.h" #include "chrome/installer/mini_installer/decompress.h" #include "chrome/installer/mini_installer/mini_installer.h" #include "chrome/installer/mini_installer/mini_string.h" @@ -152,27 +153,24 @@ bool OpenClientStateKey(HKEY root_key, const wchar_t* app_guid, REGSAM access, (key->Open(root_key, client_state_key.get(), access) == ERROR_SUCCESS); } +// This function sets the flag in registry to indicate that Google Update +// should try full installer next time. If the current installer works, this +// flag is cleared by setup.exe at the end of install. The flag will by default +// be written to HKCU, but if --system-level is included in the command line, +// it will be written to HKLM instead. // TODO(grt): Write a unit test for this that uses registry virtualization. -void SetInstallerFlagsHelper(int args_num, const wchar_t* const* args) { - bool multi_install = false; +void SetInstallerFlags(const Configuration& configuration) { RegKey key; const REGSAM key_access = KEY_QUERY_VALUE | KEY_SET_VALUE; - const wchar_t* app_guid = google_update::kAppGuid; - HKEY root_key = HKEY_CURRENT_USER; + const HKEY root_key = + configuration.is_system_level() ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + const wchar_t* app_guid = + configuration.has_chrome_frame() ? + google_update::kChromeFrameAppGuid : + configuration.chrome_app_guid(); StackString<128> value; LONG ret; - for (int i = 1; i < args_num; ++i) { - if (0 == ::lstrcmpi(args[i], L"--chrome-sxs")) - app_guid = google_update::kSxSAppGuid; - else if (0 == ::lstrcmpi(args[i], L"--chrome-frame")) - app_guid = google_update::kChromeFrameAppGuid; - else if (0 == ::lstrcmpi(args[i], L"--multi-install")) - multi_install = true; - else if (0 == ::lstrcmpi(args[i], L"--system-level")) - root_key = HKEY_LOCAL_MACHINE; - } - // When multi_install is true, we are potentially: // 1. Performing a multi-install of some product(s) on a clean machine. // Neither the product(s) nor the multi-installer will have a ClientState @@ -185,9 +183,9 @@ void SetInstallerFlagsHelper(int args_num, const wchar_t* const* args) { // exists and its "ap" value does not contain "-multi". This is case 3, so we // modify the product's ClientState. Otherwise, we check the // multi-installer's ClientState and modify it if it exists. - if (multi_install) { + if (configuration.is_multi_install()) { if (OpenClientStateKey(root_key, app_guid, key_access, &key)) { - // The app is installed. See if it's a single-install. + // The product has a client state key. See if it's a single-install. ret = key.ReadValue(kApRegistryValueName, value.get(), value.capacity()); if (ret != ERROR_FILE_NOT_FOUND && (ret != ERROR_SUCCESS || @@ -212,68 +210,57 @@ void SetInstallerFlagsHelper(int args_num, const wchar_t* const* args) { } // The conditions below are handling two cases: - // 1. When ap key is present, we want to add the required tags only if they - // are not present. - // 2. When ap key is missing, we are going to create it with the required - // tags. + // 1. When ap value is present, we want to add the required tag only if it is + // not present. + // 2. When ap value is missing, we are going to create it with the required + // tag. if ((ret == ERROR_SUCCESS) || (ret == ERROR_FILE_NOT_FOUND)) { if (ret == ERROR_FILE_NOT_FOUND) value.clear(); - bool success = true; - - if (multi_install && - !FindTagInStr(value.get(), kMultifailInstallerSuffix, NULL)) { - // We want -multifail to immediately precede -full. Chop off the latter - // if it's already present so that we can simply do two appends. - if (StrEndsWith(value.get(), kFullInstallerSuffix)) { - size_t suffix_len = (arraysize(kFullInstallerSuffix) - 1); - size_t value_len = value.length(); - value.truncate_at(value_len - suffix_len); - } - success = value.append(kMultifailInstallerSuffix); - } - if (success && !StrEndsWith(value.get(), kFullInstallerSuffix) && - (value.append(kFullInstallerSuffix))) { + if (!StrEndsWith(value.get(), kFullInstallerSuffix) && + value.append(kFullInstallerSuffix)) { key.WriteValue(kApRegistryValueName, value.get()); } } } -// This function sets the flag in registry to indicate that Google Update -// should try full installer next time. If the current installer works, this -// flag is cleared by setup.exe at the end of install. The flag will by default -// be written to HKCU, but if --system-level is included in the command line, -// it will be written to HKLM instead. -void SetInstallerFlags() { - int args_num; - wchar_t* cmd_line = ::GetCommandLine(); - wchar_t** args = ::CommandLineToArgvW(cmd_line, &args_num); - - SetInstallerFlagsHelper(args_num, args); - - ::LocalFree(args); -} - // Gets the setup.exe path from Registry by looking the value of Uninstall -// string, strips the arguments for uninstall and returns only the full path -// to setup.exe. |size| is measured in wchar_t units. -bool GetSetupExePathFromRegistry(wchar_t* path, size_t size) { - if (!ReadValueFromRegistry(HKEY_CURRENT_USER, kUninstallRegistryKey, - kUninstallRegistryValueName, path, size)) { - if (!ReadValueFromRegistry(HKEY_LOCAL_MACHINE, kUninstallRegistryKey, - kUninstallRegistryValueName, path, size)) { - return false; - } +// string. |size| is measured in wchar_t units. +bool GetSetupExePathFromRegistry(const Configuration& configuration, + wchar_t* path, + size_t size) { + const HKEY root_key = + configuration.is_system_level() ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + RegKey key; + bool succeeded = false; + + // If this is a multi install, first try looking in the binaries for the path. + if (configuration.is_multi_install() && + OpenClientStateKey(root_key, google_update::kMultiInstallAppGuid, + KEY_QUERY_VALUE, &key)) { + succeeded = (key.ReadValue(kUninstallRegistryValueName, path, + size) == ERROR_SUCCESS); } - wchar_t* tmp = const_cast<wchar_t*>(SearchStringI(path, L" --")); - if (tmp) { - *tmp = L'\0'; - } else { - return false; + + // Failing that, look in Chrome Frame's client state key --chrome-frame was + // specified. + if (!succeeded && configuration.has_chrome_frame() && + OpenClientStateKey(root_key, google_update::kChromeFrameAppGuid, + KEY_QUERY_VALUE, &key)) { + succeeded = (key.ReadValue(kUninstallRegistryValueName, path, + size) == ERROR_SUCCESS); } - return true; + // Make a last-ditch effort to look in Chrome's client state key. + if (!succeeded && + OpenClientStateKey(root_key, configuration.chrome_app_guid(), + KEY_QUERY_VALUE, &key)) { + succeeded = (key.ReadValue(kUninstallRegistryValueName, path, + size) == ERROR_SUCCESS); + } + + return succeeded; } // Calls CreateProcess with good default parameters and waits for the process @@ -308,7 +295,8 @@ bool RunProcessAndWait(const wchar_t* exe_path, wchar_t* cmdline, // Append any command line params passed to mini_installer to the given buffer // so that they can be passed on to setup.exe. We do not return any error from // this method and simply skip making any changes in case of error. -void AppendCommandLineFlags(CommandString* buffer) { +void AppendCommandLineFlags(const Configuration& configuration, + CommandString* buffer) { PathString full_exe_path; size_t len = ::GetModuleFileName(NULL, full_exe_path.get(), full_exe_path.capacity()); @@ -319,26 +307,18 @@ void AppendCommandLineFlags(CommandString* buffer) { if (exe_name == NULL) return; - int args_num; - wchar_t* cmd_line = ::GetCommandLine(); - wchar_t** args = ::CommandLineToArgvW(cmd_line, &args_num); - if (args_num <= 0) - return; - const wchar_t* cmd_to_append = L""; - if (!StrEndsWith(args[0], exe_name)) { + if (!StrEndsWith(configuration.program(), exe_name)) { // Current executable name not in the command line so just append // the whole command line. - cmd_to_append = cmd_line; - } else if (args_num > 1) { - const wchar_t* tmp = SearchStringI(cmd_line, exe_name); + cmd_to_append = configuration.command_line(); + } else if (configuration.argument_count() > 1) { + const wchar_t* tmp = SearchStringI(configuration.command_line(), exe_name); tmp = SearchStringI(tmp, L" "); cmd_to_append = tmp; } buffer->append(cmd_to_append); - - LocalFree(args); } @@ -390,8 +370,9 @@ BOOL CALLBACK OnResourceFound(HMODULE module, const wchar_t* type, // If setup.exe is present in more than one form, the precedence order is // BN < BL < B7 // For more details see chrome/tools/build/win/create_installer_archive.py. -bool UnpackBinaryResources(HMODULE module, const wchar_t* base_path, - PathString* archive_path, PathString* setup_path) { +bool UnpackBinaryResources(const Configuration& configuration, HMODULE module, + const wchar_t* base_path, PathString* archive_path, + PathString* setup_path) { // Generate the setup.exe path where we patch/uncompress setup resource. PathString setup_dest_path; if (!setup_dest_path.assign(base_path) || @@ -419,7 +400,8 @@ bool UnpackBinaryResources(HMODULE module, const wchar_t* base_path, CommandString cmd_line; // Get the path to setup.exe first. bool success = true; - if (!GetSetupExePathFromRegistry(cmd_line.get(), cmd_line.capacity()) || + if (!GetSetupExePathFromRegistry(configuration, cmd_line.get(), + cmd_line.capacity()) || !cmd_line.append(kCmdUpdateSetupExe) || !cmd_line.append(L"=\"") || !cmd_line.append(setup_path->get()) || @@ -435,7 +417,7 @@ bool UnpackBinaryResources(HMODULE module, const wchar_t* base_path, // on to setup.exe. This is important since switches such as // --multi-install and --chrome-frame affect where setup.exe will write // installer results for consumption by Google Update. - AppendCommandLineFlags(&cmd_line); + AppendCommandLineFlags(configuration, &cmd_line); int exit_code = 0; if (success && @@ -498,8 +480,8 @@ bool UnpackBinaryResources(HMODULE module, const wchar_t* base_path, } // Executes setup.exe, waits for it to finish and returns the exit code. -bool RunSetup(const wchar_t* archive_path, const wchar_t* setup_path, - int* exit_code) { +bool RunSetup(const Configuration& configuration, const wchar_t* archive_path, + const wchar_t* setup_path, int* exit_code) { // There could be three full paths in the command line for setup.exe (path // to exe itself, path to archive and path to log file), so we declare // total size as three + one additional to hold command line options. @@ -511,7 +493,7 @@ bool RunSetup(const wchar_t* archive_path, const wchar_t* setup_path, !cmd_line.append(setup_path) || !cmd_line.append(L"\"")) return false; - } else if (!GetSetupExePathFromRegistry(cmd_line.get(), + } else if (!GetSetupExePathFromRegistry(configuration, cmd_line.get(), cmd_line.capacity())) { return false; } @@ -525,7 +507,7 @@ bool RunSetup(const wchar_t* archive_path, const wchar_t* setup_path, // Get any command line option specified for mini_installer and pass them // on to setup.exe - AppendCommandLineFlags(&cmd_line); + AppendCommandLineFlags(configuration, &cmd_line); return RunProcessAndWait(NULL, cmd_line.get(), exit_code); } @@ -725,20 +707,19 @@ void DeleteOldChromeTempDirectories() { // If the function returns true, the command line has been processed and all // required actions taken. The installer must exit and return the returned // |exit_code|. -bool ProcessMiniInstallerCommandLine(int* exit_code) { +bool ProcessNonInstallOperations(const Configuration& configuration, + int* exit_code) { bool ret = false; - const wchar_t* cmd_line = ::GetCommandLineW(); - int num_args = 0; - wchar_t** args = ::CommandLineToArgvW(cmd_line, &num_args); - if (args) { - for (int i = 1; i < num_args && !ret; ++i) { - // Currently there's only one mini installer specific switch defined. - if (lstrcmpiW(args[i], kMiniCmdCleanup) == 0) { - *exit_code = 0; - ret = true; - } - } - ::LocalFree(args); + + switch (configuration.operation()) { + case Configuration::CLEANUP: + // Cleanup has already taken place in DeleteOldChromeTempDirectories at + // this point, so just tell our caller to exit early. + *exit_code = 0; + ret = true; + break; + + default: break; } return ret; @@ -772,10 +753,18 @@ int WMain(HMODULE module) { // so this could buy us some space. DeleteOldChromeTempDirectories(); + // TODO(grt): Make the exit codes more granular so we know where the popular + // errors truly are. + int exit_code = 101; + + // Parse the command line. + Configuration configuration; + if (!configuration.Initialize()) + return exit_code; + // If the --cleanup switch was specified on the command line, then that means // we should only do the cleanup and then exit. - int exit_code = 101; - if (ProcessMiniInstallerCommandLine(&exit_code)) + if (ProcessNonInstallOperations(configuration, &exit_code)) return exit_code; // First get a path where we can extract payload @@ -788,21 +777,23 @@ int WMain(HMODULE module) { // any errors here and we try to set the suffix for user level unless // --system-level is on the command line in which case we set it for system // level instead. This only applies to the Google Chrome distribution. - SetInstallerFlags(); + SetInstallerFlags(configuration); #endif PathString archive_path; PathString setup_path; - if (!UnpackBinaryResources(module, base_path.get(), &archive_path, - &setup_path)) { + if (!UnpackBinaryResources(configuration, module, base_path.get(), + &archive_path, &setup_path)) { exit_code = 102; } else { // While unpacking the binaries, we paged in a whole bunch of memory that // we don't need anymore. Let's give it back to the pool before running // setup. ::SetProcessWorkingSetSize(::GetCurrentProcess(), -1, -1); - if (!RunSetup(archive_path.get(), setup_path.get(), &exit_code)) + if (!RunSetup(configuration, archive_path.get(), setup_path.get(), + &exit_code)) { exit_code = 103; + } } if (ShouldDeleteExtractedFiles()) diff --git a/chrome/installer/mini_installer/mini_installer.h b/chrome/installer/mini_installer/mini_installer.h index 27cea55..2c2ed9e 100644 --- a/chrome/installer/mini_installer/mini_installer.h +++ b/chrome/installer/mini_installer/mini_installer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -18,22 +18,11 @@ const wchar_t kCmdInstallArchive[] = L" --install-archive"; const wchar_t kCmdUpdateSetupExe[] = L" --update-setup-exe"; const wchar_t kCmdNewSetupExe[] = L" --new-setup-exe"; -// Command line arguments specific only to the mini installer. -// Note that these constants differ from the kCmdXxx constants above in that -// they do not have leading whitespace. -// Pass --cleanup to the mini installer to delete temporary directories that -// might be left over from previous installation and then exit (i.e. do not -// extract and run setup.exe). -const wchar_t kMiniCmdCleanup[] = L"--cleanup"; - // Temp directory prefix that this process creates const wchar_t kTempPrefix[] = L"CR_"; // Google Update will use the full installer if this suffix is found in the ap // value. const wchar_t kFullInstallerSuffix[] = L"-full"; -// Google Update will use the single-install configuration if this suffix is -// found in the ap value. -const wchar_t kMultifailInstallerSuffix[] = L"-multifail"; // ap value tag for a multi-install product. const wchar_t kMultiInstallTag[] = L"-multi"; diff --git a/chrome/installer/setup/install_worker.cc b/chrome/installer/setup/install_worker.cc index 717a869..8027f43 100644 --- a/chrome/installer/setup/install_worker.cc +++ b/chrome/installer/setup/install_worker.cc @@ -125,7 +125,9 @@ void AddUninstallShortcutWorkItems(const InstallerState& installer_state, // in the Google Update client state key. We do this even for non-MSI // managed installs to avoid breaking the edge case whereby an MSI-managed // install is updated by a non-msi installer (which would confuse the MSI - // machinery if these strings were not also updated). + // machinery if these strings were not also updated). The UninstallString + // value placed in the client state key is also used by the mini_installer to + // locate the setup.exe instance used for binary patching. // Do not quote the command line for the MSI invocation. FilePath install_path(installer_state.target_path()); FilePath installer_path(installer_state.GetInstallerDirectory(new_version)); @@ -211,6 +213,28 @@ void AddUninstallShortcutWorkItems(const InstallerState& installer_state, } } +// Add uninstall-related work items for multi-install scenarios. +void AddMultiUninstallWorkItems(const InstallerState& installer_state, + const FilePath& setup_path, + const Version& new_version, + WorkItemList* install_list) { + DCHECK(installer_state.is_multi_install()); + + // The mini_installer needs a reliable way to locate setup.exe for diff + // updates. For single-installs, the product's ClientState key is consulted + // (Chrome's or Chrome Frame's). For multi-installs, the binaries' key is + // used. + const HKEY reg_root = installer_state.root_key(); + std::wstring binaries_state_key( + installer_state.multi_package_binaries_distribution()->GetStateKey()); + FilePath installer_path( + installer_state.GetInstallerDirectory(new_version) + .Append(setup_path.BaseName())); + install_list->AddCreateRegKeyWorkItem(reg_root, binaries_state_key); + install_list->AddSetRegValueWorkItem(reg_root, binaries_state_key, + installer::kUninstallStringField, installer_path.value(), true); +} + // Create Version key for a product (if not already present) and sets the new // product version as the last step. void AddVersionKeyWorkItems(HKEY root, @@ -743,6 +767,9 @@ void AddInstallWorkItems(const InstallationState& original_state, } if (installer_state.is_multi_install()) { + AddMultiUninstallWorkItems(installer_state, setup_path, new_version, + install_list); + AddVersionKeyWorkItems(root, installer_state.multi_package_binaries_distribution(), new_version, install_list); |