install_util.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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// See the corresponding header file for description of the functions in this 6// file. 7 8#include "chrome/installer/util/install_util.h" 9 10#include <shellapi.h> 11#include <shlobj.h> 12#include <shlwapi.h> 13 14#include <algorithm> 15 16#include "base/command_line.h" 17#include "base/file_util.h" 18#include "base/logging.h" 19#include "base/memory/scoped_ptr.h" 20#include "base/path_service.h" 21#include "base/platform_file.h" 22#include "base/process/launch.h" 23#include "base/strings/string_util.h" 24#include "base/sys_info.h" 25#include "base/values.h" 26#include "base/version.h" 27#include "base/win/metro.h" 28#include "base/win/registry.h" 29#include "base/win/windows_version.h" 30#include "chrome/installer/util/browser_distribution.h" 31#include "chrome/installer/util/google_update_constants.h" 32#include "chrome/installer/util/helper.h" 33#include "chrome/installer/util/installation_state.h" 34#include "chrome/installer/util/l10n_string_util.h" 35#include "chrome/installer/util/util_constants.h" 36#include "chrome/installer/util/work_item_list.h" 37 38using base::win::RegKey; 39using installer::ProductState; 40 41namespace { 42 43const wchar_t kStageBinaryPatching[] = L"binary_patching"; 44const wchar_t kStageBuilding[] = L"building"; 45const wchar_t kStageConfiguringAutoLaunch[] = L"configuring_auto_launch"; 46const wchar_t kStageCopyingPreferencesFile[] = L"copying_prefs"; 47const wchar_t kStageCreatingShortcuts[] = L"creating_shortcuts"; 48const wchar_t kStageEnsemblePatching[] = L"ensemble_patching"; 49const wchar_t kStageExecuting[] = L"executing"; 50const wchar_t kStageFinishing[] = L"finishing"; 51const wchar_t kStagePreconditions[] = L"preconditions"; 52const wchar_t kStageRefreshingPolicy[] = L"refreshing_policy"; 53const wchar_t kStageRegisteringChrome[] = L"registering_chrome"; 54const wchar_t kStageRemovingOldVersions[] = L"removing_old_ver"; 55const wchar_t kStageRollingback[] = L"rollingback"; 56const wchar_t kStageUncompressing[] = L"uncompressing"; 57const wchar_t kStageUnpacking[] = L"unpacking"; 58const wchar_t kStageUpdatingChannels[] = L"updating_channels"; 59const wchar_t kStageCreatingVisualManifest[] = L"creating_visual_manifest"; 60const wchar_t kStageDeferringToHigherVersion[] = L"deferring_to_higher_version"; 61const wchar_t kStageUninstallingBinaries[] = L"uninstalling_binaries"; 62const wchar_t kStageUninstallingChromeFrame[] = L"uninstalling_chrome_frame"; 63 64const wchar_t* const kStages[] = { 65 NULL, 66 kStagePreconditions, 67 kStageUncompressing, 68 kStageEnsemblePatching, 69 kStageBinaryPatching, 70 kStageUnpacking, 71 kStageBuilding, 72 kStageExecuting, 73 kStageRollingback, 74 kStageRefreshingPolicy, 75 kStageUpdatingChannels, 76 kStageCopyingPreferencesFile, 77 kStageCreatingShortcuts, 78 kStageRegisteringChrome, 79 kStageRemovingOldVersions, 80 kStageFinishing, 81 kStageConfiguringAutoLaunch, 82 kStageCreatingVisualManifest, 83 kStageDeferringToHigherVersion, 84 kStageUninstallingBinaries, 85 kStageUninstallingChromeFrame, 86}; 87 88COMPILE_ASSERT(installer::NUM_STAGES == arraysize(kStages), 89 kStages_disagrees_with_Stage_comma_they_must_match_bang); 90 91// Creates a zero-sized non-decorated foreground window that doesn't appear 92// in the taskbar. This is used as a parent window for calls to ShellExecuteEx 93// in order for the UAC dialog to appear in the foreground and for focus 94// to be returned to this process once the UAC task is dismissed. Returns 95// NULL on failure, a handle to the UAC window on success. 96HWND CreateUACForegroundWindow() { 97 HWND foreground_window = ::CreateWindowEx(WS_EX_TOOLWINDOW, 98 L"STATIC", 99 NULL, 100 WS_POPUP | WS_VISIBLE, 101 0, 0, 0, 0, 102 NULL, NULL, 103 ::GetModuleHandle(NULL), 104 NULL); 105 if (foreground_window) { 106 HMONITOR monitor = ::MonitorFromWindow(foreground_window, 107 MONITOR_DEFAULTTONEAREST); 108 if (monitor) { 109 MONITORINFO mi = {0}; 110 mi.cbSize = sizeof(mi); 111 ::GetMonitorInfo(monitor, &mi); 112 RECT screen_rect = mi.rcWork; 113 int x_offset = (screen_rect.right - screen_rect.left) / 2; 114 int y_offset = (screen_rect.bottom - screen_rect.top) / 2; 115 ::MoveWindow(foreground_window, 116 screen_rect.left + x_offset, 117 screen_rect.top + y_offset, 118 0, 0, FALSE); 119 } else { 120 NOTREACHED() << "Unable to get default monitor"; 121 } 122 ::SetForegroundWindow(foreground_window); 123 } 124 return foreground_window; 125} 126 127} // namespace 128 129base::string16 InstallUtil::GetActiveSetupPath(BrowserDistribution* dist) { 130 static const wchar_t kInstalledComponentsPath[] = 131 L"Software\\Microsoft\\Active Setup\\Installed Components\\"; 132 return kInstalledComponentsPath + dist->GetActiveSetupGuid(); 133} 134 135void InstallUtil::TriggerActiveSetupCommand() { 136 base::string16 active_setup_reg( 137 GetActiveSetupPath(BrowserDistribution::GetDistribution())); 138 base::win::RegKey active_setup_key( 139 HKEY_LOCAL_MACHINE, active_setup_reg.c_str(), KEY_QUERY_VALUE); 140 base::string16 cmd_str; 141 LONG read_status = active_setup_key.ReadValue(L"StubPath", &cmd_str); 142 if (read_status != ERROR_SUCCESS) { 143 LOG(ERROR) << active_setup_reg << ", " << read_status; 144 // This should never fail if Chrome is registered at system-level, but if it 145 // does there is not much else to be done. 146 return; 147 } 148 149 CommandLine cmd(CommandLine::FromString(cmd_str)); 150 // Force creation of shortcuts as the First Run beacon might land between now 151 // and the time setup.exe checks for it. 152 cmd.AppendSwitch(installer::switches::kForceConfigureUserSettings); 153 154 base::LaunchOptions launch_options; 155 if (base::win::IsMetroProcess()) 156 launch_options.force_breakaway_from_job_ = true; 157 if (!base::LaunchProcess(cmd.GetCommandLineString(), launch_options, NULL)) 158 PLOG(ERROR) << cmd.GetCommandLineString(); 159} 160 161bool InstallUtil::ExecuteExeAsAdmin(const CommandLine& cmd, DWORD* exit_code) { 162 base::FilePath::StringType program(cmd.GetProgram().value()); 163 DCHECK(!program.empty()); 164 DCHECK_NE(program[0], L'\"'); 165 166 CommandLine::StringType params(cmd.GetCommandLineString()); 167 if (params[0] == '"') { 168 DCHECK_EQ('"', params[program.length() + 1]); 169 DCHECK_EQ(program, params.substr(1, program.length())); 170 params = params.substr(program.length() + 2); 171 } else { 172 DCHECK_EQ(program, params.substr(0, program.length())); 173 params = params.substr(program.length()); 174 } 175 176 TrimWhitespace(params, TRIM_ALL, ¶ms); 177 178 HWND uac_foreground_window = CreateUACForegroundWindow(); 179 180 SHELLEXECUTEINFO info = {0}; 181 info.cbSize = sizeof(SHELLEXECUTEINFO); 182 info.fMask = SEE_MASK_NOCLOSEPROCESS; 183 info.hwnd = uac_foreground_window; 184 info.lpVerb = L"runas"; 185 info.lpFile = program.c_str(); 186 info.lpParameters = params.c_str(); 187 info.nShow = SW_SHOW; 188 189 bool success = false; 190 if (::ShellExecuteEx(&info) == TRUE) { 191 ::WaitForSingleObject(info.hProcess, INFINITE); 192 DWORD ret_val = 0; 193 if (::GetExitCodeProcess(info.hProcess, &ret_val)) { 194 success = true; 195 if (exit_code) 196 *exit_code = ret_val; 197 } 198 } 199 200 if (uac_foreground_window) { 201 DestroyWindow(uac_foreground_window); 202 } 203 204 return success; 205} 206 207CommandLine InstallUtil::GetChromeUninstallCmd( 208 bool system_install, BrowserDistribution::Type distribution_type) { 209 ProductState state; 210 if (state.Initialize(system_install, distribution_type)) { 211 return state.uninstall_command(); 212 } 213 return CommandLine(CommandLine::NO_PROGRAM); 214} 215 216void InstallUtil::GetChromeVersion(BrowserDistribution* dist, 217 bool system_install, 218 Version* version) { 219 DCHECK(dist); 220 RegKey key; 221 HKEY reg_root = (system_install) ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; 222 LONG result = key.Open(reg_root, dist->GetVersionKey().c_str(), 223 KEY_QUERY_VALUE); 224 225 base::string16 version_str; 226 if (result == ERROR_SUCCESS) 227 result = key.ReadValue(google_update::kRegVersionField, &version_str); 228 229 *version = Version(); 230 if (result == ERROR_SUCCESS && !version_str.empty()) { 231 VLOG(1) << "Existing " << dist->GetDisplayName() << " version found " 232 << version_str; 233 *version = Version(WideToASCII(version_str)); 234 } else { 235 DCHECK_EQ(ERROR_FILE_NOT_FOUND, result); 236 VLOG(1) << "No existing " << dist->GetDisplayName() 237 << " install found."; 238 } 239} 240 241void InstallUtil::GetCriticalUpdateVersion(BrowserDistribution* dist, 242 bool system_install, 243 Version* version) { 244 DCHECK(dist); 245 RegKey key; 246 HKEY reg_root = (system_install) ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; 247 LONG result = 248 key.Open(reg_root, dist->GetVersionKey().c_str(), KEY_QUERY_VALUE); 249 250 base::string16 version_str; 251 if (result == ERROR_SUCCESS) 252 result = key.ReadValue(google_update::kRegCriticalVersionField, 253 &version_str); 254 255 *version = Version(); 256 if (result == ERROR_SUCCESS && !version_str.empty()) { 257 VLOG(1) << "Critical Update version for " << dist->GetDisplayName() 258 << " found " << version_str; 259 *version = Version(WideToASCII(version_str)); 260 } else { 261 DCHECK_EQ(ERROR_FILE_NOT_FOUND, result); 262 VLOG(1) << "No existing " << dist->GetDisplayName() 263 << " install found."; 264 } 265} 266 267bool InstallUtil::IsOSSupported() { 268 // We do not support Win2K or older, or XP without service pack 2. 269 VLOG(1) << base::SysInfo::OperatingSystemName() << ' ' 270 << base::SysInfo::OperatingSystemVersion(); 271 base::win::Version version = base::win::GetVersion(); 272 return (version > base::win::VERSION_XP) || 273 ((version == base::win::VERSION_XP) && 274 (base::win::OSInfo::GetInstance()->service_pack().major >= 2)); 275} 276 277void InstallUtil::AddInstallerResultItems( 278 bool system_install, 279 const base::string16& state_key, 280 installer::InstallStatus status, 281 int string_resource_id, 282 const base::string16* const launch_cmd, 283 WorkItemList* install_list) { 284 DCHECK(install_list); 285 const HKEY root = system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; 286 DWORD installer_result = (GetInstallReturnCode(status) == 0) ? 0 : 1; 287 install_list->AddCreateRegKeyWorkItem(root, state_key); 288 install_list->AddSetRegValueWorkItem(root, state_key, 289 installer::kInstallerResult, 290 installer_result, true); 291 install_list->AddSetRegValueWorkItem(root, state_key, 292 installer::kInstallerError, 293 static_cast<DWORD>(status), true); 294 if (string_resource_id != 0) { 295 base::string16 msg = installer::GetLocalizedString(string_resource_id); 296 install_list->AddSetRegValueWorkItem(root, state_key, 297 installer::kInstallerResultUIString, msg, true); 298 } 299 if (launch_cmd != NULL && !launch_cmd->empty()) { 300 install_list->AddSetRegValueWorkItem(root, state_key, 301 installer::kInstallerSuccessLaunchCmdLine, *launch_cmd, true); 302 } 303} 304 305void InstallUtil::UpdateInstallerStage(bool system_install, 306 const base::string16& state_key_path, 307 installer::InstallerStage stage) { 308 DCHECK_LE(static_cast<installer::InstallerStage>(0), stage); 309 DCHECK_GT(installer::NUM_STAGES, stage); 310 const HKEY root = system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; 311 RegKey state_key; 312 LONG result = state_key.Open(root, state_key_path.c_str(), 313 KEY_QUERY_VALUE | KEY_SET_VALUE); 314 if (result == ERROR_SUCCESS) { 315 if (stage == installer::NO_STAGE) { 316 result = state_key.DeleteValue(installer::kInstallerExtraCode1); 317 LOG_IF(ERROR, result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) 318 << "Failed deleting installer stage from " << state_key_path 319 << "; result: " << result; 320 } else { 321 const DWORD extra_code_1 = static_cast<DWORD>(stage); 322 result = state_key.WriteValue(installer::kInstallerExtraCode1, 323 extra_code_1); 324 LOG_IF(ERROR, result != ERROR_SUCCESS) 325 << "Failed writing installer stage to " << state_key_path 326 << "; result: " << result; 327 } 328 // TODO(grt): Remove code below here once we're convinced that our use of 329 // Google Update's new InstallerExtraCode1 value is good. 330 installer::ChannelInfo channel_info; 331 // This will return false if the "ap" value isn't present, which is fine. 332 channel_info.Initialize(state_key); 333 if (channel_info.SetStage(kStages[stage]) && 334 !channel_info.Write(&state_key)) { 335 LOG(ERROR) << "Failed writing installer stage to " << state_key_path; 336 } 337 } else { 338 LOG(ERROR) << "Failed opening " << state_key_path 339 << " to update installer stage; result: " << result; 340 } 341} 342 343bool InstallUtil::IsPerUserInstall(const wchar_t* const exe_path) { 344 wchar_t program_files_path[MAX_PATH] = {0}; 345 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PROGRAM_FILES, NULL, 346 SHGFP_TYPE_CURRENT, program_files_path))) { 347 return !StartsWith(exe_path, program_files_path, false); 348 } else { 349 NOTREACHED(); 350 } 351 return true; 352} 353 354bool InstallUtil::IsMultiInstall(BrowserDistribution* dist, 355 bool system_install) { 356 DCHECK(dist); 357 ProductState state; 358 return state.Initialize(system_install, dist->GetType()) && 359 state.is_multi_install(); 360} 361 362bool CheckIsChromeSxSProcess() { 363 CommandLine* command_line = CommandLine::ForCurrentProcess(); 364 CHECK(command_line); 365 366 if (command_line->HasSwitch(installer::switches::kChromeSxS)) 367 return true; 368 369 // Also return true if we are running from Chrome SxS installed path. 370 base::FilePath exe_dir; 371 PathService::Get(base::DIR_EXE, &exe_dir); 372 base::string16 chrome_sxs_dir(installer::kGoogleChromeInstallSubDir2); 373 chrome_sxs_dir.append(installer::kSxSSuffix); 374 375 // This is SxS if current EXE is in or under (possibly multiple levels under) 376 // |chrome_sxs_dir|\|installer::kInstallBinaryDir| 377 std::vector<base::FilePath::StringType> components; 378 exe_dir.GetComponents(&components); 379 // We need at least 1 element in the array for the behavior of the following 380 // loop to be defined. This should always be true, since we're splitting the 381 // path to our executable and one of the components will be the drive letter. 382 DCHECK(!components.empty()); 383 typedef std::vector<base::FilePath::StringType>::const_reverse_iterator 384 ComponentsIterator; 385 for (ComponentsIterator current = components.rbegin(), parent = current + 1; 386 parent != components.rend(); current = parent++) { 387 if (base::FilePath::CompareEqualIgnoreCase( 388 *current, installer::kInstallBinaryDir) && 389 base::FilePath::CompareEqualIgnoreCase(*parent, chrome_sxs_dir)) { 390 return true; 391 } 392 } 393 394 return false; 395} 396 397bool InstallUtil::IsChromeSxSProcess() { 398 static bool sxs = CheckIsChromeSxSProcess(); 399 return sxs; 400} 401 402bool InstallUtil::GetSentinelFilePath(const base::FilePath::CharType* file, 403 BrowserDistribution* dist, 404 base::FilePath* path) { 405 base::FilePath exe_path; 406 if (!PathService::Get(base::DIR_EXE, &exe_path)) 407 return false; 408 409 if (IsPerUserInstall(exe_path.value().c_str())) { 410 const base::FilePath maybe_product_dir(exe_path.DirName().DirName()); 411 if (base::PathExists(exe_path.Append(installer::kChromeExe))) { 412 // DIR_EXE is most likely Chrome's directory in which case |exe_path| is 413 // the user-level sentinel path. 414 *path = exe_path; 415 } else if (base::PathExists( 416 maybe_product_dir.Append(installer::kChromeExe))) { 417 // DIR_EXE can also be the Installer directory if this is called from a 418 // setup.exe running from Application\<version>\Installer (see 419 // InstallerState::GetInstallerDirectory) in which case Chrome's directory 420 // is two levels up. 421 *path = maybe_product_dir; 422 } else { 423 NOTREACHED(); 424 return false; 425 } 426 } else { 427 std::vector<base::FilePath> user_data_dir_paths; 428 installer::GetChromeUserDataPaths(dist, &user_data_dir_paths); 429 430 if (!user_data_dir_paths.empty()) 431 *path = user_data_dir_paths[0]; 432 else 433 return false; 434 } 435 436 *path = path->Append(file); 437 return true; 438} 439 440// This method tries to delete a registry key and logs an error message 441// in case of failure. It returns true if deletion is successful (or the key did 442// not exist), otherwise false. 443bool InstallUtil::DeleteRegistryKey(HKEY root_key, 444 const base::string16& key_path) { 445 VLOG(1) << "Deleting registry key " << key_path; 446 LONG result = ::SHDeleteKey(root_key, key_path.c_str()); 447 if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) { 448 LOG(ERROR) << "Failed to delete registry key: " << key_path 449 << " error: " << result; 450 return false; 451 } 452 return true; 453} 454 455// This method tries to delete a registry value and logs an error message 456// in case of failure. It returns true if deletion is successful (or the key did 457// not exist), otherwise false. 458bool InstallUtil::DeleteRegistryValue(HKEY reg_root, 459 const base::string16& key_path, 460 const base::string16& value_name) { 461 RegKey key; 462 LONG result = key.Open(reg_root, key_path.c_str(), KEY_SET_VALUE); 463 if (result == ERROR_SUCCESS) 464 result = key.DeleteValue(value_name.c_str()); 465 if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) { 466 LOG(ERROR) << "Failed to delete registry value: " << value_name 467 << " error: " << result; 468 return false; 469 } 470 return true; 471} 472 473// static 474InstallUtil::ConditionalDeleteResult InstallUtil::DeleteRegistryKeyIf( 475 HKEY root_key, 476 const base::string16& key_to_delete_path, 477 const base::string16& key_to_test_path, 478 const wchar_t* value_name, 479 const RegistryValuePredicate& predicate) { 480 DCHECK(root_key); 481 ConditionalDeleteResult delete_result = NOT_FOUND; 482 RegKey key; 483 base::string16 actual_value; 484 if (key.Open(root_key, key_to_test_path.c_str(), 485 KEY_QUERY_VALUE) == ERROR_SUCCESS && 486 key.ReadValue(value_name, &actual_value) == ERROR_SUCCESS && 487 predicate.Evaluate(actual_value)) { 488 key.Close(); 489 delete_result = DeleteRegistryKey(root_key, key_to_delete_path) 490 ? DELETED : DELETE_FAILED; 491 } 492 return delete_result; 493} 494 495// static 496InstallUtil::ConditionalDeleteResult InstallUtil::DeleteRegistryValueIf( 497 HKEY root_key, 498 const wchar_t* key_path, 499 const wchar_t* value_name, 500 const RegistryValuePredicate& predicate) { 501 DCHECK(root_key); 502 DCHECK(key_path); 503 ConditionalDeleteResult delete_result = NOT_FOUND; 504 RegKey key; 505 base::string16 actual_value; 506 if (key.Open(root_key, key_path, 507 KEY_QUERY_VALUE | KEY_SET_VALUE) == ERROR_SUCCESS && 508 key.ReadValue(value_name, &actual_value) == ERROR_SUCCESS && 509 predicate.Evaluate(actual_value)) { 510 LONG result = key.DeleteValue(value_name); 511 if (result != ERROR_SUCCESS) { 512 LOG(ERROR) << "Failed to delete registry value: " 513 << (value_name ? value_name : L"(Default)") 514 << " error: " << result; 515 delete_result = DELETE_FAILED; 516 } 517 delete_result = DELETED; 518 } 519 return delete_result; 520} 521 522bool InstallUtil::ValueEquals::Evaluate(const base::string16& value) const { 523 return value == value_to_match_; 524} 525 526// static 527int InstallUtil::GetInstallReturnCode(installer::InstallStatus status) { 528 switch (status) { 529 case installer::FIRST_INSTALL_SUCCESS: 530 case installer::INSTALL_REPAIRED: 531 case installer::NEW_VERSION_UPDATED: 532 case installer::IN_USE_UPDATED: 533 case installer::UNUSED_BINARIES_UNINSTALLED: 534 return 0; 535 default: 536 return status; 537 } 538} 539 540// static 541void InstallUtil::MakeUninstallCommand(const base::string16& program, 542 const base::string16& arguments, 543 CommandLine* command_line) { 544 *command_line = CommandLine::FromString(L"\"" + program + L"\" " + arguments); 545} 546 547// static 548base::string16 InstallUtil::GetCurrentDate() { 549 static const wchar_t kDateFormat[] = L"yyyyMMdd"; 550 wchar_t date_str[arraysize(kDateFormat)] = {0}; 551 int len = GetDateFormatW(LOCALE_INVARIANT, 0, NULL, kDateFormat, 552 date_str, arraysize(date_str)); 553 if (len) { 554 --len; // Subtract terminating \0. 555 } else { 556 PLOG(DFATAL) << "GetDateFormat"; 557 } 558 559 return base::string16(date_str, len); 560} 561 562// Open |path| with minimal access to obtain information about it, returning 563// true and populating |handle| on success. 564// static 565bool InstallUtil::ProgramCompare::OpenForInfo(const base::FilePath& path, 566 base::win::ScopedHandle* handle) { 567 DCHECK(handle); 568 handle->Set(base::CreatePlatformFile(path, base::PLATFORM_FILE_OPEN, NULL, 569 NULL)); 570 return handle->IsValid(); 571} 572 573// Populate |info| for |handle|, returning true on success. 574// static 575bool InstallUtil::ProgramCompare::GetInfo(const base::win::ScopedHandle& handle, 576 BY_HANDLE_FILE_INFORMATION* info) { 577 DCHECK(handle.IsValid()); 578 return GetFileInformationByHandle( 579 const_cast<base::win::ScopedHandle&>(handle), info) != 0; 580} 581 582InstallUtil::ProgramCompare::ProgramCompare(const base::FilePath& path_to_match) 583 : path_to_match_(path_to_match), 584 file_handle_(base::kInvalidPlatformFileValue), 585 file_info_() { 586 DCHECK(!path_to_match_.empty()); 587 if (!OpenForInfo(path_to_match_, &file_handle_)) { 588 PLOG(WARNING) << "Failed opening " << path_to_match_.value() 589 << "; falling back to path string comparisons."; 590 } else if (!GetInfo(file_handle_, &file_info_)) { 591 PLOG(WARNING) << "Failed getting information for " 592 << path_to_match_.value() 593 << "; falling back to path string comparisons."; 594 file_handle_.Close(); 595 } 596} 597 598InstallUtil::ProgramCompare::~ProgramCompare() { 599} 600 601bool InstallUtil::ProgramCompare::Evaluate(const base::string16& value) const { 602 // Suss out the exe portion of the value, which is expected to be a command 603 // line kinda (or exactly) like: 604 // "c:\foo\bar\chrome.exe" -- "%1" 605 base::FilePath program(CommandLine::FromString(value).GetProgram()); 606 if (program.empty()) { 607 LOG(WARNING) << "Failed to parse an executable name from command line: \"" 608 << value << "\""; 609 return false; 610 } 611 612 return EvaluatePath(program); 613} 614 615bool InstallUtil::ProgramCompare::EvaluatePath( 616 const base::FilePath& path) const { 617 // Try the simple thing first: do the paths happen to match? 618 if (base::FilePath::CompareEqualIgnoreCase(path_to_match_.value(), 619 path.value())) 620 return true; 621 622 // If the paths don't match and we couldn't open the expected file, we've done 623 // our best. 624 if (!file_handle_.IsValid()) 625 return false; 626 627 // Open the program and see if it references the expected file. 628 base::win::ScopedHandle handle; 629 BY_HANDLE_FILE_INFORMATION info = {}; 630 631 return (OpenForInfo(path, &handle) && 632 GetInfo(handle, &info) && 633 info.dwVolumeSerialNumber == file_info_.dwVolumeSerialNumber && 634 info.nFileIndexHigh == file_info_.nFileIndexHigh && 635 info.nFileIndexLow == file_info_.nFileIndexLow); 636} 637