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