| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/first_run/upgrade_util.h" |
| |
| #include <windows.h> |
| #include <objbase.h> |
| #include <psapi.h> |
| #include <shellapi.h> |
| #include <wrl/client.h> |
| |
| #include <algorithm> |
| #include <ios> |
| #include <string> |
| |
| #include "base/base_paths.h" |
| #include "base/command_line.h" |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/logging.h" |
| #include "base/path_service.h" |
| #include "base/process/launch.h" |
| #include "base/process/process_handle.h" |
| #include "base/stl_util.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_util.h" |
| #include "base/win/registry.h" |
| #include "base/win/windows_version.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/first_run/upgrade_util_win.h" |
| #include "chrome/browser/shell_integration.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/install_static/install_util.h" |
| #include "chrome/installer/util/google_update_constants.h" |
| #include "chrome/installer/util/util_constants.h" |
| #include "components/prefs/pref_service.h" |
| #include "ui/base/ui_base_switches.h" |
| |
| #if defined(GOOGLE_CHROME_BUILD) |
| #include "google_update/google_update_idl.h" |
| #endif |
| |
| namespace { |
| |
| bool GetNewerChromeFile(base::FilePath* path) { |
| if (!base::PathService::Get(base::DIR_EXE, path)) |
| return false; |
| *path = path->Append(installer::kChromeNewExe); |
| return true; |
| } |
| |
| bool InvokeGoogleUpdateForRename() { |
| #if defined(GOOGLE_CHROME_BUILD) |
| Microsoft::WRL::ComPtr<IProcessLauncher> ipl; |
| HRESULT hr = ::CoCreateInstance(__uuidof(ProcessLauncherClass), nullptr, |
| CLSCTX_ALL, IID_PPV_ARGS(&ipl)); |
| if (FAILED(hr)) { |
| LOG(ERROR) << "CoCreate ProcessLauncherClass failed; hr = " << std::hex |
| << hr; |
| return false; |
| } |
| |
| ULONG_PTR process_handle; |
| hr = ipl->LaunchCmdElevated(install_static::GetAppGuid(), |
| google_update::kRegRenameCmdField, |
| ::GetCurrentProcessId(), &process_handle); |
| if (FAILED(hr)) { |
| LOG(ERROR) << "IProcessLauncher::LaunchCmdElevated failed; hr = " |
| << std::hex << hr; |
| return false; |
| } |
| |
| base::Process rename_process( |
| reinterpret_cast<base::ProcessHandle>(process_handle)); |
| int exit_code; |
| if (!rename_process.WaitForExit(&exit_code)) { |
| PLOG(ERROR) << "WaitForExit of rename process failed"; |
| return false; |
| } |
| |
| if (exit_code != installer::RENAME_SUCCESSFUL) { |
| LOG(ERROR) << "Rename process failed with exit code " << exit_code; |
| return false; |
| } |
| |
| return true; |
| #else // GOOGLE_CHROME_BUILD |
| return false; |
| #endif // GOOGLE_CHROME_BUILD |
| } |
| |
| } // namespace |
| |
| namespace upgrade_util { |
| |
| bool RelaunchChromeBrowser(const base::CommandLine& command_line) { |
| base::FilePath chrome_exe; |
| if (!base::PathService::Get(base::FILE_EXE, &chrome_exe)) { |
| NOTREACHED(); |
| return false; |
| } |
| |
| // Explicitly make sure to relaunch chrome.exe rather than old_chrome.exe. |
| // This can happen when old_chrome.exe is launched by a user. |
| base::CommandLine chrome_exe_command_line = command_line; |
| chrome_exe_command_line.SetProgram( |
| chrome_exe.DirName().Append(installer::kChromeExe)); |
| |
| // Set the working directory to the exe's directory. This avoids a handle to |
| // the version directory being kept open in the relaunched child process. |
| base::LaunchOptions launch_options; |
| launch_options.current_directory = chrome_exe.DirName(); |
| // Give the new process the right to bring its windows to the foreground. |
| launch_options.grant_foreground_privilege = true; |
| return base::LaunchProcess(chrome_exe_command_line, launch_options).IsValid(); |
| } |
| |
| bool IsUpdatePendingRestart() { |
| base::FilePath new_chrome_exe; |
| if (!GetNewerChromeFile(&new_chrome_exe)) |
| return false; |
| return base::PathExists(new_chrome_exe); |
| } |
| |
| bool SwapNewChromeExeIfPresent() { |
| if (!IsUpdatePendingRestart()) |
| return false; |
| |
| // If this is a system-level install, ask Google Update to launch an elevated |
| // process to rename Chrome executables. |
| if (install_static::IsSystemInstall()) |
| return InvokeGoogleUpdateForRename(); |
| |
| // If this is a user-level install, directly launch a process to rename Chrome |
| // executables. Obtain the command to launch the process from the registry. |
| base::win::RegKey key; |
| auto result = |
| key.Open(HKEY_CURRENT_USER, install_static::GetClientsKeyPath().c_str(), |
| KEY_QUERY_VALUE | KEY_WOW64_32KEY); |
| if (result != ERROR_SUCCESS) { |
| ::SetLastError(result); |
| PLOG(ERROR) << "Open Clients key failed"; |
| return false; |
| } |
| |
| std::wstring rename_cmd; |
| result = key.ReadValue(google_update::kRegRenameCmdField, &rename_cmd); |
| if (result != ERROR_SUCCESS) { |
| ::SetLastError(result); |
| PLOG(ERROR) << "Read rename command failed"; |
| return false; |
| } |
| |
| base::LaunchOptions options; |
| options.wait = true; |
| options.start_hidden = true; |
| ::SetLastError(ERROR_SUCCESS); |
| base::Process process = base::LaunchProcess(rename_cmd, options); |
| if (!process.IsValid()) { |
| PLOG(ERROR) << "Launch rename process failed"; |
| return false; |
| } |
| |
| DWORD exit_code; |
| if (!::GetExitCodeProcess(process.Handle(), &exit_code)) { |
| PLOG(ERROR) << "GetExitCodeProcess of rename process failed"; |
| return false; |
| } |
| |
| if (exit_code != installer::RENAME_SUCCESSFUL) { |
| LOG(ERROR) << "Rename process failed with exit code " << exit_code; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool IsRunningOldChrome() { |
| // This figures out the actual file name that the section containing the |
| // mapped exe refers to. This is used instead of GetModuleFileName because the |
| // .exe may have been renamed out from under us while we've been running which |
| // GetModuleFileName won't notice. |
| wchar_t mapped_file_name[MAX_PATH * 2] = {}; |
| |
| if (!::GetMappedFileName(::GetCurrentProcess(), |
| reinterpret_cast<void*>(::GetModuleHandle(NULL)), |
| mapped_file_name, base::size(mapped_file_name))) { |
| return false; |
| } |
| |
| base::FilePath file_name(base::FilePath(mapped_file_name).BaseName()); |
| return base::FilePath::CompareEqualIgnoreCase(file_name.value(), |
| installer::kChromeOldExe); |
| } |
| |
| bool DoUpgradeTasks(const base::CommandLine& command_line) { |
| if (!SwapNewChromeExeIfPresent() && !IsRunningOldChrome()) |
| return false; |
| // At this point the chrome.exe has been swapped with the new one. |
| if (!RelaunchChromeBrowser(command_line)) { |
| // The re-launch fails. Feel free to panic now. |
| NOTREACHED(); |
| } |
| return true; |
| } |
| |
| } // namespace upgrade_util |