summaryrefslogtreecommitdiffstats
path: root/chrome/installer/setup
diff options
context:
space:
mode:
authorgrt@chromium.org <grt@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-22 19:20:01 +0000
committergrt@chromium.org <grt@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-07-22 19:20:01 +0000
commit855c4bae1a910dfd021a671d8150f8c87f5cb14b (patch)
tree9c1da6bb8e28556151a75cd5160f747642e77df0 /chrome/installer/setup
parent34404b66acf8b88ed094f2492069c299fc7fd1a1 (diff)
downloadchromium_src-855c4bae1a910dfd021a671d8150f8c87f5cb14b.zip
chromium_src-855c4bae1a910dfd021a671d8150f8c87f5cb14b.tar.gz
chromium_src-855c4bae1a910dfd021a671d8150f8c87f5cb14b.tar.bz2
InstallProductsHelper tweaks to support --uncompressed-archive.
setup.exe can now: * take an optional --uncompressed-archive=PATH argument to specify an uncompressed chrome.7z file from which the Chrome-bin directory can be extracted, and * do processing after uncompressing and patching the archive but before extracting files. This latter feature makes it possible for setup.exe to delegate to another instance for migrating Chrome Frame installs from multi- to single-install. This change also modifies the policy for finding the original archive file to be patched when a differential update is applied. Previously, the newest version on disk was unconditionally used. While this ordinarily results in using the right version, the version identified to Google Update by way of the "pv" value in the app's Clients key is the one used to select the differential update. As of this change, the archive for the version published in the registry is preferred. The most recent on disk used if the published one cannot be found. BUG=none Review URL: https://chromiumcodereview.appspot.com/18652010 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@212934 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/installer/setup')
-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
7 files changed, 833 insertions, 433 deletions
diff --git a/chrome/installer/setup/archive_patch_helper.cc b/chrome/installer/setup/archive_patch_helper.cc
new file mode 100644
index 0000000..c45189d
--- /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 0000000..99d430e
--- /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 0000000..17ff003
--- /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 dab094d..4b59a2c 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 d679274..4ca4447 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 97bf4d4..031b1cb 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 177d548..490475c 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());
+}