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
3 // found in the LICENSE file.
4
5 #include "chrome/installer/setup/setup_main.h"
6
7 // Must be before msi.h.
8 #include <windows.h>
9
10 #include <msi.h>
11 #include <psapi.h>
12 #include <shellapi.h>
13 #include <shlobj.h>
14 #include <stddef.h>
15 #include <stdint.h>
16
17 #include <memory>
18 #include <string>
19
20 #include "base/at_exit.h"
21 #include "base/command_line.h"
22 #include "base/file_version_info.h"
23 #include "base/files/file_path.h"
24 #include "base/files/file_util.h"
25 #include "base/files/scoped_temp_dir.h"
26 #include "base/logging.h"
27 #include "base/metrics/histogram_functions.h"
28 #include "base/metrics/histogram_macros.h"
29 #include "base/metrics/persistent_histogram_storage.h"
30 #include "base/numerics/safe_conversions.h"
31 #include "base/optional.h"
32 #include "base/path_service.h"
33 #include "base/process/launch.h"
34 #include "base/process/memory.h"
35 #include "base/process/process.h"
36 #include "base/process/process_metrics.h"
37 #include "base/stl_util.h"
38 #include "base/strings/string16.h"
39 #include "base/strings/string_number_conversions.h"
40 #include "base/strings/string_util.h"
41 #include "base/strings/stringprintf.h"
42 #include "base/strings/utf_string_conversions.h"
43 #include "base/time/time.h"
44 #include "base/values.h"
45 #include "base/version.h"
46 #include "base/win/process_startup_helper.h"
47 #include "base/win/registry.h"
48 #include "base/win/scoped_com_initializer.h"
49 #include "base/win/scoped_handle.h"
50 #include "base/win/win_util.h"
51 #include "build/branding_buildflags.h"
52 #include "build/build_config.h"
53 #include "chrome/common/chrome_constants.h"
54 #include "chrome/common/chrome_paths.h"
55 #include "chrome/common/chrome_switches.h"
56 #include "chrome/install_static/install_details.h"
57 #include "chrome/install_static/install_util.h"
58 #include "chrome/installer/setup/archive_patch_helper.h"
59 #include "chrome/installer/setup/brand_behaviors.h"
60 #include "chrome/installer/setup/buildflags.h"
61 #include "chrome/installer/setup/install.h"
62 #include "chrome/installer/setup/install_params.h"
63 #include "chrome/installer/setup/install_worker.h"
64 #include "chrome/installer/setup/installer_crash_reporting.h"
65 #include "chrome/installer/setup/installer_state.h"
66 #include "chrome/installer/setup/launch_chrome.h"
67 #include "chrome/installer/setup/modify_params.h"
68 #include "chrome/installer/setup/setup_constants.h"
69 #include "chrome/installer/setup/setup_install_details.h"
70 #include "chrome/installer/setup/setup_singleton.h"
71 #include "chrome/installer/setup/setup_util.h"
72 #include "chrome/installer/setup/uninstall.h"
73 #include "chrome/installer/setup/user_experiment.h"
74 #include "chrome/installer/util/conditional_work_item_list.h"
75 #include "chrome/installer/util/delete_after_reboot_helper.h"
76 #include "chrome/installer/util/delete_old_versions.h"
77 #include "chrome/installer/util/delete_tree_work_item.h"
78 #include "chrome/installer/util/google_update_constants.h"
79 #include "chrome/installer/util/google_update_settings.h"
80 #include "chrome/installer/util/helper.h"
81 #include "chrome/installer/util/html_dialog.h"
82 #include "chrome/installer/util/initial_preferences.h"
83 #include "chrome/installer/util/initial_preferences_constants.h"
84 #include "chrome/installer/util/install_util.h"
85 #include "chrome/installer/util/installation_state.h"
86 #include "chrome/installer/util/installer_util_strings.h"
87 #include "chrome/installer/util/l10n_string_util.h"
88 #include "chrome/installer/util/logging_installer.h"
89 #include "chrome/installer/util/lzma_util.h"
90 #include "chrome/installer/util/self_cleaning_temp_dir.h"
91 #include "chrome/installer/util/shell_util.h"
92 #include "chrome/installer/util/util_constants.h"
93 #include "components/crash/core/app/crash_switches.h"
94 #include "components/crash/core/app/run_as_crashpad_handler_win.h"
95 #include "content/public/common/content_switches.h"
96
97 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
98 #include "chrome/installer/util/google_update_util.h"
99 #endif
100
101 using installer::InitialPreferences;
102 using installer::InstallationState;
103 using installer::InstallerState;
104 using installer::ProductState;
105
106 namespace {
107
108 const wchar_t kSystemPrincipalSid[] = L"S-1-5-18";
109 const wchar_t kDisplayVersion[] = L"DisplayVersion";
110 const wchar_t kMsiDisplayVersionOverwriteDelay[] = L"10"; // seconds as string
111 const wchar_t kMsiProductIdPrefix[] = L"EnterpriseProduct";
112
113 // Overwrite an existing DisplayVersion as written by the MSI installer
114 // with the real version number of Chrome.
OverwriteDisplayVersion(const base::string16 & path,const base::string16 & value,REGSAM wowkey)115 LONG OverwriteDisplayVersion(const base::string16& path,
116 const base::string16& value,
117 REGSAM wowkey) {
118 base::win::RegKey key;
119 LONG result = 0;
120 base::string16 existing;
121 if ((result = key.Open(HKEY_LOCAL_MACHINE, path.c_str(),
122 KEY_QUERY_VALUE | KEY_SET_VALUE | wowkey)) !=
123 ERROR_SUCCESS) {
124 VLOG(1) << "Skipping DisplayVersion update because registry key " << path
125 << " does not exist in "
126 << (wowkey == KEY_WOW64_64KEY ? "64" : "32") << "bit hive";
127 return result;
128 }
129 if ((result = key.ReadValue(kDisplayVersion, &existing)) != ERROR_SUCCESS) {
130 LOG(ERROR) << "Failed to set DisplayVersion: " << kDisplayVersion
131 << " not found under " << path;
132 return result;
133 }
134 if ((result = key.WriteValue(kDisplayVersion, value.c_str())) !=
135 ERROR_SUCCESS) {
136 LOG(ERROR) << "Failed to set DisplayVersion: " << kDisplayVersion
137 << " could not be written under " << path;
138 return result;
139 }
140 VLOG(1) << "Set DisplayVersion at " << path << " to " << value << " from "
141 << existing;
142 return ERROR_SUCCESS;
143 }
144
OverwriteDisplayVersions(const base::string16 & product,const base::string16 & value)145 LONG OverwriteDisplayVersions(const base::string16& product,
146 const base::string16& value) {
147 // The version is held in two places. First change it in the MSI Installer
148 // registry entry. It is held under a "squashed guid" key.
149 base::string16 reg_path = base::StringPrintf(
150 L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\"
151 L"%ls\\Products\\%ls\\InstallProperties",
152 kSystemPrincipalSid, InstallUtil::GuidToSquid(product).c_str());
153 LONG result1 = OverwriteDisplayVersion(reg_path, value, KEY_WOW64_64KEY);
154
155 // The display version also exists under the Unininstall registry key with
156 // the original guid. Check both WOW64_64 and WOW64_32.
157 reg_path = base::StringPrintf(
158 L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{%ls}",
159 product.c_str());
160 // Consider the operation a success if either of these succeeds.
161 LONG result2 = OverwriteDisplayVersion(reg_path, value, KEY_WOW64_64KEY);
162 LONG result3 = OverwriteDisplayVersion(reg_path, value, KEY_WOW64_32KEY);
163
164 return result1 != ERROR_SUCCESS
165 ? result1
166 : (result2 != ERROR_SUCCESS ? result3 : ERROR_SUCCESS);
167 }
168
DelayedOverwriteDisplayVersions(const base::FilePath & setup_exe,const std::string & id,const base::Version & version)169 void DelayedOverwriteDisplayVersions(const base::FilePath& setup_exe,
170 const std::string& id,
171 const base::Version& version) {
172 // This process has to be able to exit so we launch ourselves with
173 // instructions on what to do and then return.
174 base::CommandLine command_line(setup_exe);
175 command_line.AppendSwitchASCII(installer::switches::kSetDisplayVersionProduct,
176 id);
177 command_line.AppendSwitchASCII(installer::switches::kSetDisplayVersionValue,
178 version.GetString());
179 command_line.AppendSwitchNative(installer::switches::kDelay,
180 kMsiDisplayVersionOverwriteDelay);
181
182 base::LaunchOptions launch_options;
183 launch_options.force_breakaway_from_job_ = true;
184 base::Process writer = base::LaunchProcess(command_line, launch_options);
185 if (!writer.IsValid()) {
186 PLOG(ERROR) << "Failed to set DisplayVersion: "
187 << "could not launch subprocess to make desired changes."
188 << " <<" << command_line.GetCommandLineString() << ">>";
189 }
190 }
191
192 // Returns nullptr if no compressed archive is available for processing,
193 // otherwise returns a patch helper configured to uncompress and patch.
CreateChromeArchiveHelper(const base::FilePath & setup_exe,const base::CommandLine & command_line,const installer::InstallerState & installer_state,const base::FilePath & working_directory,installer::UnPackConsumer consumer)194 std::unique_ptr<installer::ArchivePatchHelper> CreateChromeArchiveHelper(
195 const base::FilePath& setup_exe,
196 const base::CommandLine& command_line,
197 const installer::InstallerState& installer_state,
198 const base::FilePath& working_directory,
199 installer::UnPackConsumer consumer) {
200 // A compressed archive is ordinarily given on the command line by the mini
201 // installer. If one was not given, look for chrome.packed.7z next to the
202 // running program.
203 base::FilePath compressed_archive(
204 command_line.GetSwitchValuePath(installer::switches::kInstallArchive));
205 bool compressed_archive_specified = !compressed_archive.empty();
206 if (!compressed_archive_specified) {
207 compressed_archive =
208 setup_exe.DirName().Append(installer::kChromeCompressedArchive);
209 }
210
211 // Fail if no compressed archive is found.
212 if (!base::PathExists(compressed_archive)) {
213 if (compressed_archive_specified) {
214 LOG(ERROR) << installer::switches::kInstallArchive << "="
215 << compressed_archive.value() << " not found.";
216 }
217 return nullptr;
218 }
219
220 // chrome.7z is either extracted directly from the compressed archive into the
221 // working dir or is the target of patching in the working dir.
222 base::FilePath target(working_directory.Append(installer::kChromeArchive));
223 DCHECK(!base::PathExists(target));
224
225 // Specify an empty path for the patch source since it isn't yet known that
226 // one is needed. It will be supplied in UncompressAndPatchChromeArchive if it
227 // is.
228 return std::make_unique<installer::ArchivePatchHelper>(
229 working_directory, compressed_archive, base::FilePath(), target,
230 consumer);
231 }
232
233 // Returns the MSI product ID from the ClientState key that is populated for MSI
234 // installs. This property is encoded in a value name whose format is
235 // "EnterpriseProduct<GUID>" where <GUID> is the MSI product id. <GUID> is in
236 // the format XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX. The id will be returned if
237 // found otherwise this method will return an empty string.
238 //
239 // This format is strange and its provenance is shrouded in mystery but it has
240 // the data we need, so use it.
FindMsiProductId(const InstallerState & installer_state)241 base::string16 FindMsiProductId(const InstallerState& installer_state) {
242 HKEY reg_root = installer_state.root_key();
243
244 base::win::RegistryValueIterator value_iter(
245 reg_root, install_static::GetClientStateKeyPath().c_str(),
246 KEY_WOW64_32KEY);
247 for (; value_iter.Valid(); ++value_iter) {
248 base::string16 value_name(value_iter.Name());
249 if (base::StartsWith(value_name, kMsiProductIdPrefix,
250 base::CompareCase::INSENSITIVE_ASCII)) {
251 return value_name.substr(base::size(kMsiProductIdPrefix) - 1);
252 }
253 }
254 return base::string16();
255 }
256
257 // Workhorse for producing an uncompressed archive (chrome.7z) given a
258 // chrome.packed.7z containing either a patch file based on the version of
259 // chrome being updated or the full uncompressed archive. Returns true on
260 // success, in which case |archive_type| is populated based on what was found.
261 // Returns false on failure, in which case |install_status| contains the error
262 // code and the result is written to the registry (via WriteInstallerResult).
UncompressAndPatchChromeArchive(const installer::InstallationState & original_state,const installer::InstallerState & installer_state,installer::ArchivePatchHelper * archive_helper,installer::ArchiveType * archive_type,installer::InstallStatus * install_status,const base::Version & previous_version)263 bool UncompressAndPatchChromeArchive(
264 const installer::InstallationState& original_state,
265 const installer::InstallerState& installer_state,
266 installer::ArchivePatchHelper* archive_helper,
267 installer::ArchiveType* archive_type,
268 installer::InstallStatus* install_status,
269 const base::Version& previous_version) {
270 installer_state.SetStage(installer::UNCOMPRESSING);
271
272 // UMA tells us the following about the time required for uncompression as of
273 // M75:
274 // --- Foreground (<10%) ---
275 // Full archive: 7.5s (50%ile) / 52s (99%ile)
276 // Archive patch: <2s (50%ile) / 10-20s (99%ile)
277 // --- Background (>90%) ---
278 // Full archive: 22s (50%ile) / >3m (99%ile)
279 // Archive patch: ~2s (50%ile) / 1.5m - >3m (99%ile)
280 //
281 // The top unpack failure result with 28 days aggregation (>=0.01%)
282 // Setup.Install.LzmaUnPackResult_CompressedChromeArchive
283 // 13.50% DISK_FULL
284 // 0.67% ERROR_NO_SYSTEM_RESOURCES
285 // 0.12% ERROR_IO_DEVICE
286 // 0.05% INVALID_HANDLE
287 // 0.01% INVALID_LEVEL
288 // 0.01% FILE_NOT_FOUND
289 // 0.01% LOCK_VIOLATION
290 // 0.01% ACCESS_DENIED
291 //
292 // Setup.Install.LzmaUnPackResult_ChromeArchivePatch
293 // 0.09% DISK_FULL
294 // 0.01% FILE_NOT_FOUND
295 //
296 // More information can also be found with metrics:
297 // Setup.Install.LzmaUnPackNTSTATUS_CompressedChromeArchive
298 // Setup.Install.LzmaUnPackNTSTATUS_ChromeArchivePatch
299 if (!archive_helper->Uncompress(nullptr)) {
300 *install_status = installer::UNCOMPRESSION_FAILED;
301 installer_state.WriteInstallerResult(
302 *install_status, IDS_INSTALL_UNCOMPRESSION_FAILED_BASE, nullptr);
303 return false;
304 }
305
306 // Short-circuit if uncompression produced the uncompressed archive rather
307 // than a patch file.
308 if (base::PathExists(archive_helper->target())) {
309 *archive_type = installer::FULL_ARCHIVE_TYPE;
310 return true;
311 }
312
313 // Find the installed version's archive to serve as the source for patching.
314 base::FilePath patch_source(installer::FindArchiveToPatch(
315 original_state, installer_state, previous_version));
316 if (patch_source.empty()) {
317 LOG(ERROR) << "Failed to find archive to patch.";
318 *install_status = installer::DIFF_PATCH_SOURCE_MISSING;
319 installer_state.WriteInstallerResult(
320 *install_status, IDS_INSTALL_UNCOMPRESSION_FAILED_BASE, nullptr);
321 return false;
322 }
323 archive_helper->set_patch_source(patch_source);
324
325 // UMA tells us the following about the time required for patching as of M75:
326 // --- Foreground ---
327 // 12s (50%ile) / 3-6m (99%ile)
328 // --- Background ---
329 // 1m (50%ile) / >60m (99%ile)
330 installer_state.SetStage(installer::PATCHING);
331 if (!archive_helper->ApplyPatch()) {
332 *install_status = installer::APPLY_DIFF_PATCH_FAILED;
333 installer_state.WriteInstallerResult(
334 *install_status, IDS_INSTALL_UNCOMPRESSION_FAILED_BASE, nullptr);
335 return false;
336 }
337
338 *archive_type = installer::INCREMENTAL_ARCHIVE_TYPE;
339 return true;
340 }
341
342 // Repetitively attempts to delete all files that belong to old versions of
343 // Chrome from |install_dir|. Waits 15 seconds before the first attempt and 5
344 // minutes after each unsuccessful attempt. Returns when no files that belong to
345 // an old version of Chrome remain or when another process tries to acquire the
346 // SetupSingleton.
RepeatDeleteOldVersions(const base::FilePath & install_dir,const installer::SetupSingleton & setup_singleton)347 installer::InstallStatus RepeatDeleteOldVersions(
348 const base::FilePath& install_dir,
349 const installer::SetupSingleton& setup_singleton) {
350 // The 99th percentile of the number of attempts it takes to successfully
351 // delete old versions is 2.75. The 75th percentile is 1.77. 98% of calls to
352 // this function will successfully delete old versions.
353 // Source: 30 days of UMA data on June 25, 2019.
354 constexpr int kMaxNumAttempts = 3;
355 int num_attempts = 0;
356
357 while (num_attempts < kMaxNumAttempts) {
358 // Wait 15 seconds before the first attempt because trying to delete old
359 // files right away is likely to fail. Indeed, this is called in 2
360 // occasions:
361 // - When the installer fails to delete old files after a not-in-use update:
362 // retrying immediately is likely to fail again.
363 // - When executables are successfully renamed on Chrome startup or
364 // shutdown: old files can't be deleted because Chrome is still in use.
365 // Wait 5 minutes after an unsuccessful attempt because retrying immediately
366 // is likely to fail again.
367 const base::TimeDelta max_wait_time = num_attempts == 0
368 ? base::TimeDelta::FromSeconds(15)
369 : base::TimeDelta::FromMinutes(5);
370 if (setup_singleton.WaitForInterrupt(max_wait_time)) {
371 VLOG(1) << "Exiting --delete-old-versions process because another "
372 "process tries to acquire the SetupSingleton.";
373 return installer::SETUP_SINGLETON_RELEASED;
374 }
375
376 const bool priority_was_changed_to_background =
377 base::Process::Current().SetProcessBackgrounded(true);
378 const bool delete_old_versions_success =
379 installer::DeleteOldVersions(install_dir);
380 if (priority_was_changed_to_background)
381 base::Process::Current().SetProcessBackgrounded(false);
382 ++num_attempts;
383
384 if (delete_old_versions_success) {
385 VLOG(1) << "Successfully deleted all old files from "
386 "--delete-old-versions process.";
387 return installer::DELETE_OLD_VERSIONS_SUCCESS;
388 } else if (num_attempts == 1) {
389 VLOG(1) << "Failed to delete all old files from --delete-old-versions "
390 "process. Will retry every five minutes.";
391 }
392 }
393
394 VLOG(1) << "Exiting --delete-old-versions process after retrying too many "
395 "times to delete all old files.";
396 DCHECK_EQ(num_attempts, kMaxNumAttempts);
397 return installer::DELETE_OLD_VERSIONS_TOO_MANY_ATTEMPTS;
398 }
399
400 // This function is called when --rename-chrome-exe option is specified on
401 // setup.exe command line. This function assumes an in-use update has happened
402 // for Chrome so there should be files called new_chrome.exe and
403 // new_chrome_proxy.exe on the file system and a key called 'opv' in the
404 // registry. This function will move new_chrome.exe to chrome.exe,
405 // new_chrome_proxy.exe to chrome_proxy.exe and delete 'opv' key in one atomic
406 // operation. This function also deletes elevation policies associated with the
407 // old version if they exist. |setup_exe| is the path to the current executable.
RenameChromeExecutables(const base::FilePath & setup_exe,const InstallationState & original_state,InstallerState * installer_state)408 installer::InstallStatus RenameChromeExecutables(
409 const base::FilePath& setup_exe,
410 const InstallationState& original_state,
411 InstallerState* installer_state) {
412 const base::FilePath& target_path = installer_state->target_path();
413 base::FilePath chrome_exe(target_path.Append(installer::kChromeExe));
414 base::FilePath chrome_new_exe(target_path.Append(installer::kChromeNewExe));
415 base::FilePath chrome_old_exe(target_path.Append(installer::kChromeOldExe));
416 base::FilePath chrome_proxy_exe(
417 target_path.Append(installer::kChromeProxyExe));
418 base::FilePath chrome_proxy_new_exe(
419 target_path.Append(installer::kChromeProxyNewExe));
420 base::FilePath chrome_proxy_old_exe(
421 target_path.Append(installer::kChromeProxyOldExe));
422
423 // Create a temporary backup directory on the same volume as chrome.exe so
424 // that moving in-use files doesn't lead to trouble.
425 installer::SelfCleaningTempDir temp_path;
426 if (!temp_path.Initialize(target_path.DirName(),
427 installer::kInstallTempDir)) {
428 PLOG(ERROR)
429 << "Failed to create Temp directory "
430 << target_path.DirName().Append(installer::kInstallTempDir).value();
431 return installer::RENAME_FAILED;
432 }
433 std::unique_ptr<WorkItemList> install_list(WorkItem::CreateWorkItemList());
434 // Move chrome.exe to old_chrome.exe, then move new_chrome.exe to chrome.exe.
435 install_list->AddMoveTreeWorkItem(chrome_exe, chrome_old_exe,
436 temp_path.path(), WorkItem::ALWAYS_MOVE);
437 install_list->AddMoveTreeWorkItem(chrome_new_exe, chrome_exe,
438 temp_path.path(), WorkItem::ALWAYS_MOVE);
439 install_list->AddDeleteTreeWorkItem(chrome_new_exe, temp_path.path());
440
441 // Move chrome_proxy.exe to old_chrome_proxy.exe if it exists (a previous
442 // installation may not have included it), then move new_chrome_proxy.exe to
443 // chrome_proxy.exe.
444 std::unique_ptr<WorkItemList> existing_proxy_rename_list(
445 WorkItem::CreateConditionalWorkItemList(
446 new ConditionRunIfFileExists(chrome_proxy_exe)));
447 existing_proxy_rename_list->set_log_message("ExistingProxyRenameItemList");
448 existing_proxy_rename_list->AddMoveTreeWorkItem(
449 chrome_proxy_exe, chrome_proxy_old_exe, temp_path.path(),
450 WorkItem::ALWAYS_MOVE);
451 install_list->AddWorkItem(existing_proxy_rename_list.release());
452 install_list->AddMoveTreeWorkItem(chrome_proxy_new_exe, chrome_proxy_exe,
453 temp_path.path(), WorkItem::ALWAYS_MOVE);
454 install_list->AddDeleteTreeWorkItem(chrome_proxy_new_exe, temp_path.path());
455
456 // Add work items to delete Chrome's "opv", "cpv", and "cmd" values.
457 // TODO(grt): Clean this up; https://crbug.com/577816.
458 HKEY reg_root = installer_state->root_key();
459 const base::string16 clients_key = install_static::GetClientsKeyPath();
460
461 install_list->AddDeleteRegValueWorkItem(reg_root, clients_key,
462 KEY_WOW64_32KEY,
463 google_update::kRegOldVersionField);
464 install_list->AddDeleteRegValueWorkItem(
465 reg_root, clients_key, KEY_WOW64_32KEY,
466 google_update::kRegCriticalVersionField);
467 install_list->AddDeleteRegValueWorkItem(reg_root, clients_key,
468 KEY_WOW64_32KEY,
469 google_update::kRegRenameCmdField);
470
471 // If a channel was specified by policy, update the "channel" registry value
472 // with it so that the browser knows which channel to use, otherwise delete
473 // whatever value that key holds.
474 const auto& install_details = install_static::InstallDetails::Get();
475 if (install_details.channel_origin() ==
476 install_static::ChannelOrigin::kPolicy) {
477 install_list->AddSetRegValueWorkItem(reg_root, clients_key, KEY_WOW64_32KEY,
478 google_update::kRegChannelField,
479 install_details.channel(), true);
480 } else {
481 install_list->AddDeleteRegValueWorkItem(reg_root, clients_key,
482 KEY_WOW64_32KEY,
483 google_update::kRegChannelField);
484 }
485
486 // old_chrome.exe is still in use in most cases, so ignore failures here.
487 install_list->AddDeleteTreeWorkItem(chrome_old_exe, temp_path.path())
488 ->set_best_effort(true);
489 install_list->AddDeleteTreeWorkItem(chrome_proxy_old_exe, temp_path.path())
490 ->set_best_effort(true);
491
492 installer::InstallStatus ret = installer::RENAME_SUCCESSFUL;
493 if (install_list->Do()) {
494 installer::LaunchDeleteOldVersionsProcess(setup_exe, *installer_state);
495 } else {
496 LOG(ERROR) << "Renaming of executables failed. Rolling back any changes.";
497 install_list->Rollback();
498 ret = installer::RENAME_FAILED;
499 }
500 // temp_path's dtor will take care of deleting or scheduling itself for
501 // deletion at reboot when this scope closes.
502 VLOG(1) << "Deleting temporary directory " << temp_path.path().value();
503
504 return ret;
505 }
506
507 // Checks for compatibility between the current state of the system and the
508 // desired operation.
509 // Also blocks simultaneous user-level and system-level installs. In the case
510 // of trying to install user-level Chrome when system-level exists, the
511 // existing system-level Chrome is launched.
512 // When the pre-install conditions are not satisfied, the result is written to
513 // the registry (via WriteInstallerResult), |status| is set appropriately, and
514 // false is returned.
CheckPreInstallConditions(const InstallationState & original_state,const InstallerState & installer_state,installer::InstallStatus * status)515 bool CheckPreInstallConditions(const InstallationState& original_state,
516 const InstallerState& installer_state,
517 installer::InstallStatus* status) {
518 if (!installer_state.system_install()) {
519 // This is a user-level installation. Make sure that we are not installing
520 // on top of an existing system-level installation.
521
522 const ProductState* user_level_product_state =
523 original_state.GetProductState(false);
524 const ProductState* system_level_product_state =
525 original_state.GetProductState(true);
526
527 // Allow upgrades to proceed so that out-of-date versions are not left
528 // around.
529 if (user_level_product_state)
530 return true;
531
532 // This is a new user-level install...
533
534 if (system_level_product_state) {
535 // ... and the product already exists at system-level.
536 LOG(ERROR) << "Already installed version "
537 << system_level_product_state->version().GetString()
538 << " at system-level conflicts with this one at user-level.";
539 // Instruct Google Update to launch the existing system-level Chrome.
540 // There should be no error dialog.
541 base::FilePath install_path(
542 installer::GetChromeInstallPath(true /* system_install */));
543 if (install_path.empty()) {
544 // Give up if we failed to construct the install path.
545 *status = installer::OS_ERROR;
546 installer_state.WriteInstallerResult(*status, IDS_INSTALL_OS_ERROR_BASE,
547 nullptr);
548 } else {
549 *status = installer::EXISTING_VERSION_LAUNCHED;
550 base::FilePath chrome_exe = install_path.Append(installer::kChromeExe);
551 base::CommandLine cmd(chrome_exe);
552 cmd.AppendSwitch(switches::kForceFirstRun);
553 installer_state.WriteInstallerResult(
554 *status, IDS_INSTALL_EXISTING_VERSION_LAUNCHED_BASE, nullptr);
555 VLOG(1) << "Launching existing system-level chrome instead.";
556 base::LaunchProcess(cmd, base::LaunchOptions());
557 }
558 return false;
559 }
560 }
561
562 return true;
563 }
564
565 // Initializes |temp_path| to "Temp" within the target directory, and
566 // |unpack_path| to a random directory beginning with "source" within
567 // |temp_path|. Returns false on error.
CreateTemporaryAndUnpackDirectories(const InstallerState & installer_state,installer::SelfCleaningTempDir * temp_path,base::FilePath * unpack_path)568 bool CreateTemporaryAndUnpackDirectories(
569 const InstallerState& installer_state,
570 installer::SelfCleaningTempDir* temp_path,
571 base::FilePath* unpack_path) {
572 DCHECK(temp_path && unpack_path);
573
574 if (!temp_path->Initialize(installer_state.target_path().DirName(),
575 installer::kInstallTempDir)) {
576 PLOG(ERROR) << "Could not create temporary path.";
577 return false;
578 }
579 VLOG(1) << "Created path " << temp_path->path().value();
580
581 if (!base::CreateTemporaryDirInDir(
582 temp_path->path(), installer::kInstallSourceDir, unpack_path)) {
583 PLOG(ERROR) << "Could not create temporary path for unpacked archive.";
584 return false;
585 }
586
587 return true;
588 }
589
UninstallProducts(InstallationState & original_state,InstallerState & installer_state,const base::FilePath & setup_exe,const base::CommandLine & cmd_line)590 installer::InstallStatus UninstallProducts(InstallationState& original_state,
591 InstallerState& installer_state,
592 const base::FilePath& setup_exe,
593 const base::CommandLine& cmd_line) {
594 // System-level Chrome will be launched via this command if its program gets
595 // set below.
596 base::CommandLine system_level_cmd(base::CommandLine::NO_PROGRAM);
597
598 if (cmd_line.HasSwitch(installer::switches::kSelfDestruct) &&
599 !installer_state.system_install()) {
600 const base::FilePath system_exe_path(
601 installer::GetChromeInstallPath(true).Append(installer::kChromeExe));
602 system_level_cmd.SetProgram(system_exe_path);
603 }
604
605 installer::InstallStatus install_status = installer::UNINSTALL_SUCCESSFUL;
606 const bool force = cmd_line.HasSwitch(installer::switches::kForceUninstall);
607 const bool remove_all =
608 !cmd_line.HasSwitch(installer::switches::kDoNotRemoveSharedItems);
609
610 const base::Version current_version(
611 installer_state.GetCurrentVersion(original_state));
612 const installer::ModifyParams modify_params = {
613 installer_state,
614 original_state,
615 setup_exe,
616 current_version,
617 };
618
619 install_status = UninstallProduct(modify_params, remove_all, force, cmd_line);
620
621 installer::CleanUpInstallationDirectoryAfterUninstall(
622 installer_state.target_path(), setup_exe, &install_status);
623
624 // The app and vendor dirs may now be empty. Make a last-ditch attempt to
625 // delete them.
626 installer::DeleteChromeDirectoriesIfEmpty(installer_state.target_path());
627
628 // Trigger Active Setup if it was requested for the chrome product. This needs
629 // to be done after the UninstallProduct calls as some of them might
630 // otherwise terminate the process launched by TriggerActiveSetupCommand().
631 if (cmd_line.HasSwitch(installer::switches::kTriggerActiveSetup))
632 InstallUtil::TriggerActiveSetupCommand();
633
634 if (!system_level_cmd.GetProgram().empty())
635 base::LaunchProcess(system_level_cmd, base::LaunchOptions());
636
637 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
638 // Tell Google Update that an uninstall has taken place if this install did
639 // not originate from the MSI. Google Update has its own logic relating to
640 // MSI-driven uninstalls that conflicts with this. Ignore the return value:
641 // success or failure of Google Update has no bearing on the success or
642 // failure of Chrome's uninstallation.
643 if (!installer_state.is_msi())
644 google_update::UninstallGoogleUpdate(installer_state.system_install());
645 #endif // BUILDFLAG(GOOGLE_CHROME_BRANDING)
646
647 return install_status;
648 }
649
InstallProducts(InstallationState & original_state,const base::FilePath & setup_exe,const base::CommandLine & cmd_line,const InitialPreferences & prefs,InstallerState * installer_state,base::FilePath * installer_directory)650 installer::InstallStatus InstallProducts(InstallationState& original_state,
651 const base::FilePath& setup_exe,
652 const base::CommandLine& cmd_line,
653 const InitialPreferences& prefs,
654 InstallerState* installer_state,
655 base::FilePath* installer_directory) {
656 DCHECK(installer_state);
657 installer::InstallStatus install_status = installer::UNKNOWN_STATUS;
658 installer::ArchiveType archive_type = installer::UNKNOWN_ARCHIVE_TYPE;
659 installer_state->SetStage(installer::PRECONDITIONS);
660 // Remove any legacy "-stage:*" values from the product's "ap" value.
661 installer::UpdateInstallStatus(archive_type, install_status);
662
663 // Drop to background processing mode if the process was started below the
664 // normal process priority class. This is done here because InstallProducts-
665 // Helper has read-only access to the state and because the action also
666 // affects everything else that runs below.
667 const bool entered_background_mode = installer::AdjustProcessPriority();
668 VLOG_IF(1, entered_background_mode) << "Entered background processing mode.";
669
670 if (CheckPreInstallConditions(original_state, *installer_state,
671 &install_status)) {
672 VLOG(1) << "Installing to " << installer_state->target_path().value();
673 install_status = InstallProductsHelper(original_state, setup_exe, cmd_line,
674 prefs, *installer_state,
675 installer_directory, &archive_type);
676 } else {
677 // CheckPreInstallConditions must set the status on failure.
678 DCHECK_NE(install_status, installer::UNKNOWN_STATUS);
679 }
680
681 // Delete the master preferences file if present. Note that we do not care
682 // about rollback here and we schedule for deletion on reboot if the delete
683 // fails. As such, we do not use DeleteTreeWorkItem.
684 if (cmd_line.HasSwitch(installer::switches::kInstallerData)) {
685 base::FilePath prefs_path(
686 cmd_line.GetSwitchValuePath(installer::switches::kInstallerData));
687 if (!base::DeleteFile(prefs_path)) {
688 LOG(ERROR) << "Failed deleting master preferences file "
689 << prefs_path.value()
690 << ", scheduling for deletion after reboot.";
691 ScheduleFileSystemEntityForDeletion(prefs_path);
692 }
693 }
694
695 UpdateInstallStatus(archive_type, install_status);
696
697 return install_status;
698 }
699
ShowEulaDialog(const base::string16 & inner_frame)700 installer::InstallStatus ShowEulaDialog(const base::string16& inner_frame) {
701 VLOG(1) << "About to show EULA";
702 base::string16 eula_path = installer::GetLocalizedEulaResource();
703 if (eula_path.empty()) {
704 LOG(ERROR) << "No EULA path available";
705 return installer::EULA_REJECTED;
706 }
707 // Newer versions of the caller pass an inner frame parameter that must
708 // be given to the html page being launched.
709 installer::EulaHTMLDialog dlg(eula_path, inner_frame);
710 installer::EulaHTMLDialog::Outcome outcome = dlg.ShowModal();
711 if (installer::EulaHTMLDialog::REJECTED == outcome) {
712 LOG(ERROR) << "EULA rejected or EULA failure";
713 return installer::EULA_REJECTED;
714 }
715 if (installer::EulaHTMLDialog::ACCEPTED_OPT_IN == outcome) {
716 VLOG(1) << "EULA accepted (opt-in)";
717 return installer::EULA_ACCEPTED_OPT_IN;
718 }
719 VLOG(1) << "EULA accepted (no opt-in)";
720 return installer::EULA_ACCEPTED;
721 }
722
723 // Creates the sentinel indicating that the EULA was required and has been
724 // accepted.
CreateEulaSentinel()725 bool CreateEulaSentinel() {
726 base::FilePath eula_sentinel;
727 if (!InstallUtil::GetEulaSentinelFilePath(&eula_sentinel))
728 return false;
729
730 return (base::CreateDirectory(eula_sentinel.DirName()) &&
731 base::WriteFile(eula_sentinel, "", 0) != -1);
732 }
733
RegisterDevChrome(const installer::ModifyParams & modify_params,const base::CommandLine & cmd_line)734 installer::InstallStatus RegisterDevChrome(
735 const installer::ModifyParams& modify_params,
736 const base::CommandLine& cmd_line) {
737 const InstallationState& original_state = modify_params.installation_state;
738 const base::FilePath& setup_exe = modify_params.setup_path;
739
740 // Only proceed with registering a dev chrome if no real Chrome installation
741 // of the same install mode is present on this system.
742 const ProductState* existing_chrome = original_state.GetProductState(false);
743 if (!existing_chrome)
744 existing_chrome = original_state.GetProductState(true);
745 if (existing_chrome) {
746 static const wchar_t kPleaseUninstallYourChromeMessage[] =
747 L"You already have a full-installation (non-dev) of %1ls, please "
748 L"uninstall it first using Add/Remove Programs in the control panel.";
749 base::string16 name(InstallUtil::GetDisplayName());
750 base::string16 message(
751 base::StringPrintf(kPleaseUninstallYourChromeMessage, name.c_str()));
752
753 LOG(ERROR) << "Aborting operation: another installation of " << name
754 << " was found, as a last resort (if the product is not present "
755 "in Add/Remove Programs), try executing: "
756 << existing_chrome->uninstall_command().GetCommandLineString();
757 MessageBox(nullptr, message.c_str(), nullptr, MB_ICONERROR);
758 return installer::INSTALL_FAILED;
759 }
760
761 base::FilePath chrome_exe(
762 cmd_line.GetSwitchValuePath(installer::switches::kRegisterDevChrome));
763 if (chrome_exe.empty())
764 chrome_exe = setup_exe.DirName().Append(installer::kChromeExe);
765 if (!chrome_exe.IsAbsolute())
766 chrome_exe = base::MakeAbsoluteFilePath(chrome_exe);
767
768 installer::InstallStatus status = installer::FIRST_INSTALL_SUCCESS;
769 if (base::PathExists(chrome_exe)) {
770 // Create the Start menu shortcut and pin it to the Win7+ taskbar.
771 ShellUtil::ShortcutProperties shortcut_properties(ShellUtil::CURRENT_USER);
772 ShellUtil::AddDefaultShortcutProperties(chrome_exe, &shortcut_properties);
773 shortcut_properties.set_pin_to_taskbar(true);
774 ShellUtil::CreateOrUpdateShortcut(
775 ShellUtil::SHORTCUT_LOCATION_START_MENU_ROOT, shortcut_properties,
776 ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS);
777
778 // Register Chrome at user-level and make it default.
779 if (ShellUtil::CanMakeChromeDefaultUnattended()) {
780 ShellUtil::MakeChromeDefault(ShellUtil::CURRENT_USER, chrome_exe, true);
781 } else {
782 ShellUtil::ShowMakeChromeDefaultSystemUI(chrome_exe);
783 }
784 } else {
785 LOG(ERROR) << "Path not found: " << chrome_exe.value();
786 status = installer::INSTALL_FAILED;
787 }
788 return status;
789 }
790
791 // This method processes any command line options that make setup.exe do
792 // various tasks other than installation (renaming chrome.exe, showing eula
793 // among others). This function returns true if any such command line option
794 // has been found and processed (so setup.exe should exit at that point).
HandleNonInstallCmdLineOptions(installer::ModifyParams & modify_params,const base::CommandLine & cmd_line,int * exit_code)795 bool HandleNonInstallCmdLineOptions(installer::ModifyParams& modify_params,
796 const base::CommandLine& cmd_line,
797 int* exit_code) {
798 installer::InstallerState* installer_state = &(modify_params.installer_state);
799 installer::InstallationState* original_state =
800 &(modify_params.installation_state);
801 const base::FilePath& setup_exe = modify_params.setup_path;
802
803 // This option is independent of all others so doesn't belong in the if/else
804 // block below.
805 if (cmd_line.HasSwitch(installer::switches::kDelay)) {
806 const std::string delay_seconds_string(
807 cmd_line.GetSwitchValueASCII(installer::switches::kDelay));
808 int delay_seconds;
809 if (base::StringToInt(delay_seconds_string, &delay_seconds) &&
810 delay_seconds > 0) {
811 base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(delay_seconds));
812 }
813 }
814
815 // TODO(gab): Add a local |status| variable which each block below sets;
816 // only determine the |exit_code| from |status| at the end (this will allow
817 // this method to validate that
818 // (!handled || status != installer::UNKNOWN_STATUS)).
819 bool handled = true;
820 // TODO(tommi): Split these checks up into functions and use a data driven
821 // map of switch->function.
822 if (cmd_line.HasSwitch(installer::switches::kUpdateSetupExe)) {
823 installer_state->SetStage(installer::UPDATING_SETUP);
824 installer::InstallStatus status = installer::SETUP_PATCH_FAILED;
825 // If --update-setup-exe command line option is given, we apply the given
826 // patch to current exe, and store the resulting binary in the path
827 // specified by --new-setup-exe. But we need to first unpack the file
828 // given in --update-setup-exe.
829 base::ScopedTempDir temp_path;
830 if (!temp_path.CreateUniqueTempDir()) {
831 PLOG(ERROR) << "Could not create temporary path.";
832 } else {
833 base::FilePath compressed_archive(
834 cmd_line.GetSwitchValuePath(installer::switches::kUpdateSetupExe));
835 VLOG(1) << "Opening archive " << compressed_archive.value();
836 // The top unpack failure result with 28 days aggregation (>=0.01%)
837 // Setup.Install.LzmaUnPackResult_SetupExePatch
838 // 0.02% PATH_NOT_FOUND
839 //
840 // More information can also be found with metric:
841 // Setup.Install.LzmaUnPackNTSTATUS_SetupExePatch
842 if (installer::ArchivePatchHelper::UncompressAndPatch(
843 temp_path.GetPath(), compressed_archive, setup_exe,
844 cmd_line.GetSwitchValuePath(installer::switches::kNewSetupExe),
845 installer::UnPackConsumer::SETUP_EXE_PATCH)) {
846 status = installer::NEW_VERSION_UPDATED;
847 }
848 if (!temp_path.Delete()) {
849 // PLOG would be nice, but Delete() doesn't leave a meaningful value in
850 // the Windows last-error code.
851 LOG(WARNING) << "Scheduling temporary path "
852 << temp_path.GetPath().value()
853 << " for deletion at reboot.";
854 ScheduleDirectoryForDeletion(temp_path.GetPath());
855 }
856 }
857
858 *exit_code = InstallUtil::GetInstallReturnCode(status);
859 if (*exit_code) {
860 LOG(WARNING) << "setup.exe patching failed.";
861 installer_state->WriteInstallerResult(status, IDS_SETUP_PATCH_FAILED_BASE,
862 nullptr);
863 }
864 } else if (cmd_line.HasSwitch(installer::switches::kShowEula)) {
865 // Check if we need to show the EULA. If it is passed as a command line
866 // then the dialog is shown and regardless of the outcome setup exits here.
867 base::string16 inner_frame =
868 cmd_line.GetSwitchValueNative(installer::switches::kShowEula);
869 *exit_code = ShowEulaDialog(inner_frame);
870
871 if (installer::EULA_REJECTED != *exit_code) {
872 if (GoogleUpdateSettings::SetEulaConsent(*original_state, true))
873 CreateEulaSentinel();
874 }
875 } else if (cmd_line.HasSwitch(installer::switches::kConfigureUserSettings)) {
876 // NOTE: Should the work done here, on kConfigureUserSettings, change:
877 // kActiveSetupVersion in install_worker.cc needs to be increased for Active
878 // Setup to invoke this again for all users of this install.
879 installer::InstallStatus status = installer::INVALID_STATE_FOR_OPTION;
880 if (installer_state->system_install()) {
881 bool force =
882 cmd_line.HasSwitch(installer::switches::kForceConfigureUserSettings);
883 installer::HandleActiveSetupForBrowser(*installer_state, force);
884 status = installer::INSTALL_REPAIRED;
885 } else {
886 LOG(DFATAL)
887 << "--configure-user-settings is incompatible with user-level";
888 }
889 *exit_code = InstallUtil::GetInstallReturnCode(status);
890 } else if (cmd_line.HasSwitch(installer::switches::kRegisterDevChrome)) {
891 installer::InstallStatus status =
892 RegisterDevChrome(modify_params, cmd_line);
893 *exit_code = InstallUtil::GetInstallReturnCode(status);
894 } else if (cmd_line.HasSwitch(installer::switches::kRegisterChromeBrowser)) {
895 installer::InstallStatus status = installer::UNKNOWN_STATUS;
896 // If --register-chrome-browser option is specified, register all Chrome
897 // protocol/file associations, as well as register it as a valid browser for
898 // Start Menu->Internet shortcut. This switch will also register Chrome as a
899 // valid handler for a set of URL protocols that Chrome may become the
900 // default handler for, either by the user marking Chrome as the default
901 // browser, through the Windows Default Programs control panel settings, or
902 // through website use of registerProtocolHandler. These protocols are found
903 // in ShellUtil::kPotentialProtocolAssociations. The
904 // --register-url-protocol will additionally register Chrome as a potential
905 // handler for the supplied protocol, and is used if a website registers a
906 // handler for a protocol not found in
907 // ShellUtil::kPotentialProtocolAssociations. These options should only be
908 // used when setup.exe is launched with admin rights. We do not make any
909 // user specific changes with this option.
910 DCHECK(IsUserAnAdmin());
911 base::FilePath chrome_exe(cmd_line.GetSwitchValuePath(
912 installer::switches::kRegisterChromeBrowser));
913 base::string16 suffix;
914 if (cmd_line.HasSwitch(installer::switches::kRegisterChromeBrowserSuffix)) {
915 suffix = cmd_line.GetSwitchValueNative(
916 installer::switches::kRegisterChromeBrowserSuffix);
917 }
918 if (cmd_line.HasSwitch(installer::switches::kRegisterURLProtocol)) {
919 base::string16 protocol = cmd_line.GetSwitchValueNative(
920 installer::switches::kRegisterURLProtocol);
921 // ShellUtil::RegisterChromeForProtocol performs all registration
922 // done by ShellUtil::RegisterChromeBrowser, as well as registering
923 // with Windows as capable of handling the supplied protocol.
924 if (ShellUtil::RegisterChromeForProtocol(chrome_exe, suffix, protocol,
925 false))
926 status = installer::IN_USE_UPDATED;
927 } else {
928 if (ShellUtil::RegisterChromeBrowser(chrome_exe, suffix,
929 /*elevate_if_not_admin=*/false))
930 status = installer::IN_USE_UPDATED;
931 }
932 *exit_code = InstallUtil::GetInstallReturnCode(status);
933 } else if (cmd_line.HasSwitch(installer::switches::kDeleteOldVersions) ||
934 cmd_line.HasSwitch(installer::switches::kRenameChromeExe)) {
935 std::unique_ptr<installer::SetupSingleton> setup_singleton(
936 installer::SetupSingleton::Acquire(
937 cmd_line, InitialPreferences::ForCurrentProcess(), original_state,
938 installer_state));
939 if (!setup_singleton) {
940 *exit_code = installer::SETUP_SINGLETON_ACQUISITION_FAILED;
941 } else if (cmd_line.HasSwitch(installer::switches::kDeleteOldVersions)) {
942 *exit_code = RepeatDeleteOldVersions(installer_state->target_path(),
943 *setup_singleton);
944 } else {
945 DCHECK(cmd_line.HasSwitch(installer::switches::kRenameChromeExe));
946 *exit_code =
947 RenameChromeExecutables(setup_exe, *original_state, installer_state);
948 }
949 } else if (cmd_line.HasSwitch(
950 installer::switches::kRemoveChromeRegistration)) {
951 // This is almost reverse of --register-chrome-browser option above.
952 // Here we delete Chrome browser registration. This option should only
953 // be used when setup.exe is launched with admin rights. We do not
954 // make any user specific changes in this option.
955 base::string16 suffix;
956 if (cmd_line.HasSwitch(installer::switches::kRegisterChromeBrowserSuffix)) {
957 suffix = cmd_line.GetSwitchValueNative(
958 installer::switches::kRegisterChromeBrowserSuffix);
959 }
960 installer::InstallStatus tmp = installer::UNKNOWN_STATUS;
961 installer::DeleteChromeRegistrationKeys(*installer_state,
962 HKEY_LOCAL_MACHINE, suffix, &tmp);
963 *exit_code = tmp;
964 } else if (cmd_line.HasSwitch(installer::switches::kOnOsUpgrade)) {
965 installer::InstallStatus status = installer::INVALID_STATE_FOR_OPTION;
966 std::unique_ptr<FileVersionInfo> version_info(
967 FileVersionInfo::CreateFileVersionInfo(setup_exe));
968 const base::Version installed_version(
969 base::UTF16ToUTF8(version_info->product_version()));
970 if (installed_version.IsValid()) {
971 installer::HandleOsUpgradeForBrowser(*installer_state, installed_version);
972 status = installer::INSTALL_REPAIRED;
973 } else {
974 LOG(DFATAL) << "Failed to extract product version from "
975 << setup_exe.value();
976 }
977 *exit_code = InstallUtil::GetInstallReturnCode(status);
978 } else if (cmd_line.HasSwitch(installer::switches::kUserExperiment)) {
979 installer::RunUserExperiment(cmd_line,
980 InitialPreferences::ForCurrentProcess(),
981 original_state, installer_state);
982 exit_code = 0;
983 } else if (cmd_line.HasSwitch(installer::switches::kPatch)) {
984 const std::string patch_type_str(
985 cmd_line.GetSwitchValueASCII(installer::switches::kPatch));
986 const base::FilePath input_file(
987 cmd_line.GetSwitchValuePath(installer::switches::kInputFile));
988 const base::FilePath patch_file(
989 cmd_line.GetSwitchValuePath(installer::switches::kPatchFile));
990 const base::FilePath output_file(
991 cmd_line.GetSwitchValuePath(installer::switches::kOutputFile));
992
993 if (patch_type_str == installer::kCourgette) {
994 *exit_code =
995 installer::CourgettePatchFiles(input_file, patch_file, output_file);
996 } else if (patch_type_str == installer::kBsdiff) {
997 *exit_code =
998 installer::BsdiffPatchFiles(input_file, patch_file, output_file);
999 #if BUILDFLAG(ZUCCHINI)
1000 } else if (patch_type_str == installer::kZucchini) {
1001 *exit_code =
1002 installer::ZucchiniPatchFiles(input_file, patch_file, output_file);
1003 #endif // BUILDFLAG(ZUCCHINI)
1004 } else {
1005 *exit_code = installer::PATCH_INVALID_ARGUMENTS;
1006 }
1007 } else if (cmd_line.HasSwitch(installer::switches::kReenableAutoupdates)) {
1008 // setup.exe has been asked to attempt to reenable updates for Chrome.
1009 bool updates_enabled = GoogleUpdateSettings::ReenableAutoupdates();
1010 *exit_code = updates_enabled ? installer::REENABLE_UPDATES_SUCCEEDED
1011 : installer::REENABLE_UPDATES_FAILED;
1012 } else if (cmd_line.HasSwitch(
1013 installer::switches::kSetDisplayVersionProduct)) {
1014 const base::string16 registry_product(cmd_line.GetSwitchValueNative(
1015 installer::switches::kSetDisplayVersionProduct));
1016 const base::string16 registry_value(cmd_line.GetSwitchValueNative(
1017 installer::switches::kSetDisplayVersionValue));
1018 *exit_code = OverwriteDisplayVersions(registry_product, registry_value);
1019 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
1020 } else if (cmd_line.HasSwitch(installer::switches::kStoreDMToken)) {
1021 // Write the specified token to the registry, overwriting any already
1022 // existing value.
1023 base::string16 token_switch_value =
1024 cmd_line.GetSwitchValueNative(installer::switches::kStoreDMToken);
1025 base::Optional<std::string> token;
1026 if (!(token = installer::DecodeDMTokenSwitchValue(token_switch_value)) ||
1027 !installer::StoreDMToken(*token)) {
1028 *exit_code = installer::STORE_DMTOKEN_FAILED;
1029 } else {
1030 *exit_code = installer::STORE_DMTOKEN_SUCCESS;
1031 }
1032 #endif
1033 } else {
1034 handled = false;
1035 }
1036
1037 return handled;
1038 }
1039
1040 } // namespace
1041
1042 namespace installer {
1043
InstallProductsHelper(InstallationState & original_state,const base::FilePath & setup_exe,const base::CommandLine & cmd_line,const InitialPreferences & prefs,InstallerState & installer_state,base::FilePath * installer_directory,ArchiveType * archive_type)1044 InstallStatus InstallProductsHelper(InstallationState& original_state,
1045 const base::FilePath& setup_exe,
1046 const base::CommandLine& cmd_line,
1047 const InitialPreferences& prefs,
1048 InstallerState& installer_state,
1049 base::FilePath* installer_directory,
1050 ArchiveType* archive_type) {
1051 DCHECK(archive_type);
1052 const bool system_install = installer_state.system_install();
1053 InstallStatus install_status = UNKNOWN_STATUS;
1054
1055 // Create a temp folder where we will unpack Chrome archive. If it fails,
1056 // then we are doomed, so return immediately and no cleanup is required.
1057 SelfCleaningTempDir temp_path;
1058 base::FilePath unpack_path;
1059 if (!CreateTemporaryAndUnpackDirectories(installer_state, &temp_path,
1060 &unpack_path)) {
1061 installer_state.WriteInstallerResult(
1062 TEMP_DIR_FAILED, IDS_INSTALL_TEMP_DIR_FAILED_BASE, nullptr);
1063 return TEMP_DIR_FAILED;
1064 }
1065
1066 // Uncompress and optionally patch the archive if an uncompressed archive was
1067 // not specified on the command line and a compressed archive is found.
1068 *archive_type = UNKNOWN_ARCHIVE_TYPE;
1069 base::FilePath uncompressed_archive(
1070 cmd_line.GetSwitchValuePath(switches::kUncompressedArchive));
1071 if (uncompressed_archive.empty()) {
1072 base::Version previous_version;
1073 if (cmd_line.HasSwitch(installer::switches::kPreviousVersion)) {
1074 previous_version = base::Version(
1075 cmd_line.GetSwitchValueASCII(installer::switches::kPreviousVersion));
1076 }
1077
1078 std::unique_ptr<ArchivePatchHelper> archive_helper(
1079 CreateChromeArchiveHelper(
1080 setup_exe, cmd_line, installer_state, unpack_path,
1081 (previous_version.IsValid()
1082 ? UnPackConsumer::CHROME_ARCHIVE_PATCH
1083 : UnPackConsumer::COMPRESSED_CHROME_ARCHIVE)));
1084 if (archive_helper) {
1085 VLOG(1) << "Installing Chrome from compressed archive "
1086 << archive_helper->compressed_archive().value();
1087 if (!UncompressAndPatchChromeArchive(original_state, installer_state,
1088 archive_helper.get(), archive_type,
1089 &install_status, previous_version)) {
1090 DCHECK_NE(install_status, UNKNOWN_STATUS);
1091 return install_status;
1092 }
1093 uncompressed_archive = archive_helper->target();
1094 DCHECK(!uncompressed_archive.empty());
1095 }
1096 }
1097
1098 // Check for an uncompressed archive alongside the current executable if one
1099 // was not given or generated.
1100 if (uncompressed_archive.empty())
1101 uncompressed_archive = setup_exe.DirName().Append(kChromeArchive);
1102
1103 if (*archive_type == UNKNOWN_ARCHIVE_TYPE) {
1104 // An archive was not uncompressed or patched above.
1105 if (uncompressed_archive.empty() ||
1106 !base::PathExists(uncompressed_archive)) {
1107 LOG(ERROR) << "Cannot install Chrome without an uncompressed archive.";
1108 installer_state.WriteInstallerResult(
1109 INVALID_ARCHIVE, IDS_INSTALL_INVALID_ARCHIVE_BASE, nullptr);
1110 return INVALID_ARCHIVE;
1111 }
1112 *archive_type = FULL_ARCHIVE_TYPE;
1113 }
1114
1115 // Unpack the uncompressed archive.
1116 // UMA tells us the following about the time required to unpack as of M75:
1117 // --- Foreground ---
1118 // <2.7s (50%ile) / 45s (99%ile)
1119 // --- Background ---
1120 // ~14s (50%ile) / >3m (99%ile)
1121 //
1122 // The top unpack failure result with 28 days aggregation (>=0.01%)
1123 // Setup.Install.LzmaUnPackResult_UncompressedChromeArchive
1124 // 0.66% DISK_FULL
1125 // 0.04% ACCESS_DENIED
1126 // 0.01% INVALID_HANDLE
1127 // 0.01% ERROR_NO_SYSTEM_RESOURCES
1128 // 0.01% PATH_NOT_FOUND
1129 // 0.01% ERROR_IO_DEVICE
1130 //
1131 // More information can also be found with metric:
1132 // Setup.Install.LzmaUnPackNTSTATUS_UncompressedChromeArchive
1133 installer_state.SetStage(UNPACKING);
1134 UnPackStatus unpack_status = UnPackArchive(uncompressed_archive, unpack_path,
1135 /*output_file=*/nullptr);
1136 RecordUnPackMetrics(unpack_status,
1137 UnPackConsumer::UNCOMPRESSED_CHROME_ARCHIVE);
1138 if (unpack_status != UNPACK_NO_ERROR) {
1139 installer_state.WriteInstallerResult(
1140 UNPACKING_FAILED, IDS_INSTALL_UNCOMPRESSION_FAILED_BASE, nullptr);
1141 return UNPACKING_FAILED;
1142 }
1143
1144 VLOG(1) << "unpacked to " << unpack_path.value();
1145 base::FilePath src_path(unpack_path.Append(kInstallSourceChromeDir));
1146 std::unique_ptr<base::Version> installer_version(
1147 GetMaxVersionFromArchiveDir(src_path));
1148 if (!installer_version.get()) {
1149 LOG(ERROR) << "Did not find any valid version in installer.";
1150 install_status = INVALID_ARCHIVE;
1151 installer_state.WriteInstallerResult(
1152 install_status, IDS_INSTALL_INVALID_ARCHIVE_BASE, nullptr);
1153 } else {
1154 VLOG(1) << "version to install: " << installer_version->GetString();
1155 bool proceed_with_installation = true;
1156
1157 if (!IsDowngradeAllowed(prefs)) {
1158 const ProductState* product_state =
1159 original_state.GetProductState(system_install);
1160 if (product_state != nullptr &&
1161 (product_state->version().CompareTo(*installer_version) > 0)) {
1162 LOG(ERROR) << "Higher version of Chrome is already installed.";
1163 int message_id = IDS_INSTALL_HIGHER_VERSION_BASE;
1164 proceed_with_installation = false;
1165 install_status = HIGHER_VERSION_EXISTS;
1166 installer_state.WriteInstallerResult(install_status, message_id,
1167 nullptr);
1168 }
1169 }
1170
1171 if (proceed_with_installation) {
1172 base::FilePath prefs_source_path(
1173 cmd_line.GetSwitchValueNative(switches::kInstallerData));
1174
1175 const base::Version current_version(
1176 installer_state.GetCurrentVersion(original_state));
1177 InstallParams install_params = {
1178 installer_state, original_state, setup_exe,
1179 current_version, uncompressed_archive, src_path,
1180 temp_path.path(), *installer_version,
1181 };
1182
1183 install_status =
1184 InstallOrUpdateProduct(install_params, prefs_source_path, prefs);
1185
1186 int install_msg_base = IDS_INSTALL_FAILED_BASE;
1187 base::FilePath chrome_exe;
1188 base::string16 quoted_chrome_exe;
1189 if (install_status == SAME_VERSION_REPAIR_FAILED) {
1190 install_msg_base = IDS_SAME_VERSION_REPAIR_FAILED_BASE;
1191 } else if (install_status != INSTALL_FAILED) {
1192 if (installer_state.target_path().empty()) {
1193 // If we failed to construct install path, it means the OS call to
1194 // get %ProgramFiles% or %AppData% failed. Report this as failure.
1195 install_msg_base = IDS_INSTALL_OS_ERROR_BASE;
1196 install_status = OS_ERROR;
1197 } else {
1198 chrome_exe = installer_state.target_path().Append(kChromeExe);
1199 quoted_chrome_exe = L"\"" + chrome_exe.value() + L"\"";
1200 install_msg_base = 0;
1201 }
1202 }
1203
1204 installer_state.SetStage(FINISHING);
1205
1206 bool do_not_register_for_update_launch = false;
1207 prefs.GetBool(initial_preferences::kDoNotRegisterForUpdateLaunch,
1208 &do_not_register_for_update_launch);
1209
1210 bool write_chrome_launch_string = (!do_not_register_for_update_launch &&
1211 install_status != IN_USE_UPDATED);
1212
1213 installer_state.WriteInstallerResult(
1214 install_status, install_msg_base,
1215 write_chrome_launch_string ? "ed_chrome_exe : nullptr);
1216
1217 if (install_status == FIRST_INSTALL_SUCCESS) {
1218 VLOG(1) << "First install successful.";
1219 // We never want to launch Chrome in system level install mode.
1220 bool do_not_launch_chrome = false;
1221 prefs.GetBool(initial_preferences::kDoNotLaunchChrome,
1222 &do_not_launch_chrome);
1223 if (!system_install && !do_not_launch_chrome)
1224 LaunchChromeBrowser(installer_state.target_path());
1225 } else if ((install_status == NEW_VERSION_UPDATED) ||
1226 (install_status == IN_USE_UPDATED)) {
1227 DCHECK_NE(chrome_exe.value(), base::string16());
1228 RemoveChromeLegacyRegistryKeys(chrome_exe);
1229 }
1230 }
1231 }
1232
1233 // If the installation completed successfully...
1234 if (InstallUtil::GetInstallReturnCode(install_status) == 0) {
1235 // Update the DisplayVersion created by an MSI-based install.
1236 base::FilePath master_preferences_file(
1237 installer_state.target_path().AppendASCII(
1238 installer::kDefaultMasterPrefs));
1239 std::string install_id;
1240 if (prefs.GetString(installer::initial_preferences::kMsiProductId,
1241 &install_id)) {
1242 // A currently active MSI install will have specified the master-
1243 // preferences file on the command-line that includes the product-id.
1244 // We must delay the setting of the DisplayVersion until after the
1245 // grandparent "msiexec" process has exited.
1246 base::FilePath new_setup =
1247 installer_state.GetInstallerDirectory(*installer_version)
1248 .Append(kSetupExe);
1249 DelayedOverwriteDisplayVersions(new_setup, install_id,
1250 *installer_version);
1251 } else {
1252 // Only when called by the MSI installer do we need to delay setting
1253 // the DisplayVersion. In other runs, such as those done by the auto-
1254 // update action, we set the value immediately.
1255 // Get the app's MSI Product-ID from an entry in ClientState.
1256 base::string16 app_guid = FindMsiProductId(installer_state);
1257 if (!app_guid.empty()) {
1258 OverwriteDisplayVersions(
1259 app_guid, base::UTF8ToUTF16(installer_version->GetString()));
1260 }
1261 }
1262 // Return the path to the directory containing the newly installed
1263 // setup.exe and uncompressed archive if the caller requested it.
1264 if (installer_directory) {
1265 *installer_directory =
1266 installer_state.GetInstallerDirectory(*installer_version);
1267 }
1268 }
1269
1270 // temp_path's dtor will take care of deleting or scheduling itself for
1271 // deletion at reboot when this scope closes.
1272 VLOG(1) << "Deleting temporary directory " << temp_path.path().value();
1273
1274 return install_status;
1275 }
1276
1277 } // namespace installer
1278
wWinMain(HINSTANCE instance,HINSTANCE prev_instance,wchar_t * command_line,int show_command)1279 int WINAPI wWinMain(HINSTANCE instance,
1280 HINSTANCE prev_instance,
1281 wchar_t* command_line,
1282 int show_command) {
1283 // Check to see if the CPU is supported before doing anything else. There's
1284 // very little than can safely be accomplished if the CPU isn't supported
1285 // since dependent libraries (e.g., base) may use invalid instructions.
1286 if (!installer::IsProcessorSupported())
1287 return installer::CPU_NOT_SUPPORTED;
1288
1289 // Persist histograms so they can be uploaded later. The storage directory is
1290 // created during installation when the main WorkItemList is evaluated so
1291 // disable storage directory creation in PersistentHistogramStorage.
1292 base::PersistentHistogramStorage persistent_histogram_storage(
1293 installer::kSetupHistogramAllocatorName,
1294 base::PersistentHistogramStorage::StorageDirManagement::kUseExisting);
1295
1296 // The exit manager is in charge of calling the dtors of singletons.
1297 base::AtExitManager exit_manager;
1298 base::CommandLine::Init(0, nullptr);
1299
1300 std::string process_type =
1301 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
1302 switches::kProcessType);
1303
1304 if (process_type == crash_reporter::switches::kCrashpadHandler) {
1305 // Histogram storage is enabled at the very top of this wWinMain. Disable it
1306 // when this process is decicated to crashpad as there is no directory in
1307 // which to write them nor a browser to subsequently upload them.
1308 persistent_histogram_storage.Disable();
1309 return crash_reporter::RunAsCrashpadHandler(
1310 *base::CommandLine::ForCurrentProcess(), base::FilePath(),
1311 switches::kProcessType, switches::kUserDataDir);
1312 }
1313
1314 // install_util uses chrome paths.
1315 chrome::RegisterPathProvider();
1316
1317 const InitialPreferences& prefs = InitialPreferences::ForCurrentProcess();
1318 installer::InitInstallerLogging(prefs);
1319
1320 const base::CommandLine& cmd_line = *base::CommandLine::ForCurrentProcess();
1321 VLOG(1) << "Command Line: " << cmd_line.GetCommandLineString();
1322
1323 InitializeInstallDetails(cmd_line, prefs);
1324
1325 bool system_install = false;
1326 prefs.GetBool(installer::initial_preferences::kSystemLevel, &system_install);
1327 VLOG(1) << "system install is " << system_install;
1328
1329 InstallationState original_state;
1330 original_state.Initialize();
1331
1332 InstallerState installer_state;
1333 installer_state.Initialize(cmd_line, prefs, original_state);
1334
1335 persistent_histogram_storage.set_storage_base_dir(
1336 installer_state.target_path());
1337
1338 installer::ConfigureCrashReporting(installer_state);
1339 installer::SetInitialCrashKeys(installer_state);
1340 installer::SetCrashKeysFromCommandLine(cmd_line);
1341
1342 // Make sure the process exits cleanly on unexpected errors.
1343 base::EnableTerminationOnHeapCorruption();
1344 base::EnableTerminationOnOutOfMemory();
1345 base::win::RegisterInvalidParamHandler();
1346 base::win::SetupCRT(cmd_line);
1347
1348 #if defined(ARCH_CPU_64_BITS) || defined(NDEBUG)
1349 // Disable the handle verifier for all but 32-bit debug builds.
1350 base::win::DisableHandleVerifier();
1351 #endif
1352
1353 const bool is_uninstall = cmd_line.HasSwitch(installer::switches::kUninstall);
1354
1355 // Histogram storage is enabled at the very top of this wWinMain. Disable it
1356 // during uninstall since there's neither a directory in which to write them
1357 // nor a browser to subsequently upload them.
1358 if (is_uninstall)
1359 persistent_histogram_storage.Disable();
1360
1361 // Check to make sure current system is Win7 or later. If not, log
1362 // error message and get out.
1363 if (!InstallUtil::IsOSSupported()) {
1364 LOG(ERROR) << "Chrome only supports Windows 7 or later.";
1365 installer_state.WriteInstallerResult(installer::OS_NOT_SUPPORTED,
1366 IDS_INSTALL_OS_NOT_SUPPORTED_BASE,
1367 nullptr);
1368 return installer::OS_NOT_SUPPORTED;
1369 }
1370
1371 // Initialize COM for use later.
1372 base::win::ScopedCOMInitializer com_initializer;
1373 if (!com_initializer.Succeeded()) {
1374 installer_state.WriteInstallerResult(installer::OS_ERROR,
1375 IDS_INSTALL_OS_ERROR_BASE, nullptr);
1376 return installer::OS_ERROR;
1377 }
1378
1379 // Make sure system_level is supported if requested. For historical reasons,
1380 // system-level installs have never been supported for Chrome canary (SxS).
1381 // This is a brand-specific policy for this particular mode. In general,
1382 // system-level installation of secondary install modes is fully supported.
1383 if (!install_static::InstallDetails::Get().supports_system_level() &&
1384 (system_install ||
1385 cmd_line.HasSwitch(installer::switches::kSelfDestruct) ||
1386 cmd_line.HasSwitch(installer::switches::kRemoveChromeRegistration))) {
1387 return installer::SXS_OPTION_NOT_SUPPORTED;
1388 }
1389 // Some switches only apply for modes that can be made the user's default
1390 // browser.
1391 if (!install_static::SupportsSetAsDefaultBrowser() &&
1392 (cmd_line.HasSwitch(installer::switches::kMakeChromeDefault) ||
1393 cmd_line.HasSwitch(installer::switches::kRegisterChromeBrowser))) {
1394 return installer::SXS_OPTION_NOT_SUPPORTED;
1395 }
1396 // Some switches only apply for modes that support retention experiments.
1397 if (!install_static::SupportsRetentionExperiments() &&
1398 cmd_line.HasSwitch(installer::switches::kUserExperiment)) {
1399 return installer::SXS_OPTION_NOT_SUPPORTED;
1400 }
1401
1402 // Some command line options are no longer supported and must error out.
1403 if (installer::ContainsUnsupportedSwitch(cmd_line))
1404 return installer::UNSUPPORTED_OPTION;
1405
1406 // A variety of installer operations require the path to the current
1407 // executable. Get it once here for use throughout these operations. Note that
1408 // the path service is the authoritative source for this path. One might think
1409 // that CommandLine::GetProgram would suffice, but it won't since
1410 // CreateProcess may have been called with a command line that is somewhat
1411 // ambiguous (e.g., an unquoted path with spaces, or a path lacking the file
1412 // extension), in which case CommandLineToArgv will not yield an argv with the
1413 // true path to the program at position 0.
1414 base::FilePath setup_exe;
1415 base::PathService::Get(base::FILE_EXE, &setup_exe);
1416
1417 const base::Version current_version(
1418 installer_state.GetCurrentVersion(original_state));
1419 installer::ModifyParams modify_params = {
1420 installer_state,
1421 original_state,
1422 setup_exe,
1423 current_version,
1424 };
1425
1426 int exit_code = 0;
1427 if (HandleNonInstallCmdLineOptions(modify_params, cmd_line, &exit_code)) {
1428 return exit_code;
1429 }
1430
1431 if (system_install && !IsUserAnAdmin()) {
1432 if (!cmd_line.HasSwitch(installer::switches::kRunAsAdmin)) {
1433 base::CommandLine new_cmd(base::CommandLine::NO_PROGRAM);
1434 new_cmd.AppendArguments(cmd_line, true);
1435 // Append --run-as-admin flag to let the new instance of setup.exe know
1436 // that we already tried to launch ourselves as admin.
1437 new_cmd.AppendSwitch(installer::switches::kRunAsAdmin);
1438 // If system_install became true due to an environment variable, append
1439 // it to the command line here since env vars may not propagate past the
1440 // elevation.
1441 if (!new_cmd.HasSwitch(installer::switches::kSystemLevel))
1442 new_cmd.AppendSwitch(installer::switches::kSystemLevel);
1443
1444 DWORD exit_code = installer::UNKNOWN_STATUS;
1445 InstallUtil::ExecuteExeAsAdmin(new_cmd, &exit_code);
1446 return exit_code;
1447 } else {
1448 LOG(ERROR) << "Non admin user can not install system level Chrome.";
1449 installer_state.WriteInstallerResult(installer::INSUFFICIENT_RIGHTS,
1450 IDS_INSTALL_INSUFFICIENT_RIGHTS_BASE,
1451 nullptr);
1452 return installer::INSUFFICIENT_RIGHTS;
1453 }
1454 }
1455
1456 std::unique_ptr<installer::SetupSingleton> setup_singleton(
1457 installer::SetupSingleton::Acquire(cmd_line, prefs, &original_state,
1458 &installer_state));
1459 if (!setup_singleton) {
1460 installer_state.WriteInstallerResult(
1461 installer::SETUP_SINGLETON_ACQUISITION_FAILED,
1462 IDS_INSTALL_SINGLETON_ACQUISITION_FAILED_BASE, nullptr);
1463 return installer::SETUP_SINGLETON_ACQUISITION_FAILED;
1464 }
1465
1466 base::FilePath installer_directory;
1467 installer::InstallStatus install_status = installer::UNKNOWN_STATUS;
1468 // If --uninstall option is given, uninstall the identified product(s)
1469 if (is_uninstall) {
1470 install_status =
1471 UninstallProducts(original_state, installer_state, setup_exe, cmd_line);
1472 } else {
1473 // If --uninstall option is not specified, we assume it is install case.
1474 install_status = InstallProducts(original_state, setup_exe, cmd_line, prefs,
1475 &installer_state, &installer_directory);
1476 DoLegacyCleanups(installer_state, install_status);
1477
1478 // It may be time to kick off an experiment if this was a successful update
1479 // and Chrome was not in use (since the experiment only applies to inactive
1480 // installs).
1481 if (install_status == installer::NEW_VERSION_UPDATED &&
1482 installer::ShouldRunUserExperiment(installer_state)) {
1483 installer::BeginUserExperiment(
1484 installer_state, installer_directory.Append(setup_exe.BaseName()),
1485 !system_install);
1486 }
1487 }
1488
1489 UMA_HISTOGRAM_ENUMERATION("Setup.Install.Result", install_status,
1490 installer::MAX_INSTALL_STATUS);
1491
1492 // Dump peak memory usage.
1493 PROCESS_MEMORY_COUNTERS pmc;
1494 if (::GetProcessMemoryInfo(::GetCurrentProcess(), &pmc, sizeof(pmc))) {
1495 UMA_HISTOGRAM_MEMORY_KB("Setup.Install.PeakPagefileUsage",
1496 base::saturated_cast<base::HistogramBase::Sample>(
1497 pmc.PeakPagefileUsage / 1024));
1498 UMA_HISTOGRAM_MEMORY_KB("Setup.Install.PeakWorkingSetSize",
1499 base::saturated_cast<base::HistogramBase::Sample>(
1500 pmc.PeakWorkingSetSize / 1024));
1501 }
1502 auto process_metrics = base::ProcessMetrics::CreateCurrentProcessMetrics();
1503 auto disk_usage = process_metrics->GetCumulativeDiskUsageInBytes();
1504 base::UmaHistogramMemoryMB(
1505 "Setup.Install.CumulativeDiskUsage2",
1506 base::saturated_cast<int>(base::ClampAdd(disk_usage, 1024 * 1024 / 2) /
1507 (1024 * 1024)));
1508
1509 int return_code = 0;
1510 // MSI demands that custom actions always return 0 (ERROR_SUCCESS) or it will
1511 // rollback the action. If we're uninstalling we want to avoid this, so always
1512 // report success, squashing any more informative return codes.
1513 if (!(installer_state.is_msi() && is_uninstall)) {
1514 // Note that we allow the status installer::UNINSTALL_REQUIRES_REBOOT
1515 // to pass through, since this is only returned on uninstall which is
1516 // never invoked directly by Google Update.
1517 return_code = InstallUtil::GetInstallReturnCode(install_status);
1518 }
1519
1520 VLOG(1) << "Installation complete, returning: " << return_code;
1521
1522 return return_code;
1523 }
1524