普通文本  |  1816行  |  78.51 KB

// 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/installer/setup/setup_main.h"

#include <windows.h>
#include <msi.h>
#include <shellapi.h>
#include <shlobj.h>

#include <string>

#include "base/at_exit.h"
#include "base/basictypes.h"
#include "base/command_line.h"
#include "base/file_util.h"
#include "base/file_version_info.h"
#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
#include "base/path_service.h"
#include "base/process/launch.h"
#include "base/strings/string16.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "base/win/registry.h"
#include "base/win/scoped_com_initializer.h"
#include "base/win/scoped_comptr.h"
#include "base/win/scoped_handle.h"
#include "base/win/win_util.h"
#include "base/win/windows_version.h"
#include "breakpad/src/client/windows/handler/exception_handler.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/installer/setup/archive_patch_helper.h"
#include "chrome/installer/setup/install.h"
#include "chrome/installer/setup/install_worker.h"
#include "chrome/installer/setup/setup_constants.h"
#include "chrome/installer/setup/setup_util.h"
#include "chrome/installer/setup/uninstall.h"
#include "chrome/installer/util/browser_distribution.h"
#include "chrome/installer/util/channel_info.h"
#include "chrome/installer/util/delete_after_reboot_helper.h"
#include "chrome/installer/util/delete_tree_work_item.h"
#include "chrome/installer/util/eula_util.h"
#include "chrome/installer/util/google_update_constants.h"
#include "chrome/installer/util/google_update_settings.h"
#include "chrome/installer/util/google_update_util.h"
#include "chrome/installer/util/helper.h"
#include "chrome/installer/util/html_dialog.h"
#include "chrome/installer/util/install_util.h"
#include "chrome/installer/util/installation_state.h"
#include "chrome/installer/util/installation_validator.h"
#include "chrome/installer/util/installer_state.h"
#include "chrome/installer/util/l10n_string_util.h"
#include "chrome/installer/util/logging_installer.h"
#include "chrome/installer/util/lzma_util.h"
#include "chrome/installer/util/master_preferences.h"
#include "chrome/installer/util/master_preferences_constants.h"
#include "chrome/installer/util/self_cleaning_temp_dir.h"
#include "chrome/installer/util/shell_util.h"
#include "chrome/installer/util/user_experiment.h"

#include "installer_util_strings.h"  // NOLINT

using installer::InstallerState;
using installer::InstallationState;
using installer::InstallationValidator;
using installer::MasterPreferences;
using installer::Product;
using installer::ProductState;
using installer::Products;

const wchar_t kChromePipeName[] = L"\\\\.\\pipe\\ChromeCrashServices";
const wchar_t kGoogleUpdatePipeName[] = L"\\\\.\\pipe\\GoogleCrashServices\\";
const wchar_t kSystemPrincipalSid[] = L"S-1-5-18";

const MINIDUMP_TYPE kLargerDumpType = static_cast<MINIDUMP_TYPE>(
    MiniDumpWithProcessThreadData |  // Get PEB and TEB.
    MiniDumpWithUnloadedModules |  // Get unloaded modules when available.
    MiniDumpWithIndirectlyReferencedMemory);  // Get memory referenced by stack.

namespace {

// Returns NULL if no compressed archive is available for processing, otherwise
// returns a patch helper configured to uncompress and patch.
scoped_ptr<installer::ArchivePatchHelper> CreateChromeArchiveHelper(
    const CommandLine& command_line,
    const installer::InstallerState& installer_state,
    const base::FilePath& working_directory) {
  // A compressed archive is ordinarily given on the command line by the mini
  // installer. If one was not given, look for chrome.packed.7z next to the
  // running program.
  base::FilePath compressed_archive(
      command_line.GetSwitchValuePath(installer::switches::kInstallArchive));
  bool compressed_archive_specified = !compressed_archive.empty();
  if (!compressed_archive_specified) {
    compressed_archive =
        command_line.GetProgram().DirName().Append(
            installer::kChromeCompressedArchive);
  }

  // Fail if no compressed archive is found.
  if (!base::PathExists(compressed_archive)) {
    if (compressed_archive_specified) {
      LOG(ERROR) << installer::switches::kInstallArchive << "="
                 << compressed_archive.value() << " not found.";
    }
    return scoped_ptr<installer::ArchivePatchHelper>();
  }

  // chrome.7z is either extracted directly from the compressed archive into the
  // working dir or is the target of patching in the working dir.
  base::FilePath target(working_directory.Append(installer::kChromeArchive));
  DCHECK(!base::PathExists(target));

  // Specify an empty path for the patch source since it isn't yet known that
  // one is needed. It will be supplied in UncompressAndPatchChromeArchive if it
  // is.
  return scoped_ptr<installer::ArchivePatchHelper>(
      new installer::ArchivePatchHelper(working_directory,
                                        compressed_archive,
                                        base::FilePath(),
                                        target));
}

// Workhorse for producing an uncompressed archive (chrome.7z) given a
// chrome.packed.7z containing either a patch file based on the version of
// chrome being updated or the full uncompressed archive. Returns true on
// success, in which case |archive_type| is populated based on what was found.
// Returns false on failure, in which case |install_status| contains the error
// code and the result is written to the registry (via WriteInstallerResult).
bool UncompressAndPatchChromeArchive(
    const installer::InstallationState& original_state,
    const installer::InstallerState& installer_state,
    installer::ArchivePatchHelper* archive_helper,
    installer::ArchiveType* archive_type,
    installer::InstallStatus* install_status) {
  installer_state.UpdateStage(installer::UNCOMPRESSING);
  if (!archive_helper->Uncompress(NULL)) {
    *install_status = installer::UNCOMPRESSION_FAILED;
    installer_state.WriteInstallerResult(*install_status,
                                         IDS_INSTALL_UNCOMPRESSION_FAILED_BASE,
                                         NULL);
    return false;
  }

  // Short-circuit if uncompression produced the uncompressed archive rather
  // than a patch file.
  if (base::PathExists(archive_helper->target())) {
    *archive_type = installer::FULL_ARCHIVE_TYPE;
    return true;
  }

  // Find the installed version's archive to serve as the source for patching.
  base::FilePath patch_source(installer::FindArchiveToPatch(original_state,
                                                            installer_state));
  if (patch_source.empty()) {
    LOG(ERROR) << "Failed to find archive to patch.";
    *install_status = installer::DIFF_PATCH_SOURCE_MISSING;
    installer_state.WriteInstallerResult(*install_status,
                                         IDS_INSTALL_UNCOMPRESSION_FAILED_BASE,
                                         NULL);
    return false;
  }
  archive_helper->set_patch_source(patch_source);

  // Try courgette first. Failing that, try bspatch.
  if ((installer_state.UpdateStage(installer::ENSEMBLE_PATCHING),
       !archive_helper->EnsemblePatch()) &&
      (installer_state.UpdateStage(installer::BINARY_PATCHING),
       !archive_helper->BinaryPatch())) {
    *install_status = installer::APPLY_DIFF_PATCH_FAILED;
    installer_state.WriteInstallerResult(*install_status,
                                         IDS_INSTALL_UNCOMPRESSION_FAILED_BASE,
                                         NULL);
    return false;
  }

  *archive_type = installer::INCREMENTAL_ARCHIVE_TYPE;
  return true;
}

// In multi-install, adds all products to |installer_state| that are
// multi-installed and must be updated along with the products already present
// in |installer_state|.
void AddExistingMultiInstalls(const InstallationState& original_state,
                              InstallerState* installer_state) {
  if (installer_state->is_multi_install()) {
    for (size_t i = 0; i < BrowserDistribution::NUM_TYPES; ++i) {
      BrowserDistribution::Type type =
          static_cast<BrowserDistribution::Type>(i);

      if (!installer_state->FindProduct(type)) {
        const ProductState* state =
            original_state.GetProductState(installer_state->system_install(),
                                           type);
        if ((state != NULL) && state->is_multi_install()) {
          installer_state->AddProductFromState(type, *state);
          VLOG(1) << "Product already installed and must be included: "
                  << BrowserDistribution::GetSpecificDistribution(type)->
                         GetDisplayName();
        }
      }
    }
  }
}

// This function is called when --rename-chrome-exe option is specified on
// setup.exe command line. This function assumes an in-use update has happened
// for Chrome so there should be a file called new_chrome.exe on the file
// system and a key called 'opv' in the registry. This function will move
// new_chrome.exe to chrome.exe and delete 'opv' key in one atomic operation.
// This function also deletes elevation policies associated with the old version
// if they exist.
installer::InstallStatus RenameChromeExecutables(
    const InstallationState& original_state,
    InstallerState* installer_state) {
  // See what products are already installed in multi mode.  When we do the
  // rename for multi installs, we must update all installations since they
  // share the binaries.
  AddExistingMultiInstalls(original_state, installer_state);
  const base::FilePath &target_path = installer_state->target_path();
  base::FilePath chrome_exe(target_path.Append(installer::kChromeExe));
  base::FilePath chrome_new_exe(target_path.Append(installer::kChromeNewExe));
  base::FilePath chrome_old_exe(target_path.Append(installer::kChromeOldExe));

  // Create a temporary backup directory on the same volume as chrome.exe so
  // that moving in-use files doesn't lead to trouble.
  installer::SelfCleaningTempDir temp_path;
  if (!temp_path.Initialize(target_path.DirName(),
                            installer::kInstallTempDir)) {
    PLOG(ERROR) << "Failed to create Temp directory "
                << target_path.DirName()
                       .Append(installer::kInstallTempDir).value();
    return installer::RENAME_FAILED;
  }
  scoped_ptr<WorkItemList> install_list(WorkItem::CreateWorkItemList());
  // Move chrome.exe to old_chrome.exe, then move new_chrome.exe to chrome.exe.
  install_list->AddMoveTreeWorkItem(chrome_exe.value(),
                                    chrome_old_exe.value(),
                                    temp_path.path().value(),
                                    WorkItem::ALWAYS_MOVE);
  install_list->AddMoveTreeWorkItem(chrome_new_exe.value(),
                                    chrome_exe.value(),
                                    temp_path.path().value(),
                                    WorkItem::ALWAYS_MOVE);
  install_list->AddDeleteTreeWorkItem(chrome_new_exe, temp_path.path());
  // old_chrome.exe is still in use in most cases, so ignore failures here.
  install_list->AddDeleteTreeWorkItem(chrome_old_exe, temp_path.path())->
      set_ignore_failure(true);

  // Add work items to delete the "opv", "cpv", and "cmd" values from all
  // products we're operating on (which including the multi-install binaries).
  const Products& products = installer_state->products();
  HKEY reg_root = installer_state->root_key();
  string16 version_key;
  for (Products::const_iterator it = products.begin(); it < products.end();
       ++it) {
    version_key = (*it)->distribution()->GetVersionKey();
    install_list->AddDeleteRegValueWorkItem(
        reg_root, version_key, google_update::kRegOldVersionField);
    install_list->AddDeleteRegValueWorkItem(
        reg_root, version_key, google_update::kRegCriticalVersionField);
    install_list->AddDeleteRegValueWorkItem(
        reg_root, version_key, google_update::kRegRenameCmdField);
  }
  installer::InstallStatus ret = installer::RENAME_SUCCESSFUL;
  if (!install_list->Do()) {
    LOG(ERROR) << "Renaming of executables failed. Rolling back any changes.";
    install_list->Rollback();
    ret = installer::RENAME_FAILED;
  }
  // temp_path's dtor will take care of deleting or scheduling itself for
  // deletion at reboot when this scope closes.
  VLOG(1) << "Deleting temporary directory " << temp_path.path().value();

  return ret;
}

// For each product that is being updated (i.e., already installed at an earlier
// version), see if that product has an update policy override that differs from
// that for the binaries.  If any are found, fail with an error indicating that
// the Group Policy settings are in an inconsistent state.  Do not do this test
// for same-version installs, since it would be unkind to block attempts to
// repair a corrupt installation.  This function returns false when installation
// should be halted, in which case |status| contains the relevant exit code and
// the proper installer result has been written to the registry.
bool CheckGroupPolicySettings(const InstallationState& original_state,
                              const InstallerState& installer_state,
                              const Version& new_version,
                              installer::InstallStatus* status) {
#if !defined(GOOGLE_CHROME_BUILD)
  // Chromium builds are not updated via Google Update, so there are no
  // Group Policy settings to consult.
  return true;
#else
  DCHECK(status);

  // Single installs are always in good shape.
  if (!installer_state.is_multi_install())
    return true;

  bool settings_are_valid = true;
  const bool is_system_install = installer_state.system_install();
  BrowserDistribution* const binaries_dist =
      installer_state.multi_package_binaries_distribution();

  // Get the update policy for the binaries.
  const GoogleUpdateSettings::UpdatePolicy binaries_policy =
      GoogleUpdateSettings::GetAppUpdatePolicy(binaries_dist->GetAppGuid(),
                                               NULL);

  // Check for differing update policies for all of the products being updated.
  const Products& products = installer_state.products();
  Products::const_iterator scan = products.begin();
  for (Products::const_iterator end = products.end(); scan != end; ++scan) {
    BrowserDistribution* dist = (*scan)->distribution();
    const ProductState* product_state =
        original_state.GetProductState(is_system_install, dist->GetType());
    // Is an earlier version of this product already installed?
    if (product_state != NULL &&
        product_state->version().CompareTo(new_version) < 0) {
      bool is_overridden = false;
      GoogleUpdateSettings::UpdatePolicy app_policy =
          GoogleUpdateSettings::GetAppUpdatePolicy(dist->GetAppGuid(),
                                                   &is_overridden);
      if (is_overridden && app_policy != binaries_policy) {
        LOG(ERROR) << "Found legacy Group Policy setting for "
                   << dist->GetDisplayName() << " (value: " << app_policy
                   << ") that does not match the setting for "
                   << binaries_dist->GetDisplayName()
                   << " (value: " << binaries_policy << ").";
        settings_are_valid = false;
      }
    }
  }

  if (!settings_are_valid) {
    // TODO(grt): add " See http://goo.gl/+++ for details." to the end of this
    // log message and to the IDS_INSTALL_INCONSISTENT_UPDATE_POLICY string once
    // we have a help center article that explains why this error is being
    // reported and how to resolve it.
    LOG(ERROR) << "Cannot apply update on account of inconsistent "
                  "Google Update Group Policy settings. Use the Group Policy "
                  "Editor to set the update policy override for the "
               << binaries_dist->GetDisplayName()
               << " application and try again.";
    *status = installer::INCONSISTENT_UPDATE_POLICY;
    installer_state.WriteInstallerResult(
        *status, IDS_INSTALL_INCONSISTENT_UPDATE_POLICY_BASE, NULL);
  }

  return settings_are_valid;
#endif  // defined(GOOGLE_CHROME_BUILD)
}

// If only the binaries are being updated, fail.
// If any product is being installed in single-mode that already exists in
// multi-mode, fail.
bool CheckMultiInstallConditions(const InstallationState& original_state,
                                 InstallerState* installer_state,
                                 installer::InstallStatus* status) {
  const Products& products = installer_state->products();
  DCHECK(products.size());

  const bool system_level = installer_state->system_install();

  if (installer_state->is_multi_install()) {
    const Product* chrome =
        installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER);
    const Product* app_host =
        installer_state->FindProduct(BrowserDistribution::CHROME_APP_HOST);
    const Product* binaries =
        installer_state->FindProduct(BrowserDistribution::CHROME_BINARIES);
    const ProductState* chrome_state =
        original_state.GetProductState(system_level,
                                       BrowserDistribution::CHROME_BROWSER);

    if (binaries) {
      if (products.size() == 1) {
        // There are no products aside from the binaries, so there is no update
        // to be applied. This can happen after multi-install Chrome Frame is
        // migrated to single-install. This is treated as an update failure
        // unless the binaries are not in-use, in which case they will be
        // uninstalled and success will be reported (see handling in wWinMain).
        VLOG(1) << "No products to be updated.";
        *status = installer::UNUSED_BINARIES;
        installer_state->WriteInstallerResult(*status, 0, NULL);
        return false;
      }
    } else {
      // This will only be hit if --multi-install is given with no products, or
      // if the app host is being installed and doesn't need the binaries at
      // user-level.
      // The former case might be due to a request by an orphaned Application
      // Host to re-install the binaries. Thus we add them to the installation.
      // The latter case is fine and we let it be.
      // If this is not an app host install and the binaries are not already
      // present, the installation will fail later due to a lack of products to
      // install.
      if (app_host && !chrome && !chrome_state) {
        DCHECK(!system_level);
        // App Host may use Chrome/Chrome binaries at system-level.
        if (original_state.GetProductState(
                true,  // system
                BrowserDistribution::CHROME_BROWSER) ||
            original_state.GetProductState(
                true,  // system
                BrowserDistribution::CHROME_BINARIES)) {
          VLOG(1) << "Installing/updating App Launcher without binaries.";
        } else {
          // Somehow the binaries were present when the quick-enable app host
          // command was run, but now they appear to be missing.
          // Force binaries to be installed/updated.
          scoped_ptr<Product> binaries_to_add(new Product(
              BrowserDistribution::GetSpecificDistribution(
                  BrowserDistribution::CHROME_BINARIES)));
          binaries_to_add->SetOption(installer::kOptionMultiInstall, true);
          binaries = installer_state->AddProduct(&binaries_to_add);
          VLOG(1) <<
              "Adding binaries for pre-existing App Launcher installation.";
        }
      }

      return true;
    }

    if (!chrome && chrome_state) {
      // A product other than Chrome is being installed in multi-install mode,
      // and Chrome is already present. Add Chrome to the set of products
      // (making it multi-install in the process) so that it is updated, too.
      scoped_ptr<Product> multi_chrome(new Product(
          BrowserDistribution::GetSpecificDistribution(
              BrowserDistribution::CHROME_BROWSER)));
      multi_chrome->SetOption(installer::kOptionMultiInstall, true);
      chrome = installer_state->AddProduct(&multi_chrome);
      VLOG(1) << "Upgrading existing Chrome browser in multi-install mode.";
    }
  } else {
    // This is a non-multi installation.

    // Check for an existing installation of the product.
    const ProductState* product_state = original_state.GetProductState(
        system_level, products[0]->distribution()->GetType());
    if (product_state != NULL) {
      // Block downgrades from multi-install to single-install.
      if (product_state->is_multi_install()) {
        LOG(ERROR) << "Multi-install "
                   << products[0]->distribution()->GetDisplayName()
                   << " exists; aborting single install.";
        *status = installer::MULTI_INSTALLATION_EXISTS;
        installer_state->WriteInstallerResult(*status,
            IDS_INSTALL_MULTI_INSTALLATION_EXISTS_BASE, NULL);
        return false;
      }
    }
  }

  return true;
}

// Checks app host pre-install conditions, specifically that this is a
// user-level multi-install.  When the pre-install conditions are not
// satisfied, the result is written to the registry (via WriteInstallerResult),
// |status| is set appropriately, and false is returned.
bool CheckAppHostPreconditions(const InstallationState& original_state,
                               InstallerState* installer_state,
                               installer::InstallStatus* status) {
  if (installer_state->FindProduct(BrowserDistribution::CHROME_APP_HOST)) {
    if (!installer_state->is_multi_install()) {
      LOG(DFATAL) << "App Launcher requires multi install";
      *status = installer::APP_HOST_REQUIRES_MULTI_INSTALL;
      // No message string since there is nothing a user can do.
      installer_state->WriteInstallerResult(*status, 0, NULL);
      return false;
    }

    if (installer_state->system_install()) {
      LOG(DFATAL) << "App Launcher may only be installed at user-level.";
      *status = installer::APP_HOST_REQUIRES_USER_LEVEL;
      // No message string since there is nothing a user can do.
      installer_state->WriteInstallerResult(*status, 0, NULL);
      return false;
    }
  }

  return true;
}

// Checks for compatibility between the current state of the system and the
// desired operation.  Also applies policy that mutates the desired operation;
// specifically, the |installer_state| object.
// Also blocks simultaneous user-level and system-level installs.  In the case
// of trying to install user-level Chrome when system-level exists, the
// existing system-level Chrome is launched.
// When the pre-install conditions are not satisfied, the result is written to
// the registry (via WriteInstallerResult), |status| is set appropriately, and
// false is returned.
bool CheckPreInstallConditions(const InstallationState& original_state,
                               InstallerState* installer_state,
                               installer::InstallStatus* status) {
  if (!CheckAppHostPreconditions(original_state, installer_state, status)) {
    DCHECK_NE(*status, installer::UNKNOWN_STATUS);
    return false;
  }

  // See what products are already installed in multi mode.  When we do multi
  // installs, we must upgrade all installations since they share the binaries.
  AddExistingMultiInstalls(original_state, installer_state);

  if (!CheckMultiInstallConditions(original_state, installer_state, status)) {
    DCHECK_NE(*status, installer::UNKNOWN_STATUS);
    return false;
  }

  const Products& products = installer_state->products();
  if (products.empty()) {
    // We haven't been given any products on which to operate.
    LOG(ERROR)
        << "Not given any products to install and no products found to update.";
    *status = installer::CHROME_NOT_INSTALLED;
    installer_state->WriteInstallerResult(*status,
        IDS_INSTALL_NO_PRODUCTS_TO_UPDATE_BASE, NULL);
    return false;
  }

  if (!installer_state->system_install()) {
    // This is a user-level installation. Make sure that we are not installing
    // on top of an existing system-level installation.
    for (Products::const_iterator it = products.begin(); it < products.end();
         ++it) {
      const Product& product = **it;
      BrowserDistribution* browser_dist = product.distribution();

      // Skip over the binaries, as it's okay for them to be at both levels
      // for different products.
      if (browser_dist->GetType() == BrowserDistribution::CHROME_BINARIES)
        continue;

      const ProductState* user_level_product_state =
          original_state.GetProductState(false, browser_dist->GetType());
      const ProductState* system_level_product_state =
          original_state.GetProductState(true, browser_dist->GetType());

      // Allow upgrades to proceed so that out-of-date versions are not left
      // around.
      if (user_level_product_state)
        continue;

      // This is a new user-level install...

      if (system_level_product_state) {
        // ... and the product already exists at system-level.
        LOG(ERROR) << "Already installed version "
                   << system_level_product_state->version().GetString()
                   << " at system-level conflicts with this one at user-level.";
        if (product.is_chrome()) {
          // Instruct Google Update to launch the existing system-level Chrome.
          // There should be no error dialog.
          base::FilePath install_path(installer::GetChromeInstallPath(
              true,  // system
              browser_dist));
          if (install_path.empty()) {
            // Give up if we failed to construct the install path.
            *status = installer::OS_ERROR;
            installer_state->WriteInstallerResult(*status,
                                                  IDS_INSTALL_OS_ERROR_BASE,
                                                  NULL);
          } else {
            *status = installer::EXISTING_VERSION_LAUNCHED;
            base::FilePath chrome_exe =
                install_path.Append(installer::kChromeExe);
            CommandLine cmd(chrome_exe);
            cmd.AppendSwitch(switches::kForceFirstRun);
            installer_state->WriteInstallerResult(*status, 0, NULL);
            VLOG(1) << "Launching existing system-level chrome instead.";
            base::LaunchProcess(cmd, base::LaunchOptions(), NULL);
          }
        } else {
          // Display an error message for other products.
          *status = installer::SYSTEM_LEVEL_INSTALL_EXISTS;
          installer_state->WriteInstallerResult(
              *status, IDS_INSTALL_SYSTEM_LEVEL_EXISTS_BASE, NULL);
        }
        return false;
      }
    }

  } else {  // System-level install.
    // --ensure-google-update-present is supported for user-level only.
    // The flag is generic, but its primary use case involves App Host.
    if (installer_state->ensure_google_update_present()) {
      LOG(DFATAL) << "--" << installer::switches::kEnsureGoogleUpdatePresent
                  << " is supported for user-level only.";
      *status = installer::APP_HOST_REQUIRES_USER_LEVEL;
      // No message string since there is nothing a user can do.
      installer_state->WriteInstallerResult(*status, 0, NULL);
      return false;
    }
  }

  return true;
}

// Initializes |temp_path| to "Temp" within the target directory, and
// |unpack_path| to a random directory beginning with "source" within
// |temp_path|. Returns false on error.
bool CreateTemporaryAndUnpackDirectories(
    const InstallerState& installer_state,
    installer::SelfCleaningTempDir* temp_path,
    base::FilePath* unpack_path) {
  DCHECK(temp_path && unpack_path);

  if (!temp_path->Initialize(installer_state.target_path().DirName(),
                             installer::kInstallTempDir)) {
    PLOG(ERROR) << "Could not create temporary path.";
    return false;
  }
  VLOG(1) << "Created path " << temp_path->path().value();

  if (!base::CreateTemporaryDirInDir(temp_path->path(),
                                     installer::kInstallSourceDir,
                                     unpack_path)) {
    PLOG(ERROR) << "Could not create temporary path for unpacked archive.";
    return false;
  }

  return true;
}

installer::InstallStatus UninstallProduct(
    const InstallationState& original_state,
    const InstallerState& installer_state,
    const CommandLine& cmd_line,
    bool remove_all,
    bool force_uninstall,
    const Product& product) {
  const ProductState* product_state =
      original_state.GetProductState(installer_state.system_install(),
                                     product.distribution()->GetType());
  if (product_state != NULL) {
    VLOG(1) << "version on the system: "
            << product_state->version().GetString();
  } else if (!force_uninstall) {
    LOG(ERROR) << product.distribution()->GetDisplayName()
               << " not found for uninstall.";
    return installer::CHROME_NOT_INSTALLED;
  }

  return installer::UninstallProduct(
      original_state, installer_state, cmd_line.GetProgram(), product,
      remove_all, force_uninstall, cmd_line);
}

installer::InstallStatus UninstallProducts(
    const InstallationState& original_state,
    const InstallerState& installer_state,
    const CommandLine& cmd_line) {
  const Products& products = installer_state.products();

  // Decide whether Active Setup should be triggered and/or system-level Chrome
  // should be launched post-uninstall. This needs to be done outside the
  // UninstallProduct calls as some of them might terminate the processes
  // launched by a previous one otherwise...
  bool trigger_active_setup = false;
  // System-level Chrome will be launched via this command if its program gets
  // set below.
  CommandLine system_level_cmd(CommandLine::NO_PROGRAM);

  const Product* chrome =
      installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER);
  if (chrome) {
    // InstallerState::Initialize always puts Chrome first, and we rely on that
    // here for this reason: if Chrome is in-use, the user will be prompted to
    // confirm uninstallation.  Upon cancel, we should not continue with the
    // other products.
    DCHECK(products[0]->is_chrome());

    if (cmd_line.HasSwitch(installer::switches::kSelfDestruct) &&
        !installer_state.system_install()) {
      BrowserDistribution* dist = chrome->distribution();
      const base::FilePath system_exe_path(
          installer::GetChromeInstallPath(true, dist)
              .Append(installer::kChromeExe));
      system_level_cmd.SetProgram(system_exe_path);

      base::FilePath first_run_sentinel;
      InstallUtil::GetSentinelFilePath(
          chrome::kFirstRunSentinel, dist, &first_run_sentinel);
      if (base::PathExists(first_run_sentinel)) {
        // If the Chrome being self-destructed has already undergone First Run,
        // trigger Active Setup and make sure the system-level Chrome doesn't go
        // through first run.
        trigger_active_setup = true;
        system_level_cmd.AppendSwitch(::switches::kCancelFirstRun);
      }
    }
  }
  if (installer_state.FindProduct(BrowserDistribution::CHROME_BINARIES)) {
    // Chrome Binaries should be last; if something else is cancelled, they
    // should stay.
    DCHECK(products[products.size() - 1]->is_chrome_binaries());
  }

  installer::InstallStatus install_status = installer::UNINSTALL_SUCCESSFUL;
  installer::InstallStatus prod_status = installer::UNKNOWN_STATUS;
  const bool force = cmd_line.HasSwitch(installer::switches::kForceUninstall);
  const bool remove_all = !cmd_line.HasSwitch(
      installer::switches::kDoNotRemoveSharedItems);

  for (Products::const_iterator it = products.begin();
       install_status != installer::UNINSTALL_CANCELLED && it < products.end();
       ++it) {
    prod_status = UninstallProduct(original_state, installer_state,
        cmd_line, remove_all, force, **it);
    if (prod_status != installer::UNINSTALL_SUCCESSFUL)
      install_status = prod_status;
  }

  installer::CleanUpInstallationDirectoryAfterUninstall(
      original_state, installer_state, cmd_line, &install_status);

  // The app and vendor dirs may now be empty. Make a last-ditch attempt to
  // delete them.
  installer::DeleteChromeDirectoriesIfEmpty(installer_state.target_path());

  if (trigger_active_setup)
    InstallUtil::TriggerActiveSetupCommand();

  if (!system_level_cmd.GetProgram().empty())
    base::LaunchProcess(system_level_cmd, base::LaunchOptions(), NULL);

  // Tell Google Update that an uninstall has taken place.
  // Ignore the return value: success or failure of Google Update
  // has no bearing on the success or failure of Chrome's uninstallation.
  google_update::UninstallGoogleUpdate(installer_state.system_install());

  return install_status;
}

// Uninstall the binaries if they are the only product present and they're not
// in-use.
void UninstallBinariesIfUnused(
    const InstallationState& original_state,
    const InstallerState& installer_state,
    installer::InstallStatus* install_status) {
  // Early exit if the binaries are still in use.
  if (*install_status != installer::UNUSED_BINARIES ||
      installer_state.AreBinariesInUse(original_state)) {
    return;
  }

  LOG(INFO) << "Uninstalling unused binaries";
  installer_state.UpdateStage(installer::UNINSTALLING_BINARIES);

  // Simulate the uninstall as coming from the installed version.
  const ProductState* binaries_state =
      original_state.GetProductState(installer_state.system_install(),
                                     BrowserDistribution::CHROME_BINARIES);
  const CommandLine& uninstall_cmd(binaries_state->uninstall_command());
  MasterPreferences uninstall_prefs(uninstall_cmd);
  InstallerState uninstall_state;
  uninstall_state.Initialize(uninstall_cmd, uninstall_prefs, original_state);

  *install_status =
      UninstallProducts(original_state, uninstall_state, uninstall_cmd);

  // Report that the binaries were uninstalled if they were. This translates
  // into a successful install return code.
  if (IsUninstallSuccess(*install_status)) {
    *install_status = installer::UNUSED_BINARIES_UNINSTALLED;
    installer_state.WriteInstallerResult(*install_status, 0, NULL);
  }
}

installer::InstallStatus InstallProducts(
    const InstallationState& original_state,
    const CommandLine& cmd_line,
    const MasterPreferences& prefs,
    InstallerState* installer_state,
    base::FilePath* installer_directory) {
  DCHECK(installer_state);
  const bool system_install = installer_state->system_install();
  installer::InstallStatus install_status = installer::UNKNOWN_STATUS;
  installer::ArchiveType archive_type = installer::UNKNOWN_ARCHIVE_TYPE;
  bool delegated_to_existing = false;
  installer_state->UpdateStage(installer::PRECONDITIONS);
  // The stage provides more fine-grained information than -multifail, so remove
  // the -multifail suffix from the Google Update "ap" value.
  BrowserDistribution::GetSpecificDistribution(installer_state->state_type())->
      UpdateInstallStatus(system_install, archive_type, install_status);
  if (CheckPreInstallConditions(original_state, installer_state,
                                &install_status)) {
    VLOG(1) << "Installing to " << installer_state->target_path().value();
    install_status = InstallProductsHelper(
        original_state, cmd_line, prefs, *installer_state,
        installer_directory, &archive_type, &delegated_to_existing);
  } else {
    // CheckPreInstallConditions must set the status on failure.
    DCHECK_NE(install_status, installer::UNKNOWN_STATUS);
  }

  // Delete the master preferences file if present. Note that we do not care
  // about rollback here and we schedule for deletion on reboot if the delete
  // fails. As such, we do not use DeleteTreeWorkItem.
  if (cmd_line.HasSwitch(installer::switches::kInstallerData)) {
    base::FilePath prefs_path(cmd_line.GetSwitchValuePath(
        installer::switches::kInstallerData));
    if (!base::DeleteFile(prefs_path, false)) {
      LOG(ERROR) << "Failed deleting master preferences file "
                 << prefs_path.value()
                 << ", scheduling for deletion after reboot.";
      ScheduleFileSystemEntityForDeletion(prefs_path);
    }
  }

  // Early exit if this setup.exe delegated to another, since that one would
  // have taken care of UpdateInstallStatus and UpdateStage.
  if (delegated_to_existing)
    return install_status;

  const Products& products = installer_state->products();
  for (Products::const_iterator it = products.begin(); it < products.end();
       ++it) {
    (*it)->distribution()->UpdateInstallStatus(
        system_install, archive_type, install_status);
  }

  UninstallBinariesIfUnused(original_state, *installer_state, &install_status);

  installer_state->UpdateStage(installer::NO_STAGE);
  return install_status;
}

installer::InstallStatus ShowEULADialog(const string16& inner_frame) {
  VLOG(1) << "About to show EULA";
  string16 eula_path = installer::GetLocalizedEulaResource();
  if (eula_path.empty()) {
    LOG(ERROR) << "No EULA path available";
    return installer::EULA_REJECTED;
  }
  // Newer versions of the caller pass an inner frame parameter that must
  // be given to the html page being launched.
  installer::EulaHTMLDialog dlg(eula_path, inner_frame);
  installer::EulaHTMLDialog::Outcome outcome = dlg.ShowModal();
  if (installer::EulaHTMLDialog::REJECTED == outcome) {
    LOG(ERROR) << "EULA rejected or EULA failure";
    return installer::EULA_REJECTED;
  }
  if (installer::EulaHTMLDialog::ACCEPTED_OPT_IN == outcome) {
    VLOG(1) << "EULA accepted (opt-in)";
    return installer::EULA_ACCEPTED_OPT_IN;
  }
  VLOG(1) << "EULA accepted (no opt-in)";
  return installer::EULA_ACCEPTED;
}

// Creates the sentinel indicating that the EULA was required and has been
// accepted.
bool CreateEULASentinel(BrowserDistribution* dist) {
  base::FilePath eula_sentinel;
  if (!InstallUtil::GetSentinelFilePath(installer::kEULASentinelFile,
                                        dist, &eula_sentinel)) {
    return false;
  }

  return (base::CreateDirectory(eula_sentinel.DirName()) &&
          file_util::WriteFile(eula_sentinel, "", 0) != -1);
}

void ActivateMetroChrome() {
  // Check to see if we're per-user or not. Need to do this since we may
  // not have been invoked with --system-level even for a machine install.
  wchar_t exe_path[MAX_PATH * 2] = {};
  GetModuleFileName(NULL, exe_path, arraysize(exe_path));
  bool is_per_user_install = InstallUtil::IsPerUserInstall(exe_path);

  string16 app_model_id =
      ShellUtil::GetBrowserModelId(BrowserDistribution::GetDistribution(),
                                   is_per_user_install);

  base::win::ScopedComPtr<IApplicationActivationManager> activator;
  HRESULT hr = activator.CreateInstance(CLSID_ApplicationActivationManager);
  if (SUCCEEDED(hr)) {
    DWORD pid = 0;
    hr = activator->ActivateApplication(
        app_model_id.c_str(), L"open", AO_NONE, &pid);
  }

  LOG_IF(ERROR, FAILED(hr)) << "Tried and failed to launch Metro Chrome. "
                            << "hr=" << std::hex << hr;
}

installer::InstallStatus RegisterDevChrome(
    const InstallationState& original_state,
    const InstallerState& installer_state,
    const CommandLine& cmd_line) {
  BrowserDistribution* chrome_dist =
      BrowserDistribution::GetSpecificDistribution(
          BrowserDistribution::CHROME_BROWSER);

  // Only proceed with registering a dev chrome if no real Chrome installation
  // of the same distribution are present on this system.
  const ProductState* existing_chrome =
      original_state.GetProductState(false,
                                     BrowserDistribution::CHROME_BROWSER);
  if (!existing_chrome) {
    existing_chrome =
      original_state.GetProductState(true, BrowserDistribution::CHROME_BROWSER);
  }
  if (existing_chrome) {
    static const wchar_t kPleaseUninstallYourChromeMessage[] =
        L"You already have a full-installation (non-dev) of %1ls, please "
        L"uninstall it first using Add/Remove Programs in the control panel.";
    string16 name(chrome_dist->GetDisplayName());
    string16 message(base::StringPrintf(kPleaseUninstallYourChromeMessage,
                                        name.c_str()));

    LOG(ERROR) << "Aborting operation: another installation of " << name
               << " was found, as a last resort (if the product is not present "
                  "in Add/Remove Programs), try executing: "
               << existing_chrome->uninstall_command().GetCommandLineString();
    MessageBox(NULL, message.c_str(), NULL, MB_ICONERROR);
    return installer::INSTALL_FAILED;
  }

  base::FilePath chrome_exe(
      cmd_line.GetSwitchValuePath(installer::switches::kRegisterDevChrome));
  if (chrome_exe.empty())
    chrome_exe = cmd_line.GetProgram().DirName().Append(installer::kChromeExe);
  if (!chrome_exe.IsAbsolute())
    chrome_exe = base::MakeAbsoluteFilePath(chrome_exe);

  installer::InstallStatus status = installer::FIRST_INSTALL_SUCCESS;
  if (base::PathExists(chrome_exe)) {
    Product chrome(chrome_dist);

    // Create the Start menu shortcut and pin it to the taskbar.
    ShellUtil::ShortcutProperties shortcut_properties(ShellUtil::CURRENT_USER);
    chrome.AddDefaultShortcutProperties(chrome_exe, &shortcut_properties);
    shortcut_properties.set_dual_mode(true);
    shortcut_properties.set_pin_to_taskbar(true);
    ShellUtil::CreateOrUpdateShortcut(
        ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_DIR, chrome_dist,
        shortcut_properties, ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS);

    // Register Chrome at user-level and make it default.
    scoped_ptr<WorkItemList> delegate_execute_list(
        WorkItem::CreateWorkItemList());
    installer::AddDelegateExecuteWorkItems(
        installer_state, chrome_exe.DirName(), Version(), chrome,
        delegate_execute_list.get());
    delegate_execute_list->Do();
    if (ShellUtil::CanMakeChromeDefaultUnattended()) {
      ShellUtil::MakeChromeDefault(
          chrome_dist, ShellUtil::CURRENT_USER, chrome_exe.value(), true);
    } else {
      ShellUtil::ShowMakeChromeDefaultSystemUI(chrome_dist, chrome_exe.value());
    }
  } else {
    LOG(ERROR) << "Path not found: " << chrome_exe.value();
    status = installer::INSTALL_FAILED;
  }
  return status;
}

// This method processes any command line options that make setup.exe do
// various tasks other than installation (renaming chrome.exe, showing eula
// among others). This function returns true if any such command line option
// has been found and processed (so setup.exe should exit at that point).
bool HandleNonInstallCmdLineOptions(const InstallationState& original_state,
                                    const CommandLine& cmd_line,
                                    InstallerState* installer_state,
                                    int* exit_code) {
  // TODO(gab): Add a local |status| variable which each block below sets;
  // only determine the |exit_code| from |status| at the end (this will allow
  // this method to validate that
  // (!handled || status != installer::UNKNOWN_STATUS)).
  bool handled = true;
  // TODO(tommi): Split these checks up into functions and use a data driven
  // map of switch->function.
  if (cmd_line.HasSwitch(installer::switches::kUpdateSetupExe)) {
    installer::InstallStatus status = installer::SETUP_PATCH_FAILED;
    // If --update-setup-exe command line option is given, we apply the given
    // patch to current exe, and store the resulting binary in the path
    // specified by --new-setup-exe. But we need to first unpack the file
    // given in --update-setup-exe.
    base::ScopedTempDir temp_path;
    if (!temp_path.CreateUniqueTempDir()) {
      PLOG(ERROR) << "Could not create temporary path.";
    } else {
      base::FilePath compressed_archive(cmd_line.GetSwitchValuePath(
          installer::switches::kUpdateSetupExe));
      VLOG(1) << "Opening archive " << compressed_archive.value();
      if (installer::ArchivePatchHelper::UncompressAndPatch(
              temp_path.path(),
              compressed_archive,
              cmd_line.GetProgram(),
              cmd_line.GetSwitchValuePath(installer::switches::kNewSetupExe))) {
        status = installer::NEW_VERSION_UPDATED;
      }
      if (!temp_path.Delete()) {
        // PLOG would be nice, but Delete() doesn't leave a meaningful value in
        // the Windows last-error code.
        LOG(WARNING) << "Scheduling temporary path " << temp_path.path().value()
                     << " for deletion at reboot.";
        ScheduleDirectoryForDeletion(temp_path.path());
      }
    }

    *exit_code = InstallUtil::GetInstallReturnCode(status);
    if (*exit_code) {
      LOG(WARNING) << "setup.exe patching failed.";
      installer_state->WriteInstallerResult(
          status, IDS_SETUP_PATCH_FAILED_BASE, NULL);
    }
    // We will be exiting normally, so clear the stage indicator.
    installer_state->UpdateStage(installer::NO_STAGE);
  } else if (cmd_line.HasSwitch(installer::switches::kShowEula)) {
    // Check if we need to show the EULA. If it is passed as a command line
    // then the dialog is shown and regardless of the outcome setup exits here.
    string16 inner_frame =
        cmd_line.GetSwitchValueNative(installer::switches::kShowEula);
    *exit_code = ShowEULADialog(inner_frame);

    if (installer::EULA_REJECTED != *exit_code) {
      if (GoogleUpdateSettings::SetEULAConsent(
              original_state, BrowserDistribution::GetDistribution(), true)) {
        CreateEULASentinel(BrowserDistribution::GetDistribution());
      }
      // For a metro-originated launch, we now need to launch back into metro.
      if (cmd_line.HasSwitch(installer::switches::kShowEulaForMetro))
        ActivateMetroChrome();
    }
  } else if (cmd_line.HasSwitch(installer::switches::kConfigureUserSettings)) {
    // NOTE: Should the work done here, on kConfigureUserSettings, change:
    // kActiveSetupVersion in install_worker.cc needs to be increased for Active
    // Setup to invoke this again for all users of this install.
    const Product* chrome_install =
        installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER);
    installer::InstallStatus status = installer::INVALID_STATE_FOR_OPTION;
    if (chrome_install && installer_state->system_install()) {
      bool force =
          cmd_line.HasSwitch(installer::switches::kForceConfigureUserSettings);
      installer::HandleActiveSetupForBrowser(installer_state->target_path(),
                                             *chrome_install, force);
      status = installer::INSTALL_REPAIRED;
    } else {
      LOG(DFATAL) << "chrome_install:" << chrome_install
                  << ", system_install:" << installer_state->system_install();
    }
    *exit_code = InstallUtil::GetInstallReturnCode(status);
  } else if (cmd_line.HasSwitch(installer::switches::kRegisterDevChrome)) {
    installer::InstallStatus status = RegisterDevChrome(
        original_state, *installer_state, cmd_line);
    *exit_code = InstallUtil::GetInstallReturnCode(status);
  } else if (cmd_line.HasSwitch(installer::switches::kRegisterChromeBrowser)) {
    installer::InstallStatus status = installer::UNKNOWN_STATUS;
    const Product* chrome_install =
        installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER);
    if (chrome_install) {
      // If --register-chrome-browser option is specified, register all
      // Chrome protocol/file associations, as well as register it as a valid
      // browser for Start Menu->Internet shortcut. This switch will also
      // register Chrome as a valid handler for a set of URL protocols that
      // Chrome may become the default handler for, either by the user marking
      // Chrome as the default browser, through the Windows Default Programs
      // control panel settings, or through website use of
      // registerProtocolHandler. These protocols are found in
      // ShellUtil::kPotentialProtocolAssociations.
      // The --register-url-protocol will additionally register Chrome as a
      // potential handler for the supplied protocol, and is used if a website
      // registers a handler for a protocol not found in
      // ShellUtil::kPotentialProtocolAssociations.
      // These options should only be used when setup.exe is launched with admin
      // rights. We do not make any user specific changes with this option.
      DCHECK(IsUserAnAdmin());
      string16 chrome_exe(cmd_line.GetSwitchValueNative(
          installer::switches::kRegisterChromeBrowser));
      string16 suffix;
      if (cmd_line.HasSwitch(
          installer::switches::kRegisterChromeBrowserSuffix)) {
        suffix = cmd_line.GetSwitchValueNative(
            installer::switches::kRegisterChromeBrowserSuffix);
      }
      if (cmd_line.HasSwitch(installer::switches::kRegisterURLProtocol)) {
        string16 protocol = cmd_line.GetSwitchValueNative(
            installer::switches::kRegisterURLProtocol);
        // ShellUtil::RegisterChromeForProtocol performs all registration
        // done by ShellUtil::RegisterChromeBrowser, as well as registering
        // with Windows as capable of handling the supplied protocol.
        if (ShellUtil::RegisterChromeForProtocol(
                chrome_install->distribution(), chrome_exe, suffix, protocol,
                false))
          status = installer::IN_USE_UPDATED;
      } else {
        if (ShellUtil::RegisterChromeBrowser(chrome_install->distribution(),
            chrome_exe, suffix, false))
          status = installer::IN_USE_UPDATED;
      }
    } else {
      LOG(DFATAL) << "Can't register browser - Chrome distribution not found";
    }
    *exit_code = InstallUtil::GetInstallReturnCode(status);
  } else if (cmd_line.HasSwitch(installer::switches::kRenameChromeExe)) {
    // If --rename-chrome-exe is specified, we want to rename the executables
    // and exit.
    *exit_code = RenameChromeExecutables(original_state, installer_state);
  } else if (cmd_line.HasSwitch(
                 installer::switches::kRemoveChromeRegistration)) {
    // This is almost reverse of --register-chrome-browser option above.
    // Here we delete Chrome browser registration. This option should only
    // be used when setup.exe is launched with admin rights. We do not
    // make any user specific changes in this option.
    string16 suffix;
    if (cmd_line.HasSwitch(
            installer::switches::kRegisterChromeBrowserSuffix)) {
      suffix = cmd_line.GetSwitchValueNative(
          installer::switches::kRegisterChromeBrowserSuffix);
    }
    installer::InstallStatus tmp = installer::UNKNOWN_STATUS;
    const Product* chrome_install =
        installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER);
    DCHECK(chrome_install);
    if (chrome_install) {
      installer::DeleteChromeRegistrationKeys(*installer_state,
          chrome_install->distribution(), HKEY_LOCAL_MACHINE, suffix, &tmp);
    }
    *exit_code = tmp;
  } else if (cmd_line.HasSwitch(installer::switches::kOnOsUpgrade)) {
    const Product* chrome_install =
        installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER);
    installer::InstallStatus status = installer::INVALID_STATE_FOR_OPTION;
    if (chrome_install) {
      installer::HandleOsUpgradeForBrowser(*installer_state,
                                           *chrome_install);
      status = installer::INSTALL_REPAIRED;
    } else {
      LOG(DFATAL) << "Chrome product not found.";
    }
    *exit_code = InstallUtil::GetInstallReturnCode(status);
  } else if (cmd_line.HasSwitch(installer::switches::kQueryEULAAcceptance)) {
    *exit_code = installer::IsEULAAccepted(installer_state->system_install());
  } else if (cmd_line.HasSwitch(installer::switches::kInactiveUserToast)) {
    // Launch the inactive user toast experiment.
    int flavor = -1;
    base::StringToInt(cmd_line.GetSwitchValueNative(
        installer::switches::kInactiveUserToast), &flavor);
    std::string experiment_group =
        cmd_line.GetSwitchValueASCII(installer::switches::kExperimentGroup);
    DCHECK_NE(-1, flavor);
    if (flavor == -1) {
      *exit_code = installer::UNKNOWN_STATUS;
    } else {
      // This code is called (via setup.exe relaunch) only if a product is known
      // to run user experiments, so no check is required.
      const Products& products = installer_state->products();
      for (Products::const_iterator it = products.begin(); it < products.end();
           ++it) {
        const Product& product = **it;
        installer::InactiveUserToastExperiment(
            flavor, ASCIIToUTF16(experiment_group), product,
            installer_state->target_path());
      }
    }
  } else if (cmd_line.HasSwitch(installer::switches::kSystemLevelToast)) {
    const Products& products = installer_state->products();
    for (Products::const_iterator it = products.begin(); it < products.end();
         ++it) {
      const Product& product = **it;
      BrowserDistribution* browser_dist = product.distribution();
      // We started as system-level and have been re-launched as user level
      // to continue with the toast experiment.
      Version installed_version;
      InstallUtil::GetChromeVersion(browser_dist, true, &installed_version);
      if (!installed_version.IsValid()) {
        LOG(ERROR) << "No installation of "
                   << browser_dist->GetDisplayName()
                   << " found for system-level toast.";
      } else {
        product.LaunchUserExperiment(
            cmd_line.GetProgram(), installer::REENTRY_SYS_UPDATE, true);
      }
    }
  } else if (cmd_line.HasSwitch(installer::switches::kPatch)) {
    const std::string patch_type_str(
        cmd_line.GetSwitchValueASCII(installer::switches::kPatch));
    const base::FilePath input_file(
        cmd_line.GetSwitchValuePath(installer::switches::kInputFile));
    const base::FilePath patch_file(
        cmd_line.GetSwitchValuePath(installer::switches::kPatchFile));
    const base::FilePath output_file(
        cmd_line.GetSwitchValuePath(installer::switches::kOutputFile));

    if (patch_type_str == installer::kCourgette) {
      *exit_code = installer::CourgettePatchFiles(input_file,
                                                  patch_file,
                                                  output_file);
    } else if (patch_type_str == installer::kBsdiff) {
      *exit_code = installer::BsdiffPatchFiles(input_file,
                                               patch_file,
                                               output_file);
    } else {
      *exit_code = installer::PATCH_INVALID_ARGUMENTS;
    }
  } else {
    handled = false;
  }

  return handled;
}

bool ShowRebootDialog() {
  // Get a token for this process.
  HANDLE token;
  if (!OpenProcessToken(GetCurrentProcess(),
                        TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
                        &token)) {
    LOG(ERROR) << "Failed to open token.";
    return false;
  }

  // Use a ScopedHandle to keep track of and eventually close our handle.
  // TODO(robertshield): Add a Receive() method to base's ScopedHandle.
  base::win::ScopedHandle scoped_handle(token);

  // Get the LUID for the shutdown privilege.
  TOKEN_PRIVILEGES tkp = {0};
  LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid);
  tkp.PrivilegeCount = 1;
  tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

  // Get the shutdown privilege for this process.
  AdjustTokenPrivileges(token, FALSE, &tkp, 0,
                        reinterpret_cast<PTOKEN_PRIVILEGES>(NULL), 0);
  if (GetLastError() != ERROR_SUCCESS) {
    LOG(ERROR) << "Unable to get shutdown privileges.";
    return false;
  }

  // Popup a dialog that will prompt to reboot using the default system message.
  // TODO(robertshield): Add a localized, more specific string to the prompt.
  RestartDialog(NULL, NULL, EWX_REBOOT | EWX_FORCEIFHUNG);
  return true;
}

// Returns the Custom information for the client identified by the exe path
// passed in. This information is used for crash reporting.
google_breakpad::CustomClientInfo* GetCustomInfo(const wchar_t* exe_path) {
  string16 product;
  string16 version;
  scoped_ptr<FileVersionInfo> version_info(
      FileVersionInfo::CreateFileVersionInfo(base::FilePath(exe_path)));
  if (version_info.get()) {
    version = version_info->product_version();
    product = version_info->product_short_name();
  }

  if (version.empty())
    version = L"0.1.0.0";

  if (product.empty())
    product = L"Chrome Installer";

  static google_breakpad::CustomInfoEntry ver_entry(L"ver", version.c_str());
  static google_breakpad::CustomInfoEntry prod_entry(L"prod", product.c_str());
  static google_breakpad::CustomInfoEntry plat_entry(L"plat", L"Win32");
  static google_breakpad::CustomInfoEntry type_entry(L"ptype",
                                                     L"Chrome Installer");
  static google_breakpad::CustomInfoEntry entries[] = {
      ver_entry, prod_entry, plat_entry, type_entry };
  static google_breakpad::CustomClientInfo custom_info = {
      entries, arraysize(entries) };
  return &custom_info;
}

// Initialize crash reporting for this process. This involves connecting to
// breakpad, etc.
google_breakpad::ExceptionHandler* InitializeCrashReporting(
    bool system_install) {
  // Only report crashes if the user allows it.
  if (!GoogleUpdateSettings::GetCollectStatsConsent())
    return NULL;

  // Get the alternate dump directory. We use the temp path.
  base::FilePath temp_directory;
  if (!base::GetTempDir(&temp_directory) || temp_directory.empty())
    return NULL;

  wchar_t exe_path[MAX_PATH * 2] = {0};
  GetModuleFileName(NULL, exe_path, arraysize(exe_path));

  // Build the pipe name. It can be either:
  // System-wide install: "NamedPipe\GoogleCrashServices\S-1-5-18"
  // Per-user install: "NamedPipe\GoogleCrashServices\<user SID>"
  string16 user_sid = kSystemPrincipalSid;

  if (!system_install) {
    if (!base::win::GetUserSidString(&user_sid)) {
      return NULL;
    }
  }

  string16 pipe_name = kGoogleUpdatePipeName;
  pipe_name += user_sid;

  google_breakpad::ExceptionHandler* breakpad =
      new google_breakpad::ExceptionHandler(
          temp_directory.value(), NULL, NULL, NULL,
          google_breakpad::ExceptionHandler::HANDLER_ALL, kLargerDumpType,
          pipe_name.c_str(), GetCustomInfo(exe_path));
  return breakpad;
}

// Uninstalls multi-install Chrome Frame if the current operation is a
// multi-install install or update. The operation is performed directly rather
// than delegated to the existing install since there is no facility in older
// versions of setup.exe to uninstall GCF without touching the binaries. The
// binaries will be uninstalled during later processing if they are not in-use
// (see UninstallBinariesIfUnused). |original_state| and |installer_state| are
// updated to reflect the state of the world following the operation.
void UninstallMultiChromeFrameIfPresent(const CommandLine& cmd_line,
                                        const MasterPreferences& prefs,
                                        InstallationState* original_state,
                                        InstallerState* installer_state) {
  // Early exit if not installing or updating multi-install product(s).
  if (installer_state->operation() != InstallerState::MULTI_INSTALL &&
      installer_state->operation() != InstallerState::MULTI_UPDATE) {
    return;
  }

  // Early exit if Chrome Frame is not present as multi-install.
  const ProductState* chrome_frame_state =
      original_state->GetProductState(installer_state->system_install(),
                                      BrowserDistribution::CHROME_FRAME);
  if (!chrome_frame_state || !chrome_frame_state->is_multi_install())
    return;

  LOG(INFO) << "Uninstalling multi-install Chrome Frame.";
  installer_state->UpdateStage(installer::UNINSTALLING_CHROME_FRAME);

  // Uninstall Chrome Frame without touching the multi-install binaries.
  // Simulate the uninstall as coming from the installed version.
  const CommandLine& uninstall_cmd(chrome_frame_state->uninstall_command());
  MasterPreferences uninstall_prefs(uninstall_cmd);
  InstallerState uninstall_state;
  uninstall_state.Initialize(uninstall_cmd, uninstall_prefs, *original_state);
  const Product* chrome_frame_product = uninstall_state.FindProduct(
      BrowserDistribution::CHROME_FRAME);
  if (chrome_frame_product) {
    // No shared state should be left behind.
    const bool remove_all = true;
    // Don't accept no for an answer.
    const bool force_uninstall = true;
    installer::InstallStatus uninstall_status =
        installer::UninstallProduct(*original_state, uninstall_state,
                                    uninstall_cmd.GetProgram(),
                                    *chrome_frame_product, remove_all,
                                    force_uninstall, cmd_line);

    VLOG(1) << "Uninstallation of Chrome Frame returned status "
            << uninstall_status;
  } else {
    LOG(ERROR) << "Chrome Frame not found for uninstall.";
  }

  // Refresh state for the continuation of the original install/update.
  original_state->Initialize();
  installer_state->Initialize(cmd_line, prefs, *original_state);
}

}  // namespace

namespace installer {

InstallStatus InstallProductsHelper(
    const InstallationState& original_state,
    const CommandLine& cmd_line,
    const MasterPreferences& prefs,
    const InstallerState& installer_state,
    base::FilePath* installer_directory,
    ArchiveType* archive_type,
    bool* delegated_to_existing) {
  DCHECK(archive_type);
  DCHECK(delegated_to_existing);
  const bool system_install = installer_state.system_install();
  InstallStatus install_status = UNKNOWN_STATUS;

  // Drop to background processing mode if the process was started below the
  // normal process priority class.
  bool entered_background_mode = AdjustProcessPriority();
  VLOG_IF(1, entered_background_mode) << "Entered background processing mode.";

  // Create a temp folder where we will unpack Chrome archive. If it fails,
  // then we are doomed, so return immediately and no cleanup is required.
  SelfCleaningTempDir temp_path;
  base::FilePath unpack_path;
  if (!CreateTemporaryAndUnpackDirectories(installer_state, &temp_path,
                                           &unpack_path)) {
    installer_state.WriteInstallerResult(TEMP_DIR_FAILED,
                                         IDS_INSTALL_TEMP_DIR_FAILED_BASE,
                                         NULL);
    return TEMP_DIR_FAILED;
  }

  // Uncompress and optionally patch the archive if an uncompressed archive was
  // not specified on the command line and a compressed archive is found.
  *archive_type = UNKNOWN_ARCHIVE_TYPE;
  base::FilePath uncompressed_archive(cmd_line.GetSwitchValuePath(
      switches::kUncompressedArchive));
  if (uncompressed_archive.empty()) {
    scoped_ptr<ArchivePatchHelper> archive_helper(
        CreateChromeArchiveHelper(cmd_line, installer_state, unpack_path));
    if (archive_helper) {
      VLOG(1) << "Installing Chrome from compressed archive "
              << archive_helper->compressed_archive().value();
      if (!UncompressAndPatchChromeArchive(original_state,
                                           installer_state,
                                           archive_helper.get(),
                                           archive_type,
                                           &install_status)) {
        DCHECK_NE(install_status, UNKNOWN_STATUS);
        return install_status;
      }
      uncompressed_archive = archive_helper->target();
      DCHECK(!uncompressed_archive.empty());
    }
  }

  // Check for an uncompressed archive alongside the current executable if one
  // was not given or generated.
  if (uncompressed_archive.empty()) {
    uncompressed_archive =
        cmd_line.GetProgram().DirName().Append(kChromeArchive);
  }

  if (*archive_type == UNKNOWN_ARCHIVE_TYPE) {
    // An archive was not uncompressed or patched above.
    if (uncompressed_archive.empty() ||
        !base::PathExists(uncompressed_archive)) {
      LOG(ERROR) << "Cannot install Chrome without an uncompressed archive.";
      installer_state.WriteInstallerResult(
          INVALID_ARCHIVE, IDS_INSTALL_INVALID_ARCHIVE_BASE, NULL);
      return INVALID_ARCHIVE;
    }
    *archive_type = FULL_ARCHIVE_TYPE;
  }

  // Unpack the uncompressed archive.
  if (LzmaUtil::UnPackArchive(uncompressed_archive.value(),
                              unpack_path.value(),
                              NULL)) {
    installer_state.WriteInstallerResult(
        UNCOMPRESSION_FAILED,
        IDS_INSTALL_UNCOMPRESSION_FAILED_BASE,
        NULL);
    return UNCOMPRESSION_FAILED;
  }

  VLOG(1) << "unpacked to " << unpack_path.value();
  base::FilePath src_path(
      unpack_path.Append(kInstallSourceChromeDir));
  scoped_ptr<Version>
      installer_version(GetMaxVersionFromArchiveDir(src_path));
  if (!installer_version.get()) {
    LOG(ERROR) << "Did not find any valid version in installer.";
    install_status = INVALID_ARCHIVE;
    installer_state.WriteInstallerResult(install_status,
        IDS_INSTALL_INVALID_ARCHIVE_BASE, NULL);
  } else {
    VLOG(1) << "version to install: " << installer_version->GetString();
    bool proceed_with_installation = true;

    if (installer_state.operation() == InstallerState::MULTI_INSTALL) {
      // This is a new install of a multi-install product. Rather than give up
      // in case a higher version of the binaries (including a single-install
      // of Chrome, which can safely be migrated to multi-install by way of
      // CheckMultiInstallConditions) is already installed, delegate to the
      // installed setup.exe to install the product at hand.
      base::FilePath setup_exe;
      if (GetExistingHigherInstaller(original_state, system_install,
                                     *installer_version, &setup_exe)) {
        VLOG(1) << "Deferring to existing installer.";
        installer_state.UpdateStage(DEFERRING_TO_HIGHER_VERSION);
        if (DeferToExistingInstall(setup_exe, cmd_line, installer_state,
                                   temp_path.path(), &install_status)) {
          *delegated_to_existing = true;
          return install_status;
        }
      }
    }

    uint32 higher_products = 0;
    COMPILE_ASSERT(
        sizeof(higher_products) * 8 > BrowserDistribution::NUM_TYPES,
        too_many_distribution_types_);
    const Products& products = installer_state.products();
    for (Products::const_iterator it = products.begin(); it < products.end();
         ++it) {
      const Product& product = **it;
      const ProductState* product_state =
          original_state.GetProductState(system_install,
                                         product.distribution()->GetType());
      if (product_state != NULL &&
          (product_state->version().CompareTo(*installer_version) > 0)) {
        LOG(ERROR) << "Higher version of "
                   << product.distribution()->GetDisplayName()
                   << " is already installed.";
        higher_products |= (1 << product.distribution()->GetType());
      }
    }

    if (higher_products != 0) {
      COMPILE_ASSERT(BrowserDistribution::NUM_TYPES == 4,
                     add_support_for_new_products_here_);
      const uint32 kBrowserBit = 1 << BrowserDistribution::CHROME_BROWSER;
      const uint32 kAppHostBit = 1 << BrowserDistribution::CHROME_APP_HOST;
      int message_id = 0;

      proceed_with_installation = false;
      install_status = HIGHER_VERSION_EXISTS;
      switch (higher_products) {
        case kBrowserBit:
          message_id = IDS_INSTALL_HIGHER_VERSION_BASE;
          break;
        default:
          message_id = IDS_INSTALL_HIGHER_VERSION_APP_LAUNCHER_BASE;
          break;
      }

      installer_state.WriteInstallerResult(install_status, message_id, NULL);
    }

    proceed_with_installation =
        proceed_with_installation &&
        CheckGroupPolicySettings(original_state, installer_state,
                                 *installer_version, &install_status);

    if (proceed_with_installation) {
      // If Google Update is absent at user-level, install it using the
      // Google Update installer from an existing system-level installation.
      // This is for quick-enable App Host install from a system-level
      // Chrome Binaries installation.
      if (!system_install && installer_state.ensure_google_update_present()) {
        if (!google_update::EnsureUserLevelGoogleUpdatePresent()) {
          LOG(ERROR) << "Failed to install Google Update";
          proceed_with_installation = false;
          install_status = INSTALL_OF_GOOGLE_UPDATE_FAILED;
          installer_state.WriteInstallerResult(install_status, 0, NULL);
        }
      }

    }

    if (proceed_with_installation) {
      base::FilePath prefs_source_path(cmd_line.GetSwitchValueNative(
          switches::kInstallerData));
      install_status = InstallOrUpdateProduct(
          original_state, installer_state, cmd_line.GetProgram(),
          uncompressed_archive, temp_path.path(), src_path, prefs_source_path,
          prefs, *installer_version);

      int install_msg_base = IDS_INSTALL_FAILED_BASE;
      string16 chrome_exe;
      string16 quoted_chrome_exe;
      if (install_status == SAME_VERSION_REPAIR_FAILED) {
        install_msg_base = IDS_SAME_VERSION_REPAIR_FAILED_BASE;
      } else if (install_status != INSTALL_FAILED) {
        if (installer_state.target_path().empty()) {
          // If we failed to construct install path, it means the OS call to
          // get %ProgramFiles% or %AppData% failed. Report this as failure.
          install_msg_base = IDS_INSTALL_OS_ERROR_BASE;
          install_status = OS_ERROR;
        } else {
          chrome_exe = installer_state.target_path().Append(kChromeExe).value();
          quoted_chrome_exe = L"\"" + chrome_exe + L"\"";
          install_msg_base = 0;
        }
      }

      installer_state.UpdateStage(FINISHING);

      // Only do Chrome-specific stuff (like launching the browser) if
      // Chrome was specifically requested (rather than being upgraded as
      // part of a multi-install).
      const Product* chrome_install = prefs.install_chrome() ?
          installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER) :
          NULL;

      bool do_not_register_for_update_launch = false;
      if (chrome_install) {
        prefs.GetBool(master_preferences::kDoNotRegisterForUpdateLaunch,
                      &do_not_register_for_update_launch);
      } else {
        do_not_register_for_update_launch = true;  // Never register.
      }

      bool write_chrome_launch_string =
          (!do_not_register_for_update_launch &&
           install_status != IN_USE_UPDATED);

      installer_state.WriteInstallerResult(install_status, install_msg_base,
          write_chrome_launch_string ? &quoted_chrome_exe : NULL);

      if (install_status == FIRST_INSTALL_SUCCESS) {
        VLOG(1) << "First install successful.";
        if (chrome_install) {
          // We never want to launch Chrome in system level install mode.
          bool do_not_launch_chrome = false;
          prefs.GetBool(master_preferences::kDoNotLaunchChrome,
                        &do_not_launch_chrome);
          if (!system_install && !do_not_launch_chrome)
            chrome_install->LaunchChrome(installer_state.target_path());
        }
      } else if ((install_status == NEW_VERSION_UPDATED) ||
                 (install_status == IN_USE_UPDATED)) {
        const Product* chrome = installer_state.FindProduct(
            BrowserDistribution::CHROME_BROWSER);
        if (chrome != NULL) {
          DCHECK_NE(chrome_exe, string16());
          RemoveChromeLegacyRegistryKeys(chrome->distribution(), chrome_exe);
        }
      }

      if (prefs.install_chrome_app_launcher() &&
          InstallUtil::GetInstallReturnCode(install_status) == 0) {
        std::string webstore_item(google_update::GetUntrustedDataValue(
            kInstallFromWebstore));
        if (!webstore_item.empty()) {
          bool success = InstallFromWebstore(webstore_item);
          VLOG_IF(1, !success) << "Failed to launch app installation.";
        }
      }
    }
  }

  // There might be an experiment (for upgrade usually) that needs to happen.
  // An experiment's outcome can include chrome's uninstallation. If that is
  // the case we would not do that directly at this point but in another
  // instance of setup.exe
  //
  // There is another way to reach this same function if this is a system
  // level install. See HandleNonInstallCmdLineOptions().
  {
    // If installation failed, use the path to the currently running setup.
    // If installation succeeded, use the path to setup in the installer dir.
    base::FilePath setup_path(cmd_line.GetProgram());
    if (InstallUtil::GetInstallReturnCode(install_status) == 0) {
      setup_path = installer_state.GetInstallerDirectory(*installer_version)
          .Append(setup_path.BaseName());
    }
    const Products& products = installer_state.products();
    for (Products::const_iterator it = products.begin(); it < products.end();
         ++it) {
      const Product& product = **it;
      product.LaunchUserExperiment(setup_path, install_status,
                                   system_install);
    }
  }

  // If installation completed successfully, return the path to the directory
  // containing the newly installed setup.exe and uncompressed archive if the
  // caller requested it.
  if (installer_directory &&
      InstallUtil::GetInstallReturnCode(install_status) == 0) {
    *installer_directory =
        installer_state.GetInstallerDirectory(*installer_version);
  }

  // temp_path's dtor will take care of deleting or scheduling itself for
  // deletion at reboot when this scope closes.
  VLOG(1) << "Deleting temporary directory " << temp_path.path().value();

  return install_status;
}

}  // namespace installer

int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance,
                    wchar_t* command_line, int show_command) {
  // The exit manager is in charge of calling the dtors of singletons.
  base::AtExitManager exit_manager;
  CommandLine::Init(0, NULL);

  const MasterPreferences& prefs = MasterPreferences::ForCurrentProcess();
  installer::InitInstallerLogging(prefs);

  const CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
  VLOG(1) << "Command Line: " << cmd_line.GetCommandLineString();

  VLOG(1) << "multi install is " << prefs.is_multi_install();
  bool system_install = false;
  prefs.GetBool(installer::master_preferences::kSystemLevel, &system_install);
  VLOG(1) << "system install is " << system_install;

  google_breakpad::scoped_ptr<google_breakpad::ExceptionHandler> breakpad(
      InitializeCrashReporting(system_install));

  InstallationState original_state;
  original_state.Initialize();

  InstallerState installer_state;
  installer_state.Initialize(cmd_line, prefs, original_state);
  const bool is_uninstall = cmd_line.HasSwitch(installer::switches::kUninstall);

  // Check to make sure current system is WinXP or later. If not, log
  // error message and get out.
  if (!InstallUtil::IsOSSupported()) {
    LOG(ERROR) << "Chrome only supports Windows XP or later.";
    installer_state.WriteInstallerResult(
        installer::OS_NOT_SUPPORTED, IDS_INSTALL_OS_NOT_SUPPORTED_BASE, NULL);
    return installer::OS_NOT_SUPPORTED;
  }

  // Initialize COM for use later.
  base::win::ScopedCOMInitializer com_initializer;
  if (!com_initializer.succeeded()) {
    installer_state.WriteInstallerResult(
        installer::OS_ERROR, IDS_INSTALL_OS_ERROR_BASE, NULL);
    return installer::OS_ERROR;
  }

  // Some command line options don't work with SxS install/uninstall
  if (InstallUtil::IsChromeSxSProcess()) {
    if (system_install ||
        prefs.is_multi_install() ||
        cmd_line.HasSwitch(installer::switches::kForceUninstall) ||
        cmd_line.HasSwitch(installer::switches::kMakeChromeDefault) ||
        cmd_line.HasSwitch(installer::switches::kRegisterChromeBrowser) ||
        cmd_line.HasSwitch(installer::switches::kRemoveChromeRegistration) ||
        cmd_line.HasSwitch(installer::switches::kInactiveUserToast) ||
        cmd_line.HasSwitch(installer::switches::kSystemLevelToast)) {
      return installer::SXS_OPTION_NOT_SUPPORTED;
    }
  }

  // Some command line options are no longer supported and must error out.
  if (installer::ContainsUnsupportedSwitch(cmd_line))
    return installer::UNSUPPORTED_OPTION;

  int exit_code = 0;
  if (HandleNonInstallCmdLineOptions(
          original_state, cmd_line, &installer_state, &exit_code)) {
    return exit_code;
  }

  if (system_install && !IsUserAnAdmin()) {
    if (base::win::GetVersion() >= base::win::VERSION_VISTA &&
        !cmd_line.HasSwitch(installer::switches::kRunAsAdmin)) {
      CommandLine new_cmd(CommandLine::NO_PROGRAM);
      new_cmd.AppendArguments(cmd_line, true);
      // Append --run-as-admin flag to let the new instance of setup.exe know
      // that we already tried to launch ourselves as admin.
      new_cmd.AppendSwitch(installer::switches::kRunAsAdmin);
      // If system_install became true due to an environment variable, append
      // it to the command line here since env vars may not propagate past the
      // elevation.
      if (!new_cmd.HasSwitch(installer::switches::kSystemLevel))
        new_cmd.AppendSwitch(installer::switches::kSystemLevel);

      DWORD exit_code = installer::UNKNOWN_STATUS;
      InstallUtil::ExecuteExeAsAdmin(new_cmd, &exit_code);
      return exit_code;
    } else {
      LOG(ERROR) << "Non admin user can not install system level Chrome.";
      installer_state.WriteInstallerResult(installer::INSUFFICIENT_RIGHTS,
          IDS_INSTALL_INSUFFICIENT_RIGHTS_BASE, NULL);
      return installer::INSUFFICIENT_RIGHTS;
    }
  }

  UninstallMultiChromeFrameIfPresent(cmd_line, prefs,
                                     &original_state, &installer_state);

  base::FilePath installer_directory;
  installer::InstallStatus install_status = installer::UNKNOWN_STATUS;
  // If --uninstall option is given, uninstall the identified product(s)
  if (is_uninstall) {
    install_status =
        UninstallProducts(original_state, installer_state, cmd_line);
  } else {
    // If --uninstall option is not specified, we assume it is install case.
    install_status =
        InstallProducts(original_state, cmd_line, prefs, &installer_state,
                        &installer_directory);
  }

  // Validate that the machine is now in a good state following the operation.
  // TODO(grt): change this to log at DFATAL once we're convinced that the
  // validator handles all cases properly.
  InstallationValidator::InstallationType installation_type =
      InstallationValidator::NO_PRODUCTS;
  LOG_IF(ERROR,
         !InstallationValidator::ValidateInstallationType(system_install,
                                                          &installation_type));

  int return_code = 0;
  // MSI demands that custom actions always return 0 (ERROR_SUCCESS) or it will
  // rollback the action. If we're uninstalling we want to avoid this, so always
  // report success, squashing any more informative return codes.
  if (!(installer_state.is_msi() && is_uninstall)) {
    // Note that we allow the status installer::UNINSTALL_REQUIRES_REBOOT
    // to pass through, since this is only returned on uninstall which is
    // never invoked directly by Google Update.
    return_code = InstallUtil::GetInstallReturnCode(install_status);
  }

  VLOG(1) << "Installation complete, returning: " << return_code;

  return return_code;
}