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 ? &quoted_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