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