Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 // | 4 // |
| 5 // This file declares util functions for setup project. | 5 // This file declares util functions for setup project. |
| 6 | 6 |
| 7 #include "chrome/installer/setup/setup_util.h" | 7 #include "chrome/installer/setup/setup_util.h" |
| 8 | 8 |
| 9 #include <windows.h> | 9 #include <windows.h> |
| 10 #include <stddef.h> | 10 #include <stddef.h> |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 35 #include "base/win/windows_version.h" | 35 #include "base/win/windows_version.h" |
| 36 #include "chrome/installer/setup/installer_state.h" | 36 #include "chrome/installer/setup/installer_state.h" |
| 37 #include "chrome/installer/setup/setup_constants.h" | 37 #include "chrome/installer/setup/setup_constants.h" |
| 38 #include "chrome/installer/setup/user_hive_visitor.h" | 38 #include "chrome/installer/setup/user_hive_visitor.h" |
| 39 #include "chrome/installer/util/app_registration_data.h" | 39 #include "chrome/installer/util/app_registration_data.h" |
| 40 #include "chrome/installer/util/google_update_constants.h" | 40 #include "chrome/installer/util/google_update_constants.h" |
| 41 #include "chrome/installer/util/install_util.h" | 41 #include "chrome/installer/util/install_util.h" |
| 42 #include "chrome/installer/util/installation_state.h" | 42 #include "chrome/installer/util/installation_state.h" |
| 43 #include "chrome/installer/util/master_preferences.h" | 43 #include "chrome/installer/util/master_preferences.h" |
| 44 #include "chrome/installer/util/master_preferences_constants.h" | 44 #include "chrome/installer/util/master_preferences_constants.h" |
| 45 #include "chrome/installer/util/non_updating_app_registration_data.h" | |
| 46 #include "chrome/installer/util/updating_app_registration_data.h" | |
| 45 #include "chrome/installer/util/util_constants.h" | 47 #include "chrome/installer/util/util_constants.h" |
| 46 #include "courgette/courgette.h" | 48 #include "courgette/courgette.h" |
| 47 #include "courgette/third_party/bsdiff/bsdiff.h" | 49 #include "courgette/third_party/bsdiff/bsdiff.h" |
| 48 | 50 |
| 49 namespace installer { | 51 namespace installer { |
| 50 | 52 |
| 51 namespace { | 53 namespace { |
| 52 | 54 |
| 53 // Event log providers registry location. | 55 // Event log providers registry location. |
| 54 constexpr wchar_t kEventLogProvidersRegPath[] = | 56 constexpr wchar_t kEventLogProvidersRegPath[] = |
| 55 L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\"; | 57 L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\"; |
| 56 | 58 |
| 57 // TODO(grt): use install_static::InstallDetails::Get().install_full_name() when | 59 // TODO(grt): use install_static::InstallDetails::Get().install_full_name() when |
| 58 // InstallDetails is initialized in the installer. | 60 // InstallDetails is initialized in the installer. |
| 59 base::string16 InstallFullName() { | 61 base::string16 InstallFullName() { |
| 60 #if defined(GOOGLE_CHROME_BUILD) | 62 #if defined(GOOGLE_CHROME_BUILD) |
| 61 base::string16 reg_path(L"Chrome"); | 63 base::string16 reg_path(L"Chrome"); |
| 62 if (InstallUtil::IsChromeSxSProcess()) | 64 if (InstallUtil::IsChromeSxSProcess()) |
| 63 reg_path.append(L" SxS"); | 65 reg_path.append(L" SxS"); |
| 64 return reg_path; | 66 return reg_path; |
| 65 #else | 67 #else |
| 66 return base::string16(L"Chromium"); | 68 return base::string16(L"Chromium"); |
| 67 #endif | 69 #endif |
| 68 } | 70 } |
| 69 | 71 |
| 70 // Returns true if product |type| cam be meaningfully installed without the | |
| 71 // --multi-install flag. | |
| 72 bool SupportsSingleInstall(BrowserDistribution::Type type) { | |
| 73 return (type == BrowserDistribution::CHROME_BROWSER || | |
| 74 type == BrowserDistribution::CHROME_FRAME); | |
| 75 } | |
| 76 | |
| 77 // Returns true if the "lastrun" value in |root|\|key_path| (a path to Chrome's | 72 // Returns true if the "lastrun" value in |root|\|key_path| (a path to Chrome's |
| 78 // ClientState key for a user) indicates that Chrome has been used within the | 73 // ClientState key for a user) indicates that Chrome has been used within the |
| 79 // last 28 days. | 74 // last 28 days. |
| 80 bool IsActivelyUsedIn(HKEY root, const wchar_t* key_path) { | 75 bool IsActivelyUsedIn(HKEY root, const wchar_t* key_path) { |
| 81 // This duplicates some logic in GoogleUpdateSettings::GetLastRunTime, which | 76 // This duplicates some logic in GoogleUpdateSettings::GetLastRunTime, which |
| 82 // is suitable for use from the context of Chrome but not from the installer | 77 // is suitable for use from the context of Chrome but not from the installer |
| 83 // because it was implemented with the assumption that | 78 // because it was implemented with the assumption that |
| 84 // BrowserDistribution::GetDistribution() will always be the right thing. | 79 // BrowserDistribution::GetDistribution() will always be the right thing. |
| 85 // This is true in Chrome, but not in the installer in a multi-install world. | 80 // This is true in Chrome, but not in the installer in a multi-install world. |
| 86 // Once multi-install goes away, this assumption will once again become true | 81 // Once multi-install goes away, this assumption will once again become true |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 125 const wchar_t* user_sid, | 120 const wchar_t* user_sid, |
| 126 base::win::RegKey* user_hive) { | 121 base::win::RegKey* user_hive) { |
| 127 // Continue the iteration if this hive isn't owned by an active Chrome user. | 122 // Continue the iteration if this hive isn't owned by an active Chrome user. |
| 128 if (!IsActivelyUsedIn(user_hive->Handle(), client_state_path.c_str())) | 123 if (!IsActivelyUsedIn(user_hive->Handle(), client_state_path.c_str())) |
| 129 return true; | 124 return true; |
| 130 // Stop the iteration. | 125 // Stop the iteration. |
| 131 *is_used = true; | 126 *is_used = true; |
| 132 return false; | 127 return false; |
| 133 } | 128 } |
| 134 | 129 |
| 130 // "The binaries" once referred to the on-disk footprint of Chrome and/or Chrome | |
| 131 // Frame when the products were configured to share such on-disk bits. Support | |
| 132 // for this mode of install was dropped from ToT in December 2016. Remove any | |
| 133 // stray bits in the registry leftover from such installs. | |
| 134 void RemoveBinariesVersionKey(const InstallerState& installer_state) { | |
| 135 #if defined(GOOGLE_CHROME_BUILD) | |
| 136 UpdatingAppRegistrationData reg_data( | |
| 137 L"{4DC8B4CA-1BDA-483e-B5FA-D3C12E15B62D}"); | |
| 138 #else | |
| 139 NonUpdatingAppRegistrationData reg_data(L"Software\\Chromium Binaries"); | |
| 140 #endif | |
| 141 | |
| 142 base::string16 path(reg_data.GetVersionKey()); | |
| 143 if (base::win::RegKey(installer_state.root_key(), path.c_str(), | |
| 144 KEY_QUERY_VALUE | KEY_WOW64_32KEY) | |
| 145 .Valid() && | |
| 146 InstallUtil::DeleteRegistryKey(installer_state.root_key(), path, | |
| 147 KEY_WOW64_32KEY)) { | |
| 148 UMA_HISTOGRAM_BOOLEAN("Setup.Install.BinariesRemoved", true); | |
| 149 } | |
| 150 } | |
| 151 | |
| 152 // Remove leftover traces of multi-install Chrome Frame, if present. Once upon a | |
| 153 // time, Google Chrome Frame could be co-installed with Chrome such that they | |
| 154 // shared the same binaries on disk. Support for new installs of GCF was dropped | |
| 155 // from ToT in December 2013. Remove any stray bits in the registry leftover | |
| 156 // from an old multi-install GCF. | |
| 157 void RemoveMultiChromeFrame(const InstallerState& installer_state) { | |
| 158 // There never was a "Chromium Frame". | |
| 159 #if defined(GOOGLE_CHROME_BUILD) | |
| 160 // To maximize cleanup, unconditionally delete GCF's Clients and ClientState | |
| 161 // keys unless single-install GCF is present. This condition is satisfied if | |
| 162 // both keys exist, Clients\pv contains a value, and | |
| 163 // ClientState\UninstallString contains a path including "\Chrome | |
| 164 // Frame\". Multi-install GCF would have had "\Chrome\", and anything else is | |
| 165 // garbage. | |
| 166 | |
| 167 UpdatingAppRegistrationData gcf_data( | |
| 168 L"{8BA986DA-5100-405E-AA35-86F34A02ACBF}"); | |
| 169 base::win::RegKey clients_key; | |
| 170 base::win::RegKey client_state_key; | |
| 171 | |
| 172 bool has_clients_key = | |
| 173 clients_key.Open(installer_state.root_key(), | |
| 174 gcf_data.GetVersionKey().c_str(), | |
| 175 KEY_QUERY_VALUE | KEY_WOW64_32KEY) == ERROR_SUCCESS; | |
| 176 bool has_client_state_key = | |
| 177 client_state_key.Open(installer_state.root_key(), | |
| 178 gcf_data.GetStateKey().c_str(), | |
| 179 KEY_QUERY_VALUE | KEY_WOW64_32KEY) == ERROR_SUCCESS; | |
| 180 if (!has_clients_key && !has_client_state_key) | |
| 181 return; // Nothing to check or to clean. | |
| 182 | |
| 183 base::string16 value; | |
| 184 if (has_clients_key && has_client_state_key && | |
| 185 clients_key.ReadValue(google_update::kRegVersionField, &value) == | |
| 186 ERROR_SUCCESS && | |
| 187 !value.empty() && | |
| 188 client_state_key.ReadValue(kUninstallStringField, &value) == | |
| 189 ERROR_SUCCESS && | |
| 190 value.find(L"\\Chrome Frame\\") != base::string16::npos) { | |
| 191 return; // Single-install Chrome Frame found. | |
| 192 } | |
| 193 | |
| 194 // Remnants of multi-install GCF or of a malformed GCF are present. Remove the | |
| 195 // Clients and ClientState keys so that Google Update ceases to check for | |
| 196 // updates, and the Programs and Features control panel entry to reduce user | |
| 197 // confusion. | |
| 198 client_state_key.Close(); | |
| 199 clients_key.Close(); | |
| 200 | |
| 201 InstallUtil::DeleteRegistryKey(installer_state.root_key(), | |
| 202 gcf_data.GetVersionKey(), KEY_WOW64_32KEY); | |
| 203 InstallUtil::DeleteRegistryKey(installer_state.root_key(), | |
| 204 gcf_data.GetStateKey(), KEY_WOW64_32KEY); | |
| 205 InstallUtil::DeleteRegistryKey( | |
| 206 installer_state.root_key(), | |
| 207 L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\" | |
| 208 L"Google Chrome Frame", | |
| 209 KEY_WOW64_32KEY); | |
| 210 | |
| 211 UMA_HISTOGRAM_BOOLEAN("Setup.Install.MultiChromeFrameRemoved", true); | |
| 212 #endif | |
| 213 } | |
| 214 | |
| 215 void RemoveAppLauncherVersionKey(const InstallerState& installer_state) { | |
| 216 // The app launcher was only registered for Google Chrome. | |
| 217 #if defined(GOOGLE_CHROME_BUILD) | |
| 218 UpdatingAppRegistrationData reg_data( | |
| 219 L"{FDA71E6F-AC4C-4a00-8B70-9958A68906BF}"); | |
| 220 | |
| 221 base::string16 path(reg_data.GetVersionKey()); | |
| 222 if (base::win::RegKey(installer_state.root_key(), path.c_str(), | |
| 223 KEY_QUERY_VALUE | KEY_WOW64_32KEY) | |
| 224 .Valid() && | |
| 225 InstallUtil::DeleteRegistryKey(installer_state.root_key(), path, | |
| 226 KEY_WOW64_32KEY)) { | |
| 227 UMA_HISTOGRAM_BOOLEAN("Setup.Install.AppLauncherRemoved", true); | |
| 228 } | |
| 229 #endif // GOOGLE_CHROME_BUILD | |
| 230 } | |
| 231 | |
| 232 void RemoveAppHostExe(const InstallerState& installer_state) { | |
| 233 // The app host was only installed for Google Chrome. | |
| 234 #if defined(GOOGLE_CHROME_BUILD) | |
| 235 base::FilePath app_host( | |
| 236 installer_state.target_path().Append(FILE_PATH_LITERAL("app_host.exe"))); | |
| 237 | |
| 238 if (base::PathExists(app_host) && base::DeleteFile(app_host, false)) | |
| 239 UMA_HISTOGRAM_BOOLEAN("Setup.Install.AppHostRemoved", true); | |
|
jwd
2017/01/03 21:10:33
Is there value in capturing a failure to delete?
grt (UTC plus 2)
2017/01/04 08:28:16
Good point. It would be useful to know if the file
| |
| 240 #endif | |
| 241 } | |
| 242 | |
| 243 void RemoveLegacyChromeAppCommands(const InstallerState& installer_state) { | |
| 244 // These app commands were only registered for Google Chrome. | |
| 245 #if defined(GOOGLE_CHROME_BUILD) | |
| 246 base::string16 path(GetRegistrationDataCommandKey( | |
| 247 installer_state.product().distribution()->GetAppRegistrationData(), | |
| 248 L"install-extension")); | |
| 249 | |
| 250 if (base::win::RegKey(installer_state.root_key(), path.c_str(), | |
| 251 KEY_QUERY_VALUE | KEY_WOW64_32KEY) | |
| 252 .Valid() && | |
| 253 InstallUtil::DeleteRegistryKey(installer_state.root_key(), path, | |
| 254 KEY_WOW64_32KEY)) { | |
| 255 UMA_HISTOGRAM_BOOLEAN("Setup.Install.InstallExtensionCommandRemoved", true); | |
| 256 } | |
| 257 #endif | |
| 258 } | |
| 259 | |
| 135 } // namespace | 260 } // namespace |
| 136 | 261 |
| 137 const char kUnPackStatusMetricsName[] = "Setup.Install.LzmaUnPackStatus"; | 262 const char kUnPackStatusMetricsName[] = "Setup.Install.LzmaUnPackStatus"; |
| 138 const char kUnPackNTSTATUSMetricsName[] = "Setup.Install.LzmaUnPackNTSTATUS"; | 263 const char kUnPackNTSTATUSMetricsName[] = "Setup.Install.LzmaUnPackNTSTATUS"; |
| 139 | 264 |
| 140 int CourgettePatchFiles(const base::FilePath& src, | 265 int CourgettePatchFiles(const base::FilePath& src, |
| 141 const base::FilePath& patch, | 266 const base::FilePath& patch, |
| 142 const base::FilePath& dest) { | 267 const base::FilePath& dest) { |
| 143 VLOG(1) << "Applying Courgette patch " << patch.value() | 268 VLOG(1) << "Applying Courgette patch " << patch.value() |
| 144 << " to file " << src.value() | 269 << " to file " << src.value() |
| (...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 295 PLOG(ERROR) << "VirtualAllocEx"; | 420 PLOG(ERROR) << "VirtualAllocEx"; |
| 296 ::TerminateProcess(pi.hProcess, ~static_cast<UINT>(0)); | 421 ::TerminateProcess(pi.hProcess, ~static_cast<UINT>(0)); |
| 297 } | 422 } |
| 298 ::CloseHandle(pi.hThread); | 423 ::CloseHandle(pi.hThread); |
| 299 ::CloseHandle(pi.hProcess); | 424 ::CloseHandle(pi.hProcess); |
| 300 } | 425 } |
| 301 | 426 |
| 302 return ok != FALSE; | 427 return ok != FALSE; |
| 303 } | 428 } |
| 304 | 429 |
| 305 // There are 4 disjoint cases => return values {false,true}: | |
| 306 // (1) Product is being uninstalled => false. | |
| 307 // (2) Product is being installed => true. | |
| 308 // (3) Current operation ignores product, product is absent => false. | |
| 309 // (4) Current operation ignores product, product is present => true. | |
| 310 bool WillProductBePresentAfterSetup( | |
| 311 const installer::InstallerState& installer_state, | |
| 312 const installer::InstallationState& machine_state, | |
| 313 BrowserDistribution::Type type) { | |
| 314 DCHECK(SupportsSingleInstall(type) || installer_state.is_multi_install()); | |
| 315 | |
| 316 const ProductState* product_state = | |
| 317 machine_state.GetProductState(installer_state.system_install(), type); | |
| 318 | |
| 319 // Determine if the product is present prior to the current operation. | |
| 320 bool is_present = (product_state != NULL); | |
| 321 bool is_uninstall = installer_state.operation() == InstallerState::UNINSTALL; | |
| 322 | |
| 323 // Determine if current operation affects the product. | |
| 324 const Product* product = installer_state.FindProduct(type); | |
| 325 bool is_affected = (product != NULL); | |
| 326 | |
| 327 // Decide among {(1),(2),(3),(4)}. | |
| 328 return is_affected ? !is_uninstall : is_present; | |
| 329 } | |
| 330 | |
| 331 bool AdjustProcessPriority() { | 430 bool AdjustProcessPriority() { |
| 332 if (base::win::GetVersion() >= base::win::VERSION_VISTA) { | 431 if (base::win::GetVersion() >= base::win::VERSION_VISTA) { |
| 333 DWORD priority_class = ::GetPriorityClass(::GetCurrentProcess()); | 432 DWORD priority_class = ::GetPriorityClass(::GetCurrentProcess()); |
| 334 if (priority_class == 0) { | 433 if (priority_class == 0) { |
| 335 PLOG(WARNING) << "Failed to get the process's priority class."; | 434 PLOG(WARNING) << "Failed to get the process's priority class."; |
| 336 } else if (priority_class == BELOW_NORMAL_PRIORITY_CLASS || | 435 } else if (priority_class == BELOW_NORMAL_PRIORITY_CLASS || |
| 337 priority_class == IDLE_PRIORITY_CLASS) { | 436 priority_class == IDLE_PRIORITY_CLASS) { |
| 338 BOOL result = ::SetPriorityClass(::GetCurrentProcess(), | 437 BOOL result = ::SetPriorityClass(::GetCurrentProcess(), |
| 339 PROCESS_MODE_BACKGROUND_BEGIN); | 438 PROCESS_MODE_BACKGROUND_BEGIN); |
| 340 PLOG_IF(WARNING, !result) << "Failed to enter background mode."; | 439 PLOG_IF(WARNING, !result) << "Failed to enter background mode."; |
| (...skipping 384 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 725 base::string16 reg_path(kEventLogProvidersRegPath); | 824 base::string16 reg_path(kEventLogProvidersRegPath); |
| 726 reg_path.append(InstallFullName()); | 825 reg_path.append(InstallFullName()); |
| 727 | 826 |
| 728 // TODO(http://crbug.com/668120): If the Event Viewer is open the provider dll | 827 // TODO(http://crbug.com/668120): If the Event Viewer is open the provider dll |
| 729 // will fail to get deleted. This doesn't fail the uninstallation altogether | 828 // will fail to get deleted. This doesn't fail the uninstallation altogether |
| 730 // but leaves files behind. | 829 // but leaves files behind. |
| 731 InstallUtil::DeleteRegistryKey(HKEY_LOCAL_MACHINE, reg_path, | 830 InstallUtil::DeleteRegistryKey(HKEY_LOCAL_MACHINE, reg_path, |
| 732 WorkItem::kWow64Default); | 831 WorkItem::kWow64Default); |
| 733 } | 832 } |
| 734 | 833 |
| 834 void DoLegacyCleanups(const InstallerState& installer_state, | |
| 835 InstallStatus install_status) { | |
| 836 // Do no harm if the install didn't succeed. | |
| 837 if (InstallUtil::GetInstallReturnCode(install_status)) | |
| 838 return; | |
| 839 | |
| 840 // The cleanups below only apply to normal Chrome, not side-by-side (canary). | |
| 841 if (InstallUtil::IsChromeSxSProcess()) | |
| 842 return; | |
| 843 | |
| 844 RemoveBinariesVersionKey(installer_state); | |
| 845 RemoveMultiChromeFrame(installer_state); | |
| 846 RemoveAppLauncherVersionKey(installer_state); | |
| 847 RemoveAppHostExe(installer_state); | |
| 848 RemoveLegacyChromeAppCommands(installer_state); | |
| 849 } | |
| 850 | |
| 735 ScopedTokenPrivilege::ScopedTokenPrivilege(const wchar_t* privilege_name) | 851 ScopedTokenPrivilege::ScopedTokenPrivilege(const wchar_t* privilege_name) |
| 736 : is_enabled_(false) { | 852 : is_enabled_(false) { |
| 737 HANDLE temp_handle; | 853 HANDLE temp_handle; |
| 738 if (!::OpenProcessToken(::GetCurrentProcess(), | 854 if (!::OpenProcessToken(::GetCurrentProcess(), |
| 739 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, | 855 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, |
| 740 &temp_handle)) { | 856 &temp_handle)) { |
| 741 return; | 857 return; |
| 742 } | 858 } |
| 743 token_.Set(temp_handle); | 859 token_.Set(temp_handle); |
| 744 | 860 |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 767 } | 883 } |
| 768 | 884 |
| 769 ScopedTokenPrivilege::~ScopedTokenPrivilege() { | 885 ScopedTokenPrivilege::~ScopedTokenPrivilege() { |
| 770 if (is_enabled_ && previous_privileges_.PrivilegeCount != 0) { | 886 if (is_enabled_ && previous_privileges_.PrivilegeCount != 0) { |
| 771 ::AdjustTokenPrivileges(token_.Get(), FALSE, &previous_privileges_, | 887 ::AdjustTokenPrivileges(token_.Get(), FALSE, &previous_privileges_, |
| 772 sizeof(TOKEN_PRIVILEGES), NULL, NULL); | 888 sizeof(TOKEN_PRIVILEGES), NULL, NULL); |
| 773 } | 889 } |
| 774 } | 890 } |
| 775 | 891 |
| 776 } // namespace installer | 892 } // namespace installer |
| OLD | NEW |