diff options
author | grt@chromium.org <grt@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-11 18:23:30 +0000 |
---|---|---|
committer | grt@chromium.org <grt@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-11 18:23:30 +0000 |
commit | baf405574a8a4f40dde15ac35ba4ad0cf34846cc (patch) | |
tree | b0190fb67df57ca85a68dfd44f2959315520d856 /chrome | |
parent | 9800de5ee99882a819eb2673fdf4245cd5c3c770 (diff) | |
download | chromium_src-baf405574a8a4f40dde15ac35ba4ad0cf34846cc.zip chromium_src-baf405574a8a4f40dde15ac35ba4ad0cf34846cc.tar.gz chromium_src-baf405574a8a4f40dde15ac35ba4ad0cf34846cc.tar.bz2 |
Add -stage:ID modifier to products' Google Update "ap" values to better track installation failures.
BUG=none
TEST=Normal install/uninstall scenarios all function as before. Any that crash leave a "-stage:ID" modifier on the "ap" value.
Review URL: http://codereview.chromium.org/6612068
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@77831 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/installer/setup/install.cc | 11 | ||||
-rw-r--r-- | chrome/installer/setup/install_worker.cc | 79 | ||||
-rw-r--r-- | chrome/installer/setup/setup_main.cc | 21 | ||||
-rw-r--r-- | chrome/installer/setup/setup_util.cc | 13 | ||||
-rw-r--r-- | chrome/installer/setup/setup_util.h | 28 | ||||
-rw-r--r-- | chrome/installer/setup/setup_util_unittest.cc | 5 | ||||
-rw-r--r-- | chrome/installer/util/browser_distribution.h | 1 | ||||
-rw-r--r-- | chrome/installer/util/channel_info.cc | 110 | ||||
-rw-r--r-- | chrome/installer/util/channel_info.h | 9 | ||||
-rw-r--r-- | chrome/installer/util/channel_info_unittest.cc | 108 | ||||
-rw-r--r-- | chrome/installer/util/install_util.cc | 55 | ||||
-rw-r--r-- | chrome/installer/util/install_util.h | 7 | ||||
-rw-r--r-- | chrome/installer/util/install_util_unittest.cc | 51 | ||||
-rw-r--r-- | chrome/installer/util/installer_state.cc | 75 | ||||
-rw-r--r-- | chrome/installer/util/installer_state.h | 8 | ||||
-rw-r--r-- | chrome/installer/util/util_constants.h | 20 |
16 files changed, 477 insertions, 124 deletions
diff --git a/chrome/installer/setup/install.cc b/chrome/installer/setup/install.cc index e9b394b..9a1c45f 100644 --- a/chrome/installer/setup/install.cc +++ b/chrome/installer/setup/install.cc @@ -262,6 +262,8 @@ installer::InstallStatus InstallNewVersion( scoped_ptr<Version>* current_version) { DCHECK(current_version); + installer_state.UpdateStage(installer::BUILDING); + current_version->reset(installer_state.GetCurrentVersion(original_state)); scoped_ptr<WorkItemList> install_list(WorkItem::CreateWorkItemList()); @@ -281,7 +283,10 @@ installer::InstallStatus InstallNewVersion( FilePath new_chrome_exe( installer_state.target_path().Append(installer::kChromeNewExe)); + installer_state.UpdateStage(installer::EXECUTING); + if (!install_list->Do()) { + installer_state.UpdateStage(installer::ROLLINGBACK); installer::InstallStatus result = file_util::PathExists(new_chrome_exe) && current_version->get() && new_version.Equals(*current_version->get()) ? @@ -293,6 +298,8 @@ installer::InstallStatus InstallNewVersion( return result; } + installer_state.UpdateStage(installer::FINISHING); + installer::RefreshElevationPolicy(); if (!current_version->get()) { @@ -358,6 +365,10 @@ InstallStatus InstallOrUpdateProduct( // TODO(robertshield): Everything below this line should instead be captured // by WorkItems. if (!InstallUtil::GetInstallReturnCode(result)) { + // Update the modifiers on the channel values for the product(s) being + // installed and for the binaries in case of multi-install. + installer_state.UpdateChannels(); + if (result == FIRST_INSTALL_SUCCESS && !prefs_path.empty()) CopyPreferenceFileForFirstRun(installer_state, prefs_path); diff --git a/chrome/installer/setup/install_worker.cc b/chrome/installer/setup/install_worker.cc index 24fbf6d..37d9f73 100644 --- a/chrome/installer/setup/install_worker.cc +++ b/chrome/installer/setup/install_worker.cc @@ -264,81 +264,12 @@ void AddGoogleUpdateWorkItems(const InstallationState& original_state, return; } - // Update the "ap" value for the product being installed/updated. We get - // this via GetNonVersionedProductState since the product whose ap value - // we are querying may be in the process of being installed for the first - // time. - BrowserDistribution::Type client_state_distribution = - installer_state.state_type(); - const ProductState* target_product_state = - original_state.GetNonVersionedProductState( - installer_state.system_install(), client_state_distribution); - ChannelInfo channel_info(target_product_state->channel()); - - // This is a multi-install product. - bool modified = channel_info.SetMultiInstall(true); - - // Add the appropriate modifiers for all products and their options. - modified |= installer_state.SetChannelFlags(true, &channel_info); - - VLOG(1) << "ap: " << channel_info.value(); - - const HKEY reg_root = installer_state.root_key(); - const std::wstring& key_path(installer_state.state_key()); - - // Write the results if needed. - if (modified) { - install_list->AddSetRegValueWorkItem(reg_root, key_path, - google_update::kRegApField, - channel_info.value(), true); - } else { - VLOG(1) << "Channel flags not modified"; - } - - // Synchronize the other products and the package with this one. - std::vector<std::wstring> state_key_paths; - - state_key_paths.reserve(installer_state.products().size()); - std::wstring multi_key( - installer_state.multi_package_binaries_distribution()->GetStateKey()); - if (multi_key != key_path) - state_key_paths.push_back(multi_key); - - std::wstring other_key; - Products::const_iterator scan = installer_state.products().begin(); - Products::const_iterator end = installer_state.products().end(); - for (; scan != end; ++scan) { - other_key = (*scan)->distribution()->GetStateKey(); - if (other_key != key_path) - state_key_paths.push_back(other_key); - } - - RegKey key; - ChannelInfo other_info; - std::vector<std::wstring>::const_iterator kscan = state_key_paths.begin(); - std::vector<std::wstring>::const_iterator kend = state_key_paths.end(); - for (; kscan != kend; ++kscan) { - // Handle the case where the ClientState key doesn't exist by creating it. - // This takes care of the multi-installer's package key, which is not - // created by Google Update for us. - if ((key.Open(reg_root, kscan->c_str(), KEY_QUERY_VALUE) != ERROR_SUCCESS) - || (!other_info.Initialize(key))) { - other_info.set_value(std::wstring()); - } - if (!other_info.Equals(channel_info)) { - if (!key.Valid()) { - install_list->AddCreateRegKeyWorkItem(reg_root, *kscan); - } - install_list->AddSetRegValueWorkItem(reg_root, *kscan, - google_update::kRegApField, - channel_info.value(), true); - } - } - // Creating the ClientState key for binaries, if we're migrating to multi then // copy over Chrome's brand code if it has one. Chrome Frame currently never // has a brand code. - if (multi_key != key_path) { + if (installer_state.state_type() != BrowserDistribution::CHROME_BINARIES) { + std::wstring multi_key( + installer_state.multi_package_binaries_distribution()->GetStateKey()); const ProductState* chrome_product_state = original_state.GetNonVersionedProductState( installer_state.system_install(), @@ -346,9 +277,11 @@ void AddGoogleUpdateWorkItems(const InstallationState& original_state, const std::wstring& brand(chrome_product_state->brand()); if (!brand.empty()) { + install_list->AddCreateRegKeyWorkItem(installer_state.root_key(), + multi_key); // Write Chrome's brand code to the multi key. Never overwrite the value // if one is already present (although this shouldn't happen). - install_list->AddSetRegValueWorkItem(reg_root, + install_list->AddSetRegValueWorkItem(installer_state.root_key(), multi_key, google_update::kRegBrandField, brand, diff --git a/chrome/installer/setup/setup_main.cc b/chrome/installer/setup/setup_main.cc index edd5b6b..48ff47d 100644 --- a/chrome/installer/setup/setup_main.cc +++ b/chrome/installer/setup/setup_main.cc @@ -91,6 +91,9 @@ DWORD UnPackArchive(const FilePath& archive, const FilePath& output_directory, installer::ArchiveType* archive_type) { DCHECK(archive_type); + + installer_state.UpdateStage(installer::UNCOMPRESSING); + // First uncompress the payload. This could be a differential // update (patch.7z) or full archive (chrome.7z). If this uncompress fails // return with error. @@ -120,9 +123,10 @@ DWORD UnPackArchive(const FilePath& archive, archive_version->GetString())); existing_archive = existing_archive.Append(installer::kInstallerDir); existing_archive = existing_archive.Append(installer::kChromeArchive); - if (int i = installer::ApplyDiffPatch(FilePath(existing_archive), + if (int i = installer::ApplyDiffPatch(existing_archive, FilePath(unpacked_file), - FilePath(uncompressed_archive))) { + uncompressed_archive, + &installer_state)) { LOG(ERROR) << "Binary patching failed with error " << i; return i; } @@ -130,6 +134,8 @@ DWORD UnPackArchive(const FilePath& archive, *archive_type = installer::FULL_ARCHIVE_TYPE; } + installer_state.UpdateStage(installer::UNPACKING); + // Unpack the uncompressed archive. return LzmaUtil::UnPackArchive(uncompressed_archive.value(), output_directory.value(), &unpacked_file); @@ -671,16 +677,21 @@ installer::InstallStatus InstallProducts( const MasterPreferences& prefs, InstallerState* installer_state) { DCHECK(installer_state); + const bool system_install = installer_state->system_install(); installer::InstallStatus install_status = installer::UNKNOWN_STATUS; installer::ArchiveType archive_type = installer::UNKNOWN_ARCHIVE_TYPE; bool incremental_install = false; + installer_state->UpdateStage(installer::PRECONDITIONS); + // The stage provides more fine-grained information than -multifail, so remove + // the -multifail suffix from the Google Update "ap" value. + BrowserDistribution::GetSpecificDistribution(installer_state->state_type()) + ->UpdateInstallStatus(system_install, archive_type, install_status); if (CheckPreInstallConditions(original_state, installer_state, &install_status)) { install_status = InstallProductsHelper( original_state, cmd_line, prefs, *installer_state, &archive_type); } - const bool system_install = installer_state->system_install(); const Products& products = installer_state->products(); for (size_t i = 0; i < products.size(); ++i) { @@ -693,6 +704,7 @@ installer::InstallStatus InstallProducts( system_install, archive_type, install_status); } + installer_state->UpdateStage(installer::NO_STAGE); return install_status; } @@ -779,7 +791,8 @@ bool HandleNonInstallCmdLineOptions(const InstallationState& original_state, installer::switches::kNewSetupExe); if (!installer::ApplyDiffPatch(old_setup_exe, FilePath(uncompressed_patch), - new_setup_exe)) + new_setup_exe, + installer_state)) status = installer::NEW_VERSION_UPDATED; } if (!temp_path.Delete()) { diff --git a/chrome/installer/setup/setup_util.cc b/chrome/installer/setup/setup_util.cc index c0e4ff4..9395c28 100644 --- a/chrome/installer/setup/setup_util.cc +++ b/chrome/installer/setup/setup_util.cc @@ -9,16 +9,21 @@ #include "base/file_util.h" #include "base/logging.h" #include "base/string_util.h" +#include "chrome/installer/util/installer_state.h" #include "chrome/installer/util/util_constants.h" #include "courgette/courgette.h" #include "third_party/bspatch/mbspatch.h" int installer::ApplyDiffPatch(const FilePath& src, - const FilePath& patch, - const FilePath& dest) { + const FilePath& patch, + const 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 = @@ -29,6 +34,10 @@ int installer::ApplyDiffPatch(const FilePath& src, return 0; VLOG(1) << "Failed to apply patch " << patch.value() << " using courgette."; + + if (installer_state != NULL) + installer_state->UpdateStage(installer::BINARY_PATCHING); + return ApplyBinaryPatch(src.value().c_str(), patch.value().c_str(), dest.value().c_str()); } diff --git a/chrome/installer/setup/setup_util.h b/chrome/installer/setup/setup_util.h index 22ddc14..267235f 100644 --- a/chrome/installer/setup/setup_util.h +++ b/chrome/installer/setup/setup_util.h @@ -13,17 +13,23 @@ class FilePath; namespace installer { - // 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. - int ApplyDiffPatch(const FilePath& src, - const FilePath& patch, - const FilePath& dest); - - // Find the version of Chrome from an install source directory. - // Chrome_path should contain at least one version folder. - // Returns the maximum version found or NULL if no version is found. - Version* GetMaxVersionFromArchiveDir(const FilePath& chrome_path); + +class InstallerState; + +// 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. +// The installer stage is updated if |installer_state| is non-NULL. +int ApplyDiffPatch(const FilePath& src, + const FilePath& patch, + const FilePath& dest, + const InstallerState* installer_state); + +// Find the version of Chrome from an install source directory. +// Chrome_path should contain at least one version folder. +// Returns the maximum version found or NULL if no version is found. +Version* GetMaxVersionFromArchiveDir(const FilePath& chrome_path); + } // namespace installer #endif // CHROME_INSTALLER_SETUP_SETUP_UTIL_H_ diff --git a/chrome/installer/setup/setup_util_unittest.cc b/chrome/installer/setup/setup_util_unittest.cc index 37b1909..0730ba8 100644 --- a/chrome/installer/setup/setup_util_unittest.cc +++ b/chrome/installer/setup/setup_util_unittest.cc @@ -49,11 +49,12 @@ TEST_F(SetupUtilTest, ApplyDiffPatchTest) { FilePath src = data_dir_.AppendASCII("archive1.7z"); FilePath patch = data_dir_.AppendASCII("archive.diff"); FilePath dest = work_dir.AppendASCII("archive2.7z"); - EXPECT_EQ(installer::ApplyDiffPatch(src, patch, dest), 0); + EXPECT_EQ(installer::ApplyDiffPatch(src, patch, dest, NULL), 0); FilePath base = data_dir_.AppendASCII("archive2.7z"); EXPECT_TRUE(file_util::ContentsEqual(dest, base)); - EXPECT_EQ(installer::ApplyDiffPatch(FilePath(), FilePath(), FilePath()), 6); + EXPECT_EQ(installer::ApplyDiffPatch(FilePath(), FilePath(), FilePath(), NULL), + 6); } // Test that we are parsing Chrome version correctly. diff --git a/chrome/installer/util/browser_distribution.h b/chrome/installer/util/browser_distribution.h index 71f9acf..6c0df20 100644 --- a/chrome/installer/util/browser_distribution.h +++ b/chrome/installer/util/browser_distribution.h @@ -36,6 +36,7 @@ class BrowserDistribution { CHROME_BROWSER, CHROME_FRAME, CHROME_BINARIES, + NUM_TYPES }; static BrowserDistribution* GetDistribution(); diff --git a/chrome/installer/util/channel_info.cc b/chrome/installer/util/channel_info.cc index 98f54d8..f2bb28d 100644 --- a/chrome/installer/util/channel_info.cc +++ b/chrome/installer/util/channel_info.cc @@ -19,6 +19,7 @@ const wchar_t kModChrome[] = L"-chrome"; const wchar_t kModChromeFrame[] = L"-chromeframe"; const wchar_t kModMultiInstall[] = L"-multi"; const wchar_t kModReadyMode[] = L"-readymode"; +const wchar_t kModStage[] = L"-stage:"; const wchar_t kSfxFull[] = L"-full"; const wchar_t kSfxMultiFail[] = L"-multifail"; @@ -28,6 +29,7 @@ const wchar_t* const kChannels[] = { }; const wchar_t* const kModifiers[] = { + kModStage, kModMultiInstall, kModChrome, kModChromeFrame, @@ -38,6 +40,7 @@ const wchar_t* const kModifiers[] = { }; enum ModifierIndex { + MOD_STAGE, MOD_MULTI_INSTALL, MOD_CHROME, MOD_CHROME_FRAME, @@ -52,29 +55,44 @@ COMPILE_ASSERT(NUM_MODIFIERS == arraysize(kModifiers), kModifiers_disagrees_with_ModifierIndex_comma_they_must_match_bang); // Returns true if the modifier is found, in which case |position| holds the -// location at which the modifier was found. +// location at which the modifier was found. The number of characters in the +// modifier is returned in |length|, if non-NULL. bool FindModifier(ModifierIndex index, const std::wstring& ap_value, - std::wstring::size_type* position) { + std::wstring::size_type* position, + std::wstring::size_type* length) { DCHECK(position != NULL); + std::wstring::size_type mod_position = std::wstring::npos; std::wstring::size_type mod_length = std::wstring::traits_type::length(kModifiers[index]); - for (std::wstring::size_type pos = 0; ; ) { - *position = ap_value.find(kModifiers[index], pos, mod_length); - if (*position == std::wstring::npos) + const bool mod_takes_arg = (kModifiers[index][mod_length - 1] == L':'); + std::wstring::size_type pos = 0; + do { + mod_position = ap_value.find(kModifiers[index], pos, mod_length); + if (mod_position == std::wstring::npos) + return false; // Modifier not found. + pos = mod_position + mod_length; + // Modifiers that take an argument gobble up to the next separator or to the + // end. + if (mod_takes_arg) { + pos = ap_value.find(L'-', pos); + if (pos == std::wstring::npos) + pos = ap_value.size(); break; - // The modifier must be either at the end of the string or followed by -. - pos = *position + mod_length; - if (pos == ap_value.size() || ap_value[pos] == L'-') - return true; - } - return false; + } + // Regular modifiers must be followed by '-' or the end of the string. + } while (pos != ap_value.size() && ap_value[pos] != L'-'); + DCHECK_NE(mod_position, std::wstring::npos); + *position = mod_position; + if (length != NULL) + *length = pos - mod_position; + return true; } bool HasModifier(ModifierIndex index, const std::wstring& ap_value) { DCHECK(index >= 0 && index < NUM_MODIFIERS); std::wstring::size_type position; - return FindModifier(index, ap_value, &position); + return FindModifier(index, ap_value, &position, NULL); } std::wstring::size_type FindInsertionPoint(ModifierIndex index, @@ -83,7 +101,7 @@ std::wstring::size_type FindInsertionPoint(ModifierIndex index, std::wstring::size_type result; for (int scan = index + 1; scan < NUM_MODIFIERS; ++scan) { - if (FindModifier(static_cast<ModifierIndex>(scan), ap_value, &result)) + if (FindModifier(static_cast<ModifierIndex>(scan), ap_value, &result, NULL)) return result; } @@ -95,7 +113,8 @@ bool SetModifier(ModifierIndex index, bool set, std::wstring* ap_value) { DCHECK(index >= 0 && index < NUM_MODIFIERS); DCHECK(ap_value); std::wstring::size_type position; - bool have_modifier = FindModifier(index, *ap_value, &position); + std::wstring::size_type length; + bool have_modifier = FindModifier(index, *ap_value, &position, &length); if (set) { if (!have_modifier) { ap_value->insert(FindInsertionPoint(index, *ap_value), kModifiers[index]); @@ -103,8 +122,7 @@ bool SetModifier(ModifierIndex index, bool set, std::wstring* ap_value) { } } else { if (have_modifier) { - ap_value->erase(position, - std::wstring::traits_type::length(kModifiers[index])); + ap_value->erase(position, length); return true; } } @@ -120,8 +138,16 @@ bool ChannelInfo::Initialize(const RegKey& key) { } bool ChannelInfo::Write(RegKey* key) const { - return (key->WriteValue(google_update::kRegApField, value_.c_str()) == - ERROR_SUCCESS); + DCHECK(key); + // Google Update deletes the value when it is empty, so we may as well, too. + LONG result = value_.empty() ? + key->DeleteValue(google_update::kRegApField) : + key->WriteValue(google_update::kRegApField, value_.c_str()); + if (result != ERROR_SUCCESS) { + LOG(ERROR) << "Failed writing channel info; result: " << result; + return false; + } + return true; } bool ChannelInfo::GetChannelName(std::wstring* channel_name) const { @@ -157,12 +183,12 @@ bool ChannelInfo::EqualsBaseOf(const ChannelInfo& other) const { std::wstring::size_type this_base_end; std::wstring::size_type other_base_end; - if (!FindModifier(MOD_MULTI_INSTALL, value_, &this_base_end)) - this_base_end = FindInsertionPoint(MOD_MULTI_INSTALL, value_); - if (!FindModifier(MOD_MULTI_INSTALL, other.value_, &other_base_end)) - other_base_end = FindInsertionPoint(MOD_MULTI_INSTALL, other.value_); + if (!FindModifier(MOD_STAGE, value_, &this_base_end, NULL)) + this_base_end = FindInsertionPoint(MOD_STAGE, value_); + if (!FindModifier(MOD_STAGE, other.value_, &other_base_end, NULL)) + other_base_end = FindInsertionPoint(MOD_STAGE, other.value_); return value_.compare(0, this_base_end, - other.value_.c_str(), other_base_end) == 0; + other.value_, 0, other_base_end) == 0; } bool ChannelInfo::IsCeee() const { @@ -205,6 +231,44 @@ bool ChannelInfo::SetReadyMode(bool value) { return SetModifier(MOD_READY_MODE, value, &value_); } +bool ChannelInfo::SetStage(const wchar_t* stage) { + std::wstring::size_type position; + std::wstring::size_type length; + bool have_modifier = FindModifier(MOD_STAGE, value_, &position, &length); + if (stage != NULL && *stage != L'\0') { + std::wstring stage_str(kModStage); + stage_str.append(stage); + if (!have_modifier) { + value_.insert(FindInsertionPoint(MOD_STAGE, value_), stage_str); + return true; + } + if (value_.compare(position, length, stage_str) != 0) { + value_.replace(position, length, stage_str); + return true; + } + } else { + if (have_modifier) { + value_.erase(position, length); + return true; + } + } + return false; +} + +std::wstring ChannelInfo::GetStage() const { + std::wstring::size_type position; + std::wstring::size_type length; + + if (FindModifier(MOD_STAGE, value_, &position, &length)) { + // Return the portion after the prefix. + std::wstring::size_type pfx_length = + std::wstring::traits_type::length(kModStage); + DCHECK_LE(pfx_length, length); + return value_.substr(position + pfx_length, length - pfx_length); + } + return std::wstring(); +} + bool ChannelInfo::HasFullSuffix() const { return HasModifier(SFX_FULL, value_); } diff --git a/chrome/installer/util/channel_info.h b/chrome/installer/util/channel_info.h index a5b895d..e9c46e1 100644 --- a/chrome/installer/util/channel_info.h +++ b/chrome/installer/util/channel_info.h @@ -80,6 +80,15 @@ class ChannelInfo { // modified. bool SetReadyMode(bool value); + // Adds the -stage: modifier with the given string (if |stage| is non-NULL) or + // removes the -stage: modifier (otherwise), returning true if the value is + // modified. + bool SetStage(const wchar_t* stage); + + // Returns the string identifying the current stage, or an empty string if the + // -stage: modifier is not present in the value. + std::wstring GetStage() const; + // Returns true if the -full suffix is present in the value. bool HasFullSuffix() const; diff --git a/chrome/installer/util/channel_info_unittest.cc b/chrome/installer/util/channel_info_unittest.cc index 5c03040..2dec71a 100644 --- a/chrome/installer/util/channel_info_unittest.cc +++ b/chrome/installer/util/channel_info_unittest.cc @@ -8,6 +8,8 @@ #include "chrome/installer/util/channel_info.h" #include "testing/gtest/include/gtest/gtest.h" +using installer::ChannelInfo; + namespace { const std::wstring kChannelStable; @@ -17,7 +19,7 @@ const std::wstring kChannelDev(L"dev"); } // namespace TEST(ChannelInfoTest, Channels) { - installer::ChannelInfo ci; + ChannelInfo ci; std::wstring channel; ci.set_value(L""); @@ -58,7 +60,7 @@ TEST(ChannelInfoTest, Channels) { } TEST(ChannelInfoTest, CEEE) { - installer::ChannelInfo ci; + ChannelInfo ci; ci.set_value(L""); EXPECT_TRUE(ci.SetCeee(true)); @@ -90,7 +92,7 @@ TEST(ChannelInfoTest, CEEE) { } TEST(ChannelInfoTest, FullInstall) { - installer::ChannelInfo ci; + ChannelInfo ci; ci.set_value(L""); EXPECT_TRUE(ci.SetFullSuffix(true)); @@ -122,7 +124,7 @@ TEST(ChannelInfoTest, FullInstall) { } TEST(ChannelInfoTest, MultiInstall) { - installer::ChannelInfo ci; + ChannelInfo ci; ci.set_value(L""); EXPECT_TRUE(ci.SetMultiInstall(true)); @@ -154,7 +156,7 @@ TEST(ChannelInfoTest, MultiInstall) { } TEST(ChannelInfoTest, Combinations) { - installer::ChannelInfo ci; + ChannelInfo ci; ci.set_value(L"2.0-beta-chromeframe"); EXPECT_FALSE(ci.IsChrome()); @@ -163,14 +165,22 @@ TEST(ChannelInfoTest, Combinations) { } TEST(ChannelInfoTest, EqualsBaseOf) { - installer::ChannelInfo ci1; - installer::ChannelInfo ci2; + ChannelInfo ci1; + ChannelInfo ci2; std::pair<std::wstring, std::wstring> trues[] = { std::make_pair(std::wstring(L""), std::wstring(L"")), std::make_pair(std::wstring(L"2.0-beta"), std::wstring(L"2.0-beta")), std::make_pair(std::wstring(L"-full"), std::wstring(L"-full")), - std::make_pair(std::wstring(L""), std::wstring(L"-multi")) + std::make_pair(std::wstring(L""), std::wstring(L"-multi")), + std::make_pair(std::wstring(L"2.0-beta"), + std::wstring(L"2.0-beta-stage:finishing")), + std::make_pair(std::wstring(L"2.0-beta-stage:finishing"), + std::wstring(L"2.0-beta")), + std::make_pair(std::wstring(L"2.0-beta-stage:executing"), + std::wstring(L"2.0-beta-stage:finishing")), + std::make_pair(std::wstring(L"2.0-beta-stage:spamming"), + std::wstring(L"2.0-beta-stage:")), }; for (int i = 0; i < arraysize(trues); ++i) { std::pair<std::wstring, std::wstring>& the_pair = trues[i]; @@ -184,7 +194,9 @@ TEST(ChannelInfoTest, EqualsBaseOf) { std::make_pair(std::wstring(L""), std::wstring(L"2.0-beta")), std::make_pair(std::wstring(L"2.0-gamma"), std::wstring(L"2.0-beta")), std::make_pair(std::wstring(L"spam-full"), std::wstring(L"-full")), - std::make_pair(std::wstring(L"multi"), std::wstring(L"-multi")) + std::make_pair(std::wstring(L"multi"), std::wstring(L"-multi")), + std::make_pair(std::wstring(L"2.0-beta"), + std::wstring(L"2.0-stage:finishing")), }; for (int i = 0; i < arraysize(falses); ++i) { std::pair<std::wstring, std::wstring>& the_pair = falses[i]; @@ -194,3 +206,81 @@ TEST(ChannelInfoTest, EqualsBaseOf) { << the_pair.second; } } + +TEST(ChannelInfoTest, GetStage) { + ChannelInfo ci; + + ci.set_value(L""); + EXPECT_EQ(L"", ci.GetStage()); + ci.set_value(L"-stage"); + EXPECT_EQ(L"", ci.GetStage()); + ci.set_value(L"-stage:"); + EXPECT_EQ(L"", ci.GetStage()); + ci.set_value(L"-stage:spammy"); + EXPECT_EQ(L"spammy", ci.GetStage()); + + ci.set_value(L"-multi"); + EXPECT_EQ(L"", ci.GetStage()); + ci.set_value(L"-stage-multi"); + EXPECT_EQ(L"", ci.GetStage()); + ci.set_value(L"-stage:-multi"); + EXPECT_EQ(L"", ci.GetStage()); + ci.set_value(L"-stage:spammy-multi"); + EXPECT_EQ(L"spammy", ci.GetStage()); + + ci.set_value(L"2.0-beta-multi"); + EXPECT_EQ(L"", ci.GetStage()); + ci.set_value(L"2.0-beta-stage-multi"); + EXPECT_EQ(L"", ci.GetStage()); + ci.set_value(L"2.0-beta-stage:-multi"); + EXPECT_EQ(L"", ci.GetStage()); + ci.set_value(L"2.0-beta-stage:spammy-multi"); + EXPECT_EQ(L"spammy", ci.GetStage()); +} + +TEST(ChannelInfoTest, SetStage) { + ChannelInfo ci; + + ci.set_value(L""); + EXPECT_FALSE(ci.SetStage(NULL)); + EXPECT_EQ(L"", ci.value()); + EXPECT_TRUE(ci.SetStage(L"spammy")); + EXPECT_EQ(L"-stage:spammy", ci.value()); + EXPECT_FALSE(ci.SetStage(L"spammy")); + EXPECT_EQ(L"-stage:spammy", ci.value()); + EXPECT_TRUE(ci.SetStage(NULL)); + EXPECT_EQ(L"", ci.value()); + EXPECT_TRUE(ci.SetStage(L"spammy")); + EXPECT_TRUE(ci.SetStage(L"")); + EXPECT_EQ(L"", ci.value()); + + ci.set_value(L"-multi"); + EXPECT_FALSE(ci.SetStage(NULL)); + EXPECT_EQ(L"-multi", ci.value()); + EXPECT_TRUE(ci.SetStage(L"spammy")); + EXPECT_EQ(L"-stage:spammy-multi", ci.value()); + EXPECT_FALSE(ci.SetStage(L"spammy")); + EXPECT_EQ(L"-stage:spammy-multi", ci.value()); + EXPECT_TRUE(ci.SetStage(NULL)); + EXPECT_EQ(L"-multi", ci.value()); + EXPECT_TRUE(ci.SetStage(L"spammy")); + EXPECT_TRUE(ci.SetStage(L"")); + EXPECT_EQ(L"-multi", ci.value()); + + ci.set_value(L"2.0-beta-multi"); + EXPECT_FALSE(ci.SetStage(NULL)); + EXPECT_EQ(L"2.0-beta-multi", ci.value()); + EXPECT_TRUE(ci.SetStage(L"spammy")); + EXPECT_EQ(L"2.0-beta-stage:spammy-multi", ci.value()); + EXPECT_FALSE(ci.SetStage(L"spammy")); + EXPECT_EQ(L"2.0-beta-stage:spammy-multi", ci.value()); + EXPECT_TRUE(ci.SetStage(NULL)); + EXPECT_EQ(L"2.0-beta-multi", ci.value()); + EXPECT_TRUE(ci.SetStage(L"spammy")); + EXPECT_TRUE(ci.SetStage(L"")); + EXPECT_EQ(L"2.0-beta-multi", ci.value()); + + ci.set_value(L"2.0-beta-stage:-multi"); + EXPECT_TRUE(ci.SetStage(NULL)); + EXPECT_EQ(L"2.0-beta-multi", ci.value()); +} diff --git a/chrome/installer/util/install_util.cc b/chrome/installer/util/install_util.cc index 4aec77b..ea5d9ce 100644 --- a/chrome/installer/util/install_util.cc +++ b/chrome/installer/util/install_util.cc @@ -33,6 +33,36 @@ using base::win::RegKey; using installer::ProductState; +namespace { + +const wchar_t kStageBinaryPatching[] = L"binary_patching"; +const wchar_t kStageBuilding[] = L"building"; +const wchar_t kStageEnsemblePatching[] = L"ensemble_patching"; +const wchar_t kStageExecuting[] = L"executing"; +const wchar_t kStageFinishing[] = L"finishing"; +const wchar_t kStagePreconditions[] = L"preconditions"; +const wchar_t kStageRollingback[] = L"rollingback"; +const wchar_t kStageUncompressing[] = L"uncompressing"; +const wchar_t kStageUnpacking[] = L"unpacking"; + +const wchar_t* const kStages[] = { + NULL, + kStagePreconditions, + kStageUncompressing, + kStageEnsemblePatching, + kStageBinaryPatching, + kStageUnpacking, + kStageBuilding, + kStageExecuting, + kStageRollingback, + kStageFinishing +}; + +COMPILE_ASSERT(installer::NUM_STAGES == arraysize(kStages), + kStages_disagrees_with_Stage_comma_they_must_match_bang); + +} // namespace + bool InstallUtil::ExecuteExeAsAdmin(const CommandLine& cmd, DWORD* exit_code) { FilePath::StringType program(cmd.GetProgram().value()); DCHECK(!program.empty()); @@ -144,6 +174,31 @@ void InstallUtil::WriteInstallerResult(bool system_install, LOG(ERROR) << "Failed to record installer error information in registry."; } +void InstallUtil::UpdateInstallerStage(bool system_install, + const std::wstring& state_key_path, + installer::InstallerStage stage) { + DCHECK_LE(static_cast<installer::InstallerStage>(0), stage); + DCHECK_GT(installer::NUM_STAGES, stage); + const HKEY root = system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + RegKey state_key; + LONG result = state_key.Open(root, state_key_path.c_str(), + KEY_QUERY_VALUE | KEY_SET_VALUE); + if (result == ERROR_SUCCESS) { + // TODO(grt): switch to using Google Update's new InstallerExtraCode1 value + // once it exists. In the meantime, encode the stage into the channel name. + installer::ChannelInfo channel_info; + // This will return false if the "ap" value isn't present, which is fine. + channel_info.Initialize(state_key); + if (channel_info.SetStage(kStages[stage]) && + !channel_info.Write(&state_key)) { + LOG(ERROR) << "Failed writing installer stage to " << state_key_path; + } + } else { + LOG(ERROR) << "Failed opening " << state_key_path + << " to update installer stage; result: " << result; + } +} + bool InstallUtil::IsPerUserInstall(const wchar_t* const exe_path) { wchar_t program_files_path[MAX_PATH] = {0}; if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PROGRAM_FILES, NULL, diff --git a/chrome/installer/util/install_util.h b/chrome/installer/util/install_util.h index 98cbc6d..e03a4f9 100644 --- a/chrome/installer/util/install_util.h +++ b/chrome/installer/util/install_util.h @@ -64,6 +64,13 @@ class InstallUtil { int string_resource_id, const std::wstring* const launch_cmd); + // Update the installer stage reported by Google Update. |state_key_path| + // should be obtained via the state_key method of an InstallerState instance + // created before the machine state is modified by the installer. + static void UpdateInstallerStage(bool system_install, + const std::wstring& state_key_path, + installer::InstallerStage stage); + // Returns true if this installation path is per user, otherwise returns // false (per machine install, meaning: the exe_path contains path to // Program Files). diff --git a/chrome/installer/util/install_util_unittest.cc b/chrome/installer/util/install_util_unittest.cc index 37e38bc..f67d689 100644 --- a/chrome/installer/util/install_util_unittest.cc +++ b/chrome/installer/util/install_util_unittest.cc @@ -8,6 +8,7 @@ #include "base/command_line.h" #include "base/win/registry.h" #include "chrome/installer/util/browser_distribution.h" +#include "chrome/installer/util/google_update_constants.h" #include "chrome/installer/util/installation_state.h" #include "chrome/installer/util/installer_state.h" #include "chrome/installer/util/install_util.h" @@ -121,3 +122,53 @@ TEST_F(InstallUtilTest, GetCurrentDate) { EXPECT_TRUE(SystemTimeToFileTime(&systime, &ft)); } } + +TEST_F(InstallUtilTest, UpdateInstallerStage) { + const bool system_level = false; + const HKEY root = system_level ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + std::wstring state_key_path(L"PhonyClientState"); + + // Update the stage when there's no "ap" value. + { + TempRegKeyOverride override(root, L"root_inst_res"); + RegKey(root, state_key_path.c_str(), KEY_SET_VALUE); + InstallUtil::UpdateInstallerStage(system_level, state_key_path, + installer::BUILDING); + std::wstring value; + EXPECT_EQ(ERROR_SUCCESS, + RegKey(root, state_key_path.c_str(), KEY_QUERY_VALUE) + .ReadValue(google_update::kRegApField, &value)); + EXPECT_EQ(L"-stage:building", value); + } + TempRegKeyOverride::DeleteAllTempKeys(); + + // Update the stage when there is an "ap" value. + { + TempRegKeyOverride override(root, L"root_inst_res"); + RegKey(root, state_key_path.c_str(), KEY_SET_VALUE) + .WriteValue(google_update::kRegApField, L"2.0-dev"); + InstallUtil::UpdateInstallerStage(system_level, state_key_path, + installer::BUILDING); + std::wstring value; + EXPECT_EQ(ERROR_SUCCESS, + RegKey(root, state_key_path.c_str(), KEY_QUERY_VALUE) + .ReadValue(google_update::kRegApField, &value)); + EXPECT_EQ(L"2.0-dev-stage:building", value); + } + TempRegKeyOverride::DeleteAllTempKeys(); + + // Clear the stage. + { + TempRegKeyOverride override(root, L"root_inst_res"); + RegKey(root, state_key_path.c_str(), KEY_SET_VALUE) + .WriteValue(google_update::kRegApField, L"2.0-dev-stage:building"); + InstallUtil::UpdateInstallerStage(system_level, state_key_path, + installer::NO_STAGE); + std::wstring value; + EXPECT_EQ(ERROR_SUCCESS, + RegKey(root, state_key_path.c_str(), KEY_QUERY_VALUE) + .ReadValue(google_update::kRegApField, &value)); + EXPECT_EQ(L"2.0-dev", value); + } + TempRegKeyOverride::DeleteAllTempKeys(); +} diff --git a/chrome/installer/util/installer_state.cc b/chrome/installer/util/installer_state.cc index df2da8d..4f1a772 100644 --- a/chrome/installer/util/installer_state.cc +++ b/chrome/installer/util/installer_state.cc @@ -14,8 +14,10 @@ #include "base/scoped_ptr.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" +#include "base/win/registry.h" #include "chrome/installer/util/delete_tree_work_item.h" #include "chrome/installer/util/helper.h" +#include "chrome/installer/util/install_util.h" #include "chrome/installer/util/installation_state.h" #include "chrome/installer/util/master_preferences.h" #include "chrome/installer/util/master_preferences_constants.h" @@ -447,4 +449,77 @@ bool InstallerState::SetChannelFlags(bool set, return modified; } +void InstallerState::UpdateStage(installer::InstallerStage stage) const { + InstallUtil::UpdateInstallerStage(system_install(), state_key_, stage); +} + +void InstallerState::UpdateChannels() const { + if (operation_ != MULTI_INSTALL && operation_ != MULTI_UPDATE) { + VLOG(1) << "InstallerState::UpdateChannels noop: " << operation_; + return; + } + + // Update the "ap" value for the product being installed/updated. We get the + // current value from the registry since the InstallationState instance used + // by the bulk of the installer does not track changes made by UpdateStage. + // Create the app's ClientState key if it doesn't exist. + ChannelInfo channel_info; + base::win::RegKey state_key; + LONG result = state_key.Create(root_key_, state_key_.c_str(), + KEY_QUERY_VALUE | KEY_SET_VALUE); + if (result == ERROR_SUCCESS) { + channel_info.Initialize(state_key); + + // This is a multi-install product. + bool modified = channel_info.SetMultiInstall(true); + + // Add the appropriate modifiers for all products and their options. + modified |= SetChannelFlags(true, &channel_info); + + VLOG(1) << "ap: " << channel_info.value(); + + // Write the results if needed. + if (modified) + channel_info.Write(&state_key); + + // Remove the -stage: modifier since we don't want to propagate that to the + // other app_guids. + channel_info.SetStage(NULL); + + // Synchronize the other products and the package with this one. + ChannelInfo other_info; + for (int i = 0; i < BrowserDistribution::NUM_TYPES; ++i) { + BrowserDistribution::Type type = + static_cast<BrowserDistribution::Type>(i); + // Skip the app_guid we started with. + if (type == state_type_) + continue; + BrowserDistribution* dist = NULL; + // Always operate on the binaries. + if (i == BrowserDistribution::CHROME_BINARIES) { + dist = multi_package_distribution_; + } else { + const Product* product = FindProduct(type); + // Skip this one if it's for a product we're not operating on. + if (product == NULL) + continue; + dist = product->distribution(); + } + result = state_key.Create(root_key_, dist->GetStateKey().c_str(), + KEY_QUERY_VALUE | KEY_SET_VALUE); + if (result == ERROR_SUCCESS) { + other_info.Initialize(state_key); + if (!other_info.Equals(channel_info)) + channel_info.Write(&state_key); + } else { + LOG(ERROR) << "Failed opening key " << dist->GetStateKey() + << " to update app channels; result: " << result; + } + } + } else { + LOG(ERROR) << "Failed opening key " << state_key_ + << " to update app channels; result: " << result; + } +} + } // namespace installer diff --git a/chrome/installer/util/installer_state.h b/chrome/installer/util/installer_state.h index 8a2dbea..4d74b42 100644 --- a/chrome/installer/util/installer_state.h +++ b/chrome/installer/util/installer_state.h @@ -16,6 +16,7 @@ #include "base/scoped_vector.h" #include "chrome/installer/util/browser_distribution.h" #include "chrome/installer/util/product.h" +#include "chrome/installer/util/util_constants.h" #if defined(OS_WIN) #include <windows.h> // NOLINT @@ -158,6 +159,13 @@ class InstallerState { bool SetChannelFlags(bool set, ChannelInfo* channel_info) const; + // See InstallUtil::UpdateInstallerStage. + void UpdateStage(installer::InstallerStage stage) const; + + // For a MULTI_INSTALL or MULTI_UPDATE operation, updates the Google Update + // "ap" values for all products being operated on. + void UpdateChannels() const; + protected: FilePath GetDefaultProductInstallPath(BrowserDistribution* dist) const; bool CanAddProduct(const Product& product, const FilePath* product_dir) const; diff --git a/chrome/installer/util/util_constants.h b/chrome/installer/util/util_constants.h index 5c83f30..b7e5347 100644 --- a/chrome/installer/util/util_constants.h +++ b/chrome/installer/util/util_constants.h @@ -9,6 +9,8 @@ #define CHROME_INSTALLER_UTIL_UTIL_CONSTANTS_H_ #pragma once +#include "base/basictypes.h" + namespace installer { // Return status of installer @@ -80,6 +82,24 @@ enum ArchiveType { INCREMENTAL_ARCHIVE_TYPE // Incremental or differential archive. }; +// Stages of an installation reported through Google Update on failure. +enum InstallerStage { + NO_STAGE, // 0: No stage to report. + PRECONDITIONS, // 1: Evaluating pre-install conditions. + UNCOMPRESSING, // 2: Uncompressing chrome.packed.7z. + ENSEMBLE_PATCHING, // 3: Patching chrome.7z using courgette. + BINARY_PATCHING, // 4: Patching chrome.7z using bspatch. + UNPACKING, // 5: Unpacking chrome.7z. + BUILDING, // 6: Building the install work item list. + EXECUTING, // 7: Executing the install work item list. + ROLLINGBACK, // 8: Rolling-back the install work item list. + FINISHING, // 9: Finishing after the install work item list. + NUM_STAGES // 10: The number of stages. +}; + +COMPILE_ASSERT(FINISHING == 9, + never_ever_ever_change_InstallerStage_values_bang); + namespace switches { extern const char kCeee[]; extern const char kChrome[]; |