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