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