summaryrefslogtreecommitdiff
path: root/chrome/installer
diff options
context:
space:
mode:
authorBen Murdoch <benm@google.com>2013-07-23 11:17:05 +0100
committerBen Murdoch <benm@google.com>2013-07-23 11:17:05 +0100
commitca12bfac764ba476d6cd062bf1dde12cc64c3f40 (patch)
tree1cd09db25ea5de98e73c8efbe572e103daee8b2b /chrome/installer
parentfcb3e05bdd21d752df9c3dff28b6bbf29b5b733b (diff)
downloadchromium_org-ca12bfac764ba476d6cd062bf1dde12cc64c3f40.tar.gz
Merge from Chromium at DEPS revision r213057
This commit was generated by merge_to_master.py. Change-Id: I3e2e2506eb9b0080157e9c5f133559df3e600388
Diffstat (limited to 'chrome/installer')
-rw-r--r--chrome/installer/mini_installer.gyp2
-rw-r--r--chrome/installer/mini_installer_syzygy.gyp2
-rw-r--r--chrome/installer/setup/archive_patch_helper.cc105
-rw-r--r--chrome/installer/setup/archive_patch_helper.h97
-rw-r--r--chrome/installer/setup/archive_patch_helper_unittest.cc61
-rw-r--r--chrome/installer/setup/setup_main.cc732
-rw-r--r--chrome/installer/setup/setup_util.cc74
-rw-r--r--chrome/installer/setup/setup_util.h16
-rw-r--r--chrome/installer/setup/setup_util_unittest.cc181
-rw-r--r--chrome/installer/util/util_constants.cc10
-rw-r--r--chrome/installer/util/util_constants.h6
11 files changed, 849 insertions, 437 deletions
diff --git a/chrome/installer/mini_installer.gyp b/chrome/installer/mini_installer.gyp
index 62fa5bdf60..05d54d2875 100644
--- a/chrome/installer/mini_installer.gyp
+++ b/chrome/installer/mini_installer.gyp
@@ -132,7 +132,7 @@
'<(branding_dir)/BRANDING',
],
'outputs': [
- '<(INTERMEDIATE_DIR)/mini_installer_exe_version.rc',
+ '<(PRODUCT_DIR)/mini_installer_exe_version.rc',
],
'action': [
'python', '<(version_py)',
diff --git a/chrome/installer/mini_installer_syzygy.gyp b/chrome/installer/mini_installer_syzygy.gyp
index 3af4c5ceba..74050ab106 100644
--- a/chrome/installer/mini_installer_syzygy.gyp
+++ b/chrome/installer/mini_installer_syzygy.gyp
@@ -15,7 +15,7 @@
],
'conditions': [
# This target won't build in fastbuild, since there are no PDBs.
- ['OS=="win" and fastbuild==0 and chrome_split_dll==0', {
+ ['OS=="win" and fastbuild==0 and chrome_multiple_dll==0', {
'targets': [
{
'target_name': 'mini_installer_syzygy',
diff --git a/chrome/installer/setup/archive_patch_helper.cc b/chrome/installer/setup/archive_patch_helper.cc
new file mode 100644
index 0000000000..c45189de86
--- /dev/null
+++ b/chrome/installer/setup/archive_patch_helper.cc
@@ -0,0 +1,105 @@
+// Copyright 2013 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/archive_patch_helper.h"
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "chrome/installer/util/lzma_util.h"
+#include "courgette/courgette.h"
+#include "third_party/bspatch/mbspatch.h"
+
+namespace installer {
+
+ArchivePatchHelper::ArchivePatchHelper(const base::FilePath& working_directory,
+ const base::FilePath& compressed_archive,
+ const base::FilePath& patch_source,
+ const base::FilePath& target)
+ : working_directory_(working_directory),
+ compressed_archive_(compressed_archive),
+ patch_source_(patch_source),
+ target_(target) {}
+
+ArchivePatchHelper::~ArchivePatchHelper() {}
+
+// static
+bool ArchivePatchHelper::UncompressAndPatch(
+ const base::FilePath& working_directory,
+ const base::FilePath& compressed_archive,
+ const base::FilePath& patch_source,
+ const base::FilePath& target) {
+ ArchivePatchHelper instance(working_directory, compressed_archive,
+ patch_source, target);
+ return (instance.Uncompress(NULL) &&
+ (instance.EnsemblePatch() || instance.BinaryPatch()));
+}
+
+bool ArchivePatchHelper::Uncompress(base::FilePath* last_uncompressed_file) {
+ // The target shouldn't already exist.
+ DCHECK(!base::PathExists(target_));
+
+ // UnPackArchive takes care of logging.
+ string16 output_file;
+ int32 lzma_result = LzmaUtil::UnPackArchive(compressed_archive_.value(),
+ working_directory_.value(),
+ &output_file);
+ if (lzma_result != NO_ERROR)
+ return false;
+
+ last_uncompressed_file_ = base::FilePath(output_file);
+ if (last_uncompressed_file)
+ *last_uncompressed_file = last_uncompressed_file_;
+ return true;
+}
+
+bool ArchivePatchHelper::EnsemblePatch() {
+ if (last_uncompressed_file_.empty()) {
+ LOG(ERROR) << "No patch file found in compressed archive.";
+ return false;
+ }
+
+ courgette::Status result =
+ courgette::ApplyEnsemblePatch(patch_source_.value().c_str(),
+ last_uncompressed_file_.value().c_str(),
+ target_.value().c_str());
+ if (result == courgette::C_OK)
+ return true;
+
+ LOG(ERROR)
+ << "Failed to apply patch " << last_uncompressed_file_.value()
+ << " to file " << patch_source_.value()
+ << " and generating file " << target_.value()
+ << " using courgette. err=" << result;
+
+ // Ensure a partial output is not left behind.
+ base::DeleteFile(target_, false);
+
+ return false;
+}
+
+bool ArchivePatchHelper::BinaryPatch() {
+ if (last_uncompressed_file_.empty()) {
+ LOG(ERROR) << "No patch file found in compressed archive.";
+ return false;
+ }
+
+ int result = ApplyBinaryPatch(patch_source_.value().c_str(),
+ last_uncompressed_file_.value().c_str(),
+ target_.value().c_str());
+ if (result == OK)
+ return true;
+
+ LOG(ERROR)
+ << "Failed to apply patch " << last_uncompressed_file_.value()
+ << " to file " << patch_source_.value()
+ << " and generating file " << target_.value()
+ << " using bsdiff. err=" << result;
+
+ // Ensure a partial output is not left behind.
+ base::DeleteFile(target_, false);
+
+ return false;
+}
+
+} // namespace installer
diff --git a/chrome/installer/setup/archive_patch_helper.h b/chrome/installer/setup/archive_patch_helper.h
new file mode 100644
index 0000000000..99d430e252
--- /dev/null
+++ b/chrome/installer/setup/archive_patch_helper.h
@@ -0,0 +1,97 @@
+// Copyright 2013 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_SETUP_ARCHIVE_PATCH_HELPER_H_
+#define CHROME_INSTALLER_SETUP_ARCHIVE_PATCH_HELPER_H_
+
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+
+namespace installer {
+
+// A helper class that facilitates uncompressing and patching the chrome archive
+// and installer.
+//
+// Chrome's installer is deployed along with a compressed archive containing
+// either 1) an uncompressd archive of the product binaries or 2) a patch file
+// to be applied to the uncompressed archive of the version being updated. To
+// obtain the uncompressed archive, the contents of the compressed archive are
+// uncompressed and extracted. Installation proceeds directly if the
+// uncompressed archive is found after this step. Otherwise, the patch is
+// applied to the previous version's uncompressed archive using either
+// Courgette's ensemble patching or bspatch.
+//
+// Chrome's installer itself may also be deployed as a patch against the
+// previous version's saved installer binary. The same process is followed to
+// obtain the new installer. The compressed archive unconditionally contains a
+// patch file in this case.
+class ArchivePatchHelper {
+ public:
+ // Constructs an instance that can uncompress |compressed_archive| into
+ // |working_directory| and optionally apply the extracted patch file to
+ // |patch_source|, writing the result to |target|.
+ ArchivePatchHelper(const base::FilePath& working_directory,
+ const base::FilePath& compressed_archive,
+ const base::FilePath& patch_source,
+ const base::FilePath& target);
+
+ ~ArchivePatchHelper();
+
+ // Uncompresses |compressed_archive| in |working_directory| then applies the
+ // extracted patch file to |patch_source|, writing the result to |target|.
+ // Ensemble patching via Courgette is attempted first. If that fails, bspatch
+ // is attempted. Returns false if uncompression or both patching steps fail.
+ static bool UncompressAndPatch(const base::FilePath& working_directory,
+ const base::FilePath& compressed_archive,
+ const base::FilePath& patch_source,
+ const base::FilePath& target);
+
+ // Uncompresses compressed_archive() into the working directory. On success,
+ // last_uncompressed_file (if not NULL) is populated with the path to the last
+ // file extracted from the archive.
+ bool Uncompress(base::FilePath* last_uncompressed_file);
+
+ // Attempts to use courgette to apply last_uncompressed_file() to
+ // patch_source() to generate target(). Returns false if patching fails.
+ bool EnsemblePatch();
+
+ // Attempts to use bspatch to apply last_uncompressed_file() to patch_source()
+ // to generate target(). Returns false if patching fails.
+ bool BinaryPatch();
+
+ const base::FilePath& compressed_archive() const {
+ return compressed_archive_;
+ }
+ void set_patch_source(const base::FilePath& patch_source) {
+ patch_source_ = patch_source;
+ }
+ const base::FilePath& patch_source() const {
+ return patch_source_;
+ }
+ const base::FilePath& target() const {
+ return target_;
+ }
+
+ // Returns the path of the last file extracted by Uncompress().
+ const base::FilePath& last_uncompressed_file() const {
+ return last_uncompressed_file_;
+ }
+ void set_last_uncompressed_file(
+ const base::FilePath& last_uncompressed_file) {
+ last_uncompressed_file_ = last_uncompressed_file;
+ }
+
+ private:
+ base::FilePath working_directory_;
+ base::FilePath compressed_archive_;
+ base::FilePath patch_source_;
+ base::FilePath target_;
+ base::FilePath last_uncompressed_file_;
+
+ DISALLOW_COPY_AND_ASSIGN(ArchivePatchHelper);
+};
+
+} // namespace installer
+
+#endif // CHROME_INSTALLER_SETUP_ARCHIVE_PATCH_HELPER_H_
diff --git a/chrome/installer/setup/archive_patch_helper_unittest.cc b/chrome/installer/setup/archive_patch_helper_unittest.cc
new file mode 100644
index 0000000000..17ff00329b
--- /dev/null
+++ b/chrome/installer/setup/archive_patch_helper_unittest.cc
@@ -0,0 +1,61 @@
+// Copyright 2013 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/file_util.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/path_service.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/installer/setup/archive_patch_helper.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class ArchivePatchHelperTest : public testing::Test {
+ protected:
+ static void SetUpTestCase() {
+ ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_dir_));
+ data_dir_ = data_dir_.AppendASCII("installer");
+ ASSERT_TRUE(base::PathExists(data_dir_));
+ }
+
+ static void TearDownTestCase() {
+ data_dir_.clear();
+ }
+
+ virtual void SetUp() OVERRIDE {
+ // Create a temp directory for testing.
+ ASSERT_TRUE(test_dir_.CreateUniqueTempDir());
+ }
+
+ virtual void TearDown() OVERRIDE {
+ // Clean up test directory manually so we can fail if it leaks.
+ ASSERT_TRUE(test_dir_.Delete());
+ }
+
+ // The path to input data used in tests.
+ static base::FilePath data_dir_;
+
+ // The temporary directory used to contain the test operations.
+ base::ScopedTempDir test_dir_;
+};
+
+base::FilePath ArchivePatchHelperTest::data_dir_;
+
+} // namespace
+
+// Test that patching works.
+TEST_F(ArchivePatchHelperTest, Patching) {
+ base::FilePath src = data_dir_.AppendASCII("archive1.7z");
+ base::FilePath patch = data_dir_.AppendASCII("archive.diff");
+ base::FilePath dest = test_dir_.path().AppendASCII("archive2.7z");
+ installer::ArchivePatchHelper archive_helper(test_dir_.path(),
+ base::FilePath(),
+ src,
+ dest);
+ archive_helper.set_last_uncompressed_file(patch);
+ EXPECT_TRUE(archive_helper.EnsemblePatch() || archive_helper.BinaryPatch());
+ base::FilePath base = data_dir_.AppendASCII("archive2.7z");
+ EXPECT_TRUE(base::ContentsEqual(dest, base));
+}
diff --git a/chrome/installer/setup/setup_main.cc b/chrome/installer/setup/setup_main.cc
index dab094d2cf..4b59a2c0e4 100644
--- a/chrome/installer/setup/setup_main.cc
+++ b/chrome/installer/setup/setup_main.cc
@@ -33,6 +33,7 @@
#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/chrome_frame_quick_enable.h"
#include "chrome/installer/setup/chrome_frame_ready_mode.h"
#include "chrome/installer/setup/install.h"
@@ -85,71 +86,103 @@ const MINIDUMP_TYPE kLargerDumpType = static_cast<MINIDUMP_TYPE>(
namespace {
-// This method unpacks and uncompresses the given archive file. For Chrome
-// install we are creating a uncompressed archive that contains all the files
-// needed for the installer. This uncompressed archive is later compressed.
-//
-// This method first uncompresses archive specified by parameter "archive"
-// and assumes that it will result in an uncompressed full archive file
-// (chrome.7z) or uncompressed archive patch file (chrome_patch.diff). If it
-// is patch file, it is applied to the old archive file that should be
-// present on the system already. As the final step the new archive file
-// is unpacked in the path specified by parameter "output_directory".
-DWORD UnPackArchive(const base::FilePath& archive,
- const InstallerState& installer_state,
- const base::FilePath& temp_path,
- const base::FilePath& output_directory,
- installer::ArchiveType* archive_type) {
- DCHECK(archive_type);
-
- installer_state.UpdateStage(installer::UNCOMPRESSING);
+// 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);
+ }
- // First uncompress the payload. This could be a differential
- // update (patch.7z) or full archive (chrome.7z). If this uncompress fails
- // return with error.
- string16 unpacked_file;
- int32 ret = LzmaUtil::UnPackArchive(archive.value(), temp_path.value(),
- &unpacked_file);
- if (ret != NO_ERROR)
- return ret;
-
- base::FilePath uncompressed_archive(
- temp_path.Append(installer::kChromeArchive));
- scoped_ptr<Version> archive_version(
- installer::GetMaxVersionFromArchiveDir(installer_state.target_path()));
-
- // Check if this is differential update and if it is, patch it to the
- // installer archive that should already be on the machine. We assume
- // it is a differential installer if chrome.7z is not found.
- if (!base::PathExists(uncompressed_archive)) {
- *archive_type = installer::INCREMENTAL_ARCHIVE_TYPE;
- VLOG(1) << "Differential patch found. Applying to existing archive.";
- if (!archive_version.get()) {
- LOG(ERROR) << "Can not use differential update when Chrome is not "
- << "installed on the system.";
- return installer::CHROME_NOT_INSTALLED;
+ // 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>();
+ }
- base::FilePath existing_archive(installer_state.target_path().AppendASCII(
- archive_version->GetString()));
- existing_archive = existing_archive.Append(installer::kInstallerDir);
- existing_archive = existing_archive.Append(installer::kChromeArchive);
- if (int i = installer::ApplyDiffPatch(existing_archive,
- base::FilePath(unpacked_file),
- uncompressed_archive,
- &installer_state)) {
- LOG(ERROR) << "Binary patching failed with error " << i;
- return i;
- }
- } else {
+ // 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;
}
- installer_state.UpdateStage(installer::UNPACKING);
+ // 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;
+ }
- // Unpack the uncompressed archive.
- return LzmaUtil::UnPackArchive(uncompressed_archive.value(),
- output_directory.value(), &unpacked_file);
+ *archive_type = installer::INCREMENTAL_ARCHIVE_TYPE;
+ return true;
}
// In multi-install, adds all products to |installer_state| that are
@@ -627,6 +660,32 @@ bool CheckPreInstallConditions(const InstallationState& original_state,
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 (!file_util::CreateTemporaryDirInDir(temp_path->path(),
+ installer::kInstallSourceDir,
+ unpack_path)) {
+ PLOG(ERROR) << "Could not create temporary path for unpacked archive.";
+ return false;
+ }
+
+ return true;
+}
+
installer::InstallStatus InstallProductsHelper(
const InstallationState& original_state,
const CommandLine& cmd_line,
@@ -635,6 +694,7 @@ installer::InstallStatus InstallProductsHelper(
installer::ArchiveType* archive_type,
bool* delegated_to_existing) {
DCHECK(archive_type);
+ DCHECK(delegated_to_existing);
const bool system_install = installer_state.system_install();
installer::InstallStatus install_status = installer::UNKNOWN_STATUS;
@@ -643,308 +703,284 @@ installer::InstallStatus InstallProductsHelper(
bool entered_background_mode = installer::AdjustProcessPriority();
VLOG_IF(1, entered_background_mode) << "Entered background processing mode.";
- // For install the default location for chrome.packed.7z is in current
- // folder, so get that value first.
- base::FilePath archive(cmd_line.GetProgram().DirName().Append(
- installer::kChromeCompressedArchive));
-
- // If --install-archive is given, get the user specified value
- if (cmd_line.HasSwitch(installer::switches::kInstallArchive)) {
- archive = cmd_line.GetSwitchValuePath(
- installer::switches::kInstallArchive);
- }
-
- const Products& products = installer_state.products();
-
// 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.
installer::SelfCleaningTempDir temp_path;
- if (!temp_path.Initialize(installer_state.target_path().DirName(),
- installer::kInstallTempDir)) {
- PLOG(ERROR) << "Could not create temporary path.";
+ base::FilePath unpack_path;
+ if (!CreateTemporaryAndUnpackDirectories(installer_state, &temp_path,
+ &unpack_path)) {
installer_state.WriteInstallerResult(installer::TEMP_DIR_FAILED,
- IDS_INSTALL_TEMP_DIR_FAILED_BASE, NULL);
+ IDS_INSTALL_TEMP_DIR_FAILED_BASE,
+ NULL);
return installer::TEMP_DIR_FAILED;
}
- VLOG(1) << "created path " << temp_path.path().value();
- base::FilePath unpack_path;
- if (!file_util::CreateTemporaryDirInDir(temp_path.path(),
- installer::kInstallSourceDir,
- &unpack_path)) {
- PLOG(ERROR) << "Could not create temporary path for unpacked archive.";
- installer_state.WriteInstallerResult(installer::TEMP_DIR_FAILED,
- IDS_INSTALL_TEMP_DIR_FAILED_BASE, NULL);
- return installer::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 = installer::UNKNOWN_ARCHIVE_TYPE;
+ base::FilePath uncompressed_archive(cmd_line.GetSwitchValuePath(
+ installer::switches::kUncompressedArchive));
+ if (uncompressed_archive.empty()) {
+ scoped_ptr<installer::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, installer::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(installer::kChromeArchive);
}
- bool unpacked = false;
-
- // We want to keep uncompressed archive (chrome.7z) that we get after
- // uncompressing and binary patching. Get the location for this file.
- base::FilePath archive_to_copy;
- if (base::PathExists(archive)) {
- VLOG(1) << "Archive found to install Chrome " << archive.value();
- if (UnPackArchive(archive, installer_state, temp_path.path(), unpack_path,
- archive_type)) {
- install_status = (*archive_type) == installer::INCREMENTAL_ARCHIVE_TYPE ?
- installer::APPLY_DIFF_PATCH_FAILED : installer::UNCOMPRESSION_FAILED;
+ if (*archive_type == installer::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(
- install_status,
- IDS_INSTALL_UNCOMPRESSION_FAILED_BASE,
- NULL);
- } else {
- unpacked = true;
- archive_to_copy = temp_path.path().Append(installer::kChromeArchive);
+ installer::INVALID_ARCHIVE, IDS_INSTALL_INVALID_ARCHIVE_BASE, NULL);
+ return installer::INVALID_ARCHIVE;
}
+ *archive_type = installer::FULL_ARCHIVE_TYPE;
+ }
+
+ // Unpack the uncompressed archive.
+ if (LzmaUtil::UnPackArchive(uncompressed_archive.value(),
+ unpack_path.value(),
+ NULL)) {
+ installer_state.WriteInstallerResult(
+ installer::UNCOMPRESSION_FAILED,
+ IDS_INSTALL_UNCOMPRESSION_FAILED_BASE,
+ NULL);
+ return installer::UNCOMPRESSION_FAILED;
+ }
+
+ VLOG(1) << "unpacked to " << unpack_path.value();
+ base::FilePath src_path(
+ unpack_path.Append(installer::kInstallSourceChromeDir));
+ scoped_ptr<Version>
+ installer_version(installer::GetMaxVersionFromArchiveDir(src_path));
+ if (!installer_version.get()) {
+ LOG(ERROR) << "Did not find any valid version in installer.";
+ install_status = installer::INVALID_ARCHIVE;
+ installer_state.WriteInstallerResult(install_status,
+ IDS_INSTALL_INVALID_ARCHIVE_BASE, NULL);
} else {
- base::FilePath uncompressed_archive(cmd_line.GetProgram().DirName().Append(
- installer::kChromeArchive));
-
- if (base::PathExists(uncompressed_archive)) {
- VLOG(1) << "Uncompressed archive found to install Chrome "
- << uncompressed_archive.value();
- *archive_type = installer::FULL_ARCHIVE_TYPE;
- string16 unpacked_file;
- if (LzmaUtil::UnPackArchive(uncompressed_archive.value(),
- unpack_path.value(), &unpacked_file)) {
- installer_state.WriteInstallerResult(
- installer::UNCOMPRESSION_FAILED,
- IDS_INSTALL_UNCOMPRESSION_FAILED_BASE,
- NULL);
- } else {
- unpacked = true;
- archive_to_copy = uncompressed_archive;
+ 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(installer::DEFERRING_TO_HIGHER_VERSION);
+ if (DeferToExistingInstall(setup_exe, cmd_line, installer_state,
+ temp_path.path(), &install_status)) {
+ *delegated_to_existing = true;
+ return install_status;
+ }
}
}
- }
- if (unpacked) {
- VLOG(1) << "unpacked to " << unpack_path.value();
- base::FilePath src_path(
- unpack_path.Append(installer::kInstallSourceChromeDir));
- scoped_ptr<Version>
- installer_version(installer::GetMaxVersionFromArchiveDir(src_path));
- if (!installer_version.get()) {
- LOG(ERROR) << "Did not find any valid version in installer.";
- install_status = installer::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(installer::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()->GetAppShortCutName()
+ << " is already installed.";
+ higher_products |= (1 << product.distribution()->GetType());
}
+ }
- 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()->GetAppShortCutName()
- << " 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 kGCFBit = 1 << BrowserDistribution::CHROME_FRAME;
+ const uint32 kAppHostBit = 1 << BrowserDistribution::CHROME_APP_HOST;
+ int message_id = 0;
+
+ proceed_with_installation = false;
+ install_status = installer::HIGHER_VERSION_EXISTS;
+ switch (higher_products) {
+ case kBrowserBit:
+ message_id = IDS_INSTALL_HIGHER_VERSION_BASE;
+ break;
+ case kGCFBit:
+ message_id = IDS_INSTALL_HIGHER_VERSION_CF_BASE;
+ break;
+ case kGCFBit | kBrowserBit:
+ message_id = IDS_INSTALL_HIGHER_VERSION_CB_CF_BASE;
+ break;
+ default:
+ message_id = IDS_INSTALL_HIGHER_VERSION_APP_LAUNCHER_BASE;
+ break;
}
- 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 kGCFBit = 1 << BrowserDistribution::CHROME_FRAME;
- const uint32 kAppHostBit = 1 << BrowserDistribution::CHROME_APP_HOST;
- int message_id = 0;
-
- proceed_with_installation = false;
- install_status = installer::HIGHER_VERSION_EXISTS;
- switch (higher_products) {
- case kBrowserBit:
- message_id = IDS_INSTALL_HIGHER_VERSION_BASE;
- break;
- case kGCFBit:
- message_id = IDS_INSTALL_HIGHER_VERSION_CF_BASE;
- break;
- case kGCFBit | kBrowserBit:
- message_id = IDS_INSTALL_HIGHER_VERSION_CB_CF_BASE;
- break;
- default:
- message_id = IDS_INSTALL_HIGHER_VERSION_APP_LAUNCHER_BASE;
- break;
- }
+ installer_state.WriteInstallerResult(install_status, message_id, NULL);
+ }
- 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 = installer::INSTALL_OF_GOOGLE_UPDATE_FAILED;
+ installer_state.WriteInstallerResult(install_status, 0, 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 = installer::INSTALL_OF_GOOGLE_UPDATE_FAILED;
- installer_state.WriteInstallerResult(install_status, 0, NULL);
- }
+ if (proceed_with_installation) {
+ base::FilePath prefs_source_path(cmd_line.GetSwitchValueNative(
+ installer::switches::kInstallerData));
+ install_status = installer::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 == installer::SAME_VERSION_REPAIR_FAILED) {
+ if (installer_state.FindProduct(BrowserDistribution::CHROME_FRAME)) {
+ install_msg_base = IDS_SAME_VERSION_REPAIR_FAILED_CF_BASE;
+ } else {
+ install_msg_base = IDS_SAME_VERSION_REPAIR_FAILED_BASE;
+ }
+ } else if (install_status != installer::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 = installer::OS_ERROR;
+ } else {
+ chrome_exe = installer_state.target_path()
+ .Append(installer::kChromeExe).value();
+ quoted_chrome_exe = L"\"" + chrome_exe + L"\"";
+ install_msg_base = 0;
}
}
- if (proceed_with_installation) {
- base::FilePath prefs_source_path(cmd_line.GetSwitchValueNative(
- installer::switches::kInstallerData));
- install_status = installer::InstallOrUpdateProduct(
- original_state, installer_state, cmd_line.GetProgram(),
- archive_to_copy, 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 == installer::SAME_VERSION_REPAIR_FAILED) {
- if (installer_state.FindProduct(BrowserDistribution::CHROME_FRAME)) {
- install_msg_base = IDS_SAME_VERSION_REPAIR_FAILED_CF_BASE;
- } else {
- install_msg_base = IDS_SAME_VERSION_REPAIR_FAILED_BASE;
- }
- } else if (install_status != installer::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 = installer::OS_ERROR;
- } else {
- chrome_exe = installer_state.target_path()
- .Append(installer::kChromeExe).value();
- quoted_chrome_exe = L"\"" + chrome_exe + L"\"";
- install_msg_base = 0;
- }
- }
+ installer_state.UpdateStage(installer::FINISHING);
- installer_state.UpdateStage(installer::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;
- // 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(
+ installer::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 != installer::IN_USE_UPDATED);
+
+ installer_state.WriteInstallerResult(install_status, install_msg_base,
+ write_chrome_launch_string ? &quoted_chrome_exe : NULL);
- bool do_not_register_for_update_launch = false;
+ if (install_status == installer::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(
- installer::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 != installer::IN_USE_UPDATED);
-
- installer_state.WriteInstallerResult(install_status, install_msg_base,
- write_chrome_launch_string ? &quoted_chrome_exe : NULL);
-
- if (install_status == installer::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(
- installer::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 == installer::NEW_VERSION_UPDATED) ||
- (install_status == installer::IN_USE_UPDATED)) {
- const Product* chrome = installer_state.FindProduct(
- BrowserDistribution::CHROME_BROWSER);
- if (chrome != NULL) {
- DCHECK_NE(chrome_exe, string16());
- installer::RemoveChromeLegacyRegistryKeys(chrome->distribution(),
- chrome_exe);
- }
+ installer::master_preferences::kDoNotLaunchChrome,
+ &do_not_launch_chrome);
+ if (!system_install && !do_not_launch_chrome)
+ chrome_install->LaunchChrome(installer_state.target_path());
}
-
- if (prefs.install_chrome_app_launcher() &&
- InstallUtil::GetInstallReturnCode(install_status) == 0) {
- std::string webstore_item(google_update::GetUntrustedDataValue(
- installer::kInstallFromWebstore));
- if (!webstore_item.empty()) {
- bool success = installer::InstallFromWebstore(webstore_item);
- VLOG_IF(1, !success) << "Failed to launch app installation.";
- }
+ } else if ((install_status == installer::NEW_VERSION_UPDATED) ||
+ (install_status == installer::IN_USE_UPDATED)) {
+ const Product* chrome = installer_state.FindProduct(
+ BrowserDistribution::CHROME_BROWSER);
+ if (chrome != NULL) {
+ DCHECK_NE(chrome_exe, string16());
+ installer::RemoveChromeLegacyRegistryKeys(chrome->distribution(),
+ chrome_exe);
}
}
- }
- // 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());
- }
- for (Products::const_iterator it = products.begin(); it < products.end();
- ++it) {
- const Product& product = **it;
- product.LaunchUserExperiment(setup_path, install_status,
- system_install);
+ if (prefs.install_chrome_app_launcher() &&
+ InstallUtil::GetInstallReturnCode(install_status) == 0) {
+ std::string webstore_item(google_update::GetUntrustedDataValue(
+ installer::kInstallFromWebstore));
+ if (!webstore_item.empty()) {
+ bool success = installer::InstallFromWebstore(webstore_item);
+ VLOG_IF(1, !success) << "Failed to launch app installation.";
+ }
}
}
}
- // Delete the master profile 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, true)) {
- LOG(ERROR) << "Failed deleting master preferences file "
- << prefs_path.value()
- << ", scheduling for deletion after reboot.";
- ScheduleFileSystemEntityForDeletion(prefs_path.value().c_str());
+ // 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);
}
}
@@ -964,7 +1000,7 @@ installer::InstallStatus InstallProducts(
const bool system_install = installer_state->system_install();
installer::InstallStatus install_status = installer::UNKNOWN_STATUS;
installer::ArchiveType archive_type = installer::UNKNOWN_ARCHIVE_TYPE;
- bool incremental_install = false;
+ 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.
@@ -973,21 +1009,34 @@ installer::InstallStatus InstallProducts(
if (CheckPreInstallConditions(original_state, installer_state,
&install_status)) {
VLOG(1) << "Installing to " << installer_state->target_path().value();
- bool delegated_to_existing = false;
install_status = InstallProductsHelper(
original_state, cmd_line, prefs, *installer_state, &archive_type,
&delegated_to_existing);
- // 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;
} else {
// CheckPreInstallConditions must set the status on failure.
DCHECK_NE(install_status, installer::UNKNOWN_STATUS);
}
- const Products& products = installer_state->products();
+ // 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.value().c_str());
+ }
+ }
+ // 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(
@@ -1261,20 +1310,15 @@ bool HandleNonInstallCmdLineOptions(const InstallationState& original_state,
if (!temp_path.CreateUniqueTempDir()) {
PLOG(ERROR) << "Could not create temporary path.";
} else {
- string16 setup_patch = cmd_line.GetSwitchValueNative(
- installer::switches::kUpdateSetupExe);
- VLOG(1) << "Opening archive " << setup_patch;
- string16 uncompressed_patch;
- if (LzmaUtil::UnPackArchive(setup_patch, temp_path.path().value(),
- &uncompressed_patch) == NO_ERROR) {
- base::FilePath old_setup_exe = cmd_line.GetProgram();
- base::FilePath new_setup_exe = cmd_line.GetSwitchValuePath(
- installer::switches::kNewSetupExe);
- if (!installer::ApplyDiffPatch(old_setup_exe,
- base::FilePath(uncompressed_patch),
- new_setup_exe,
- installer_state))
- status = installer::NEW_VERSION_UPDATED;
+ 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
diff --git a/chrome/installer/setup/setup_util.cc b/chrome/installer/setup/setup_util.cc
index d67927402c..4ca4447c36 100644
--- a/chrome/installer/setup/setup_util.cc
+++ b/chrome/installer/setup/setup_util.cc
@@ -17,6 +17,7 @@
#include "base/strings/string_util.h"
#include "base/version.h"
#include "base/win/windows_version.h"
+#include "chrome/installer/setup/setup_constants.h"
#include "chrome/installer/util/copy_tree_work_item.h"
#include "chrome/installer/util/installation_state.h"
#include "chrome/installer/util/installer_state.h"
@@ -134,53 +135,6 @@ int BsdiffPatchFiles(const base::FilePath& src,
return exit_code;
}
-int ApplyDiffPatch(const base::FilePath& src,
- const base::FilePath& patch,
- const base::FilePath& dest,
- const InstallerState* installer_state) {
- VLOG(1) << "Applying patch " << patch.value() << " to file "
- << src.value() << " and generating file " << dest.value();
-
- if (installer_state != NULL)
- installer_state->UpdateStage(installer::ENSEMBLE_PATCHING);
-
- // Try Courgette first. Courgette checks the patch file first and fails
- // quickly if the patch file does not have a valid Courgette header.
- courgette::Status patch_status =
- courgette::ApplyEnsemblePatch(src.value().c_str(),
- patch.value().c_str(),
- dest.value().c_str());
- if (patch_status == courgette::C_OK)
- return 0;
-
- LOG(ERROR)
- << "Failed to apply patch " << patch.value()
- << " to file " << src.value() << " and generating file " << dest.value()
- << " using courgette. err=" << patch_status;
-
- // If we ran out of memory or disk space, then these are likely the errors
- // we will see. If we run into them, return an error and stay on the
- // 'ENSEMBLE_PATCHING' update stage.
- if (patch_status == courgette::C_DISASSEMBLY_FAILED ||
- patch_status == courgette::C_STREAM_ERROR) {
- return MEM_ERROR;
- }
-
- if (installer_state != NULL)
- installer_state->UpdateStage(installer::BINARY_PATCHING);
-
- int binary_patch_status = ApplyBinaryPatch(src.value().c_str(),
- patch.value().c_str(),
- dest.value().c_str());
-
- LOG_IF(ERROR, binary_patch_status != OK)
- << "Failed to apply patch " << patch.value()
- << " to file " << src.value() << " and generating file " << dest.value()
- << " using bsdiff. err=" << binary_patch_status;
-
- return binary_patch_status;
-}
-
Version* GetMaxVersionFromArchiveDir(const base::FilePath& chrome_path) {
VLOG(1) << "Looking for Chrome version folder under " << chrome_path.value();
Version* version = NULL;
@@ -208,6 +162,32 @@ Version* GetMaxVersionFromArchiveDir(const base::FilePath& chrome_path) {
return (version_found ? max_version.release() : NULL);
}
+base::FilePath FindArchiveToPatch(const InstallationState& original_state,
+ const InstallerState& installer_state) {
+ // Check based on the version number advertised to Google Update, since that
+ // is the value used to select a specific differential update. If an archive
+ // can't be found using that, fallback to using the newest version present.
+ base::FilePath patch_source;
+ const ProductState* product =
+ original_state.GetProductState(installer_state.system_install(),
+ installer_state.state_type());
+ if (product) {
+ patch_source = installer_state.GetInstallerDirectory(product->version())
+ .Append(installer::kChromeArchive);
+ if (base::PathExists(patch_source))
+ return patch_source;
+ }
+ scoped_ptr<Version> version(
+ installer::GetMaxVersionFromArchiveDir(installer_state.target_path()));
+ if (version) {
+ patch_source = installer_state.GetInstallerDirectory(*version)
+ .Append(installer::kChromeArchive);
+ if (base::PathExists(patch_source))
+ return patch_source;
+ }
+ return base::FilePath();
+}
+
bool DeleteFileFromTempProcess(const base::FilePath& path,
uint32 delay_before_delete_ms) {
static const wchar_t kRunDll32Path[] =
diff --git a/chrome/installer/setup/setup_util.h b/chrome/installer/setup/setup_util.h
index 97bf4d4715..031b1cb99b 100644
--- a/chrome/installer/setup/setup_util.h
+++ b/chrome/installer/setup/setup_util.h
@@ -30,17 +30,6 @@ class InstallationState;
class InstallerState;
class ProductState;
-// Apply a diff patch to source file. First tries to apply it using Courgette
-// since it checks for Courgette header and fails quickly. If that fails
-// tries to apply the patch using regular bsdiff. Returns status code as
-// defined by the bsdiff code (see third_party/bspatch/mbspatch.h for the
-// definitions of the codes).
-// The installer stage is updated if |installer_state| is non-NULL.
-int ApplyDiffPatch(const base::FilePath& src,
- const base::FilePath& patch,
- const base::FilePath& dest,
- const InstallerState* installer_state);
-
// Applies a patch file to source file using Courgette. Returns 0 in case of
// success. In case of errors, it returns kCourgetteErrorOffset + a Courgette
// status code, as defined in courgette/courgette.h
@@ -61,6 +50,11 @@ int BsdiffPatchFiles(const base::FilePath& src,
// Returns the maximum version found or NULL if no version is found.
Version* GetMaxVersionFromArchiveDir(const base::FilePath& chrome_path);
+// Returns the uncompressed archive of the installed version that serves as the
+// source for patching.
+base::FilePath FindArchiveToPatch(const InstallationState& original_state,
+ const InstallerState& installer_state);
+
// Spawns a new process that waits for a specified amount of time before
// attempting to delete |path|. This is useful for setup to delete the
// currently running executable or a file that we cannot close right away but
diff --git a/chrome/installer/setup/setup_util_unittest.cc b/chrome/installer/setup/setup_util_unittest.cc
index 177d548f80..490475ccb8 100644
--- a/chrome/installer/setup/setup_util_unittest.cc
+++ b/chrome/installer/setup/setup_util_unittest.cc
@@ -12,39 +12,35 @@
#include "base/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/memory/scoped_ptr.h"
-#include "base/path_service.h"
#include "base/process_util.h"
#include "base/threading/platform_thread.h"
#include "base/time/time.h"
+#include "base/version.h"
#include "base/win/scoped_handle.h"
#include "base/win/windows_version.h"
-#include "chrome/common/chrome_paths.h"
#include "chrome/installer/setup/setup_util.h"
+#include "chrome/installer/setup/setup_constants.h"
+#include "chrome/installer/util/installation_state.h"
+#include "chrome/installer/util/installer_state.h"
+#include "chrome/installer/util/util_constants.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
class SetupUtilTestWithDir : public testing::Test {
protected:
- virtual void SetUp() {
- ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_dir_));
- data_dir_ = data_dir_.AppendASCII("installer");
- ASSERT_TRUE(base::PathExists(data_dir_));
-
+ virtual void SetUp() OVERRIDE {
// Create a temp directory for testing.
ASSERT_TRUE(test_dir_.CreateUniqueTempDir());
}
- virtual void TearDown() {
+ virtual void TearDown() OVERRIDE {
// Clean up test directory manually so we can fail if it leaks.
ASSERT_TRUE(test_dir_.Delete());
}
// The temporary directory used to contain the test operations.
base::ScopedTempDir test_dir_;
-
- // The path to input data used in tests.
- base::FilePath data_dir_;
};
// The privilege tested in ScopeTokenPrivilege tests below.
@@ -95,26 +91,6 @@ bool CurrentProcessHasPrivilege(const wchar_t* privilege_name) {
} // namespace
// Test that we are parsing Chrome version correctly.
-TEST_F(SetupUtilTestWithDir, ApplyDiffPatchTest) {
- base::FilePath work_dir(test_dir_.path());
- work_dir = work_dir.AppendASCII("ApplyDiffPatchTest");
- ASSERT_FALSE(base::PathExists(work_dir));
- EXPECT_TRUE(file_util::CreateDirectory(work_dir));
- ASSERT_TRUE(base::PathExists(work_dir));
-
- base::FilePath src = data_dir_.AppendASCII("archive1.7z");
- base::FilePath patch = data_dir_.AppendASCII("archive.diff");
- base::FilePath dest = work_dir.AppendASCII("archive2.7z");
- EXPECT_EQ(installer::ApplyDiffPatch(src, patch, dest, NULL), 0);
- base::FilePath base = data_dir_.AppendASCII("archive2.7z");
- EXPECT_TRUE(base::ContentsEqual(dest, base));
-
- EXPECT_EQ(installer::ApplyDiffPatch(base::FilePath(), base::FilePath(),
- base::FilePath(), NULL),
- 6);
-}
-
-// Test that we are parsing Chrome version correctly.
TEST_F(SetupUtilTestWithDir, GetMaxVersionFromArchiveDirTest) {
// Create a version dir
base::FilePath chrome_dir = test_dir_.path().AppendASCII("1.0.0.0");
@@ -277,3 +253,146 @@ TEST(SetupUtilTest, AdjustFromBelowNormalPriority) {
else
EXPECT_EQ(PCCR_UNCHANGED, RelaunchAndDoProcessPriorityAdjustment());
}
+
+namespace {
+
+// A test fixture that configures an InstallationState and an InstallerState
+// with a product being updated.
+class FindArchiveToPatchTest : public SetupUtilTestWithDir {
+ protected:
+ class FakeInstallationState : public installer::InstallationState {
+ };
+
+ class FakeProductState : public installer::ProductState {
+ public:
+ static FakeProductState* FromProductState(const ProductState* product) {
+ return static_cast<FakeProductState*>(const_cast<ProductState*>(product));
+ }
+
+ void set_version(const Version& version) {
+ if (version.IsValid())
+ version_.reset(new Version(version));
+ else
+ version_.reset();
+ }
+
+ void set_uninstall_command(const CommandLine& uninstall_command) {
+ uninstall_command_ = uninstall_command;
+ }
+ };
+
+ virtual void SetUp() OVERRIDE {
+ SetupUtilTestWithDir::SetUp();
+ product_version_ = Version("30.0.1559.0");
+ max_version_ = Version("47.0.1559.0");
+
+ // Install the product according to the version.
+ original_state_.reset(new FakeInstallationState());
+ InstallProduct();
+
+ // Prepare to update the product in the temp dir.
+ installer_state_.reset(new installer::InstallerState(
+ kSystemInstall_ ? installer::InstallerState::SYSTEM_LEVEL :
+ installer::InstallerState::USER_LEVEL));
+ installer_state_->AddProductFromState(
+ kProductType_,
+ *original_state_->GetProductState(kSystemInstall_, kProductType_));
+
+ // Create archives in the two version dirs.
+ ASSERT_TRUE(
+ file_util::CreateDirectory(GetProductVersionArchivePath().DirName()));
+ ASSERT_EQ(1, file_util::WriteFile(GetProductVersionArchivePath(), "a", 1));
+ ASSERT_TRUE(
+ file_util::CreateDirectory(GetMaxVersionArchivePath().DirName()));
+ ASSERT_EQ(1, file_util::WriteFile(GetMaxVersionArchivePath(), "b", 1));
+ }
+
+ virtual void TearDown() OVERRIDE {
+ original_state_.reset();
+ SetupUtilTestWithDir::TearDown();
+ }
+
+ base::FilePath GetArchivePath(const Version& version) const {
+ return test_dir_.path()
+ .AppendASCII(version.GetString())
+ .Append(installer::kInstallerDir)
+ .Append(installer::kChromeArchive);
+ }
+
+ base::FilePath GetMaxVersionArchivePath() const {
+ return GetArchivePath(max_version_);
+ }
+
+ base::FilePath GetProductVersionArchivePath() const {
+ return GetArchivePath(product_version_);
+ }
+
+ void InstallProduct() {
+ FakeProductState* product = FakeProductState::FromProductState(
+ original_state_->GetNonVersionedProductState(kSystemInstall_,
+ kProductType_));
+
+ product->set_version(product_version_);
+ CommandLine uninstall_command(
+ test_dir_.path().AppendASCII(product_version_.GetString())
+ .Append(installer::kInstallerDir)
+ .Append(installer::kSetupExe));
+ uninstall_command.AppendSwitch(installer::switches::kUninstall);
+ product->set_uninstall_command(uninstall_command);
+ }
+
+ void UninstallProduct() {
+ FakeProductState::FromProductState(
+ original_state_->GetNonVersionedProductState(kSystemInstall_,
+ kProductType_))
+ ->set_version(Version());
+ }
+
+ static const bool kSystemInstall_;
+ static const BrowserDistribution::Type kProductType_;
+ Version product_version_;
+ Version max_version_;
+ scoped_ptr<FakeInstallationState> original_state_;
+ scoped_ptr<installer::InstallerState> installer_state_;
+};
+
+const bool FindArchiveToPatchTest::kSystemInstall_ = false;
+const BrowserDistribution::Type FindArchiveToPatchTest::kProductType_ =
+ BrowserDistribution::CHROME_BROWSER;
+
+} // namespace
+
+// Test that the path to the advertised product version is found.
+TEST_F(FindArchiveToPatchTest, ProductVersionFound) {
+ base::FilePath patch_source(installer::FindArchiveToPatch(
+ *original_state_, *installer_state_));
+ EXPECT_EQ(GetProductVersionArchivePath().value(), patch_source.value());
+}
+
+// Test that the path to the max version is found if the advertised version is
+// missing.
+TEST_F(FindArchiveToPatchTest, MaxVersionFound) {
+ // The patch file is absent.
+ ASSERT_TRUE(base::DeleteFile(GetProductVersionArchivePath(), false));
+ base::FilePath patch_source(installer::FindArchiveToPatch(
+ *original_state_, *installer_state_));
+ EXPECT_EQ(GetMaxVersionArchivePath().value(), patch_source.value());
+
+ // The product doesn't appear to be installed, so the max version is found.
+ UninstallProduct();
+ patch_source = installer::FindArchiveToPatch(
+ *original_state_, *installer_state_);
+ EXPECT_EQ(GetMaxVersionArchivePath().value(), patch_source.value());
+}
+
+// Test that an empty path is returned if no version is found.
+TEST_F(FindArchiveToPatchTest, NoVersionFound) {
+ // The product doesn't appear to be installed and no archives are present.
+ UninstallProduct();
+ ASSERT_TRUE(base::DeleteFile(GetProductVersionArchivePath(), false));
+ ASSERT_TRUE(base::DeleteFile(GetMaxVersionArchivePath(), false));
+
+ base::FilePath patch_source(installer::FindArchiveToPatch(
+ *original_state_, *installer_state_));
+ EXPECT_EQ(base::FilePath::StringType(), patch_source.value());
+}
diff --git a/chrome/installer/util/util_constants.cc b/chrome/installer/util/util_constants.cc
index e293e76af9..962012a33a 100644
--- a/chrome/installer/util/util_constants.cc
+++ b/chrome/installer/util/util_constants.cc
@@ -94,7 +94,10 @@ const char kForceConfigureUserSettings[] = "force-configure-user-settings";
// confirmation from user.
const char kForceUninstall[] = "force-uninstall";
-// Specify the file path of Chrome archive for install.
+// Specify the path to the compressed Chrome archive for install. If not
+// specified, chrome.packed.7z or chrome.7z in the same directory as setup.exe
+// is used (the packed file is preferred; see kUncompressedArchive to force use
+// of an uncompressed archive).
const char kInstallArchive[] = "install-archive";
// Specify the file path of Chrome master preference file.
@@ -174,6 +177,10 @@ const char kUninstall[] = "uninstall";
// path given by --new-setup-exe.
const char kUpdateSetupExe[] = "update-setup-exe";
+// Use the given uncompressed chrome.7z archive as the source of files to
+// install.
+const char kUncompressedArchive[] = "uncompressed-archive";
+
// Enable verbose logging (info level).
const char kVerboseLogging[] = "verbose-logging";
@@ -219,6 +226,7 @@ const char kOutputFile[] = "output-file";
const wchar_t kActiveSetupExe[] = L"chrmstp.exe";
const wchar_t kChromeAppHostExe[] = L"app_host.exe";
const wchar_t kChromeDll[] = L"chrome.dll";
+const wchar_t kChromeChildDll[] = L"chrome_child.dll";
const wchar_t kChromeExe[] = L"chrome.exe";
const wchar_t kChromeFrameDll[] = L"npchrome_frame.dll";
const wchar_t kChromeFrameHelperExe[] = L"chrome_frame_helper.exe";
diff --git a/chrome/installer/util/util_constants.h b/chrome/installer/util/util_constants.h
index 3f9a496a5f..b651a61339 100644
--- a/chrome/installer/util/util_constants.h
+++ b/chrome/installer/util/util_constants.h
@@ -82,12 +82,14 @@ enum InstallStatus {
WAIT_FOR_EXISTING_FAILED, // 48. OS error waiting for existing setup.exe.
PATCH_INVALID_ARGUMENTS, // 49. The arguments of --patch were missing or
// they were invalid for any reason.
+ DIFF_PATCH_SOURCE_MISSING, // 50. No previous version archive found for
+ // differential update.
// Friendly reminder: note the COMPILE_ASSERT below.
};
// Existing InstallStatus values must not change. Always add to the end.
-COMPILE_ASSERT(installer::PATCH_INVALID_ARGUMENTS == 49,
+COMPILE_ASSERT(installer::DIFF_PATCH_SOURCE_MISSING == 50,
dont_change_enum);
// The type of an update archive.
@@ -173,6 +175,7 @@ extern const char kSelfDestruct[];
extern const char kSystemLevel[];
extern const char kUninstall[];
extern const char kUpdateSetupExe[];
+extern const char kUncompressedArchive[];
extern const char kVerboseLogging[];
extern const char kShowEula[];
extern const char kShowEulaForMetro[];
@@ -190,6 +193,7 @@ extern const char kOutputFile[];
extern const wchar_t kActiveSetupExe[];
extern const wchar_t kChromeAppHostExe[];
extern const wchar_t kChromeDll[];
+extern const wchar_t kChromeChildDll[];
extern const wchar_t kChromeExe[];
extern const wchar_t kChromeFrameDll[];
extern const wchar_t kChromeFrameHelperExe[];