• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
CreateUACForegroundWindow()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 
GetActiveSetupPath(BrowserDistribution * dist)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 
TriggerActiveSetupCommand()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 
ExecuteExeAsAdmin(const CommandLine & cmd,DWORD * exit_code)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, &params);
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 
GetChromeUninstallCmd(bool system_install,BrowserDistribution::Type distribution_type)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 
GetChromeVersion(BrowserDistribution * dist,bool system_install,Version * version)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 
GetCriticalUpdateVersion(BrowserDistribution * dist,bool system_install,Version * version)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 
IsOSSupported()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 
AddInstallerResultItems(bool system_install,const string16 & state_key,installer::InstallStatus status,int string_resource_id,const string16 * const launch_cmd,WorkItemList * install_list)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 
UpdateInstallerStage(bool system_install,const string16 & state_key_path,installer::InstallerStage stage)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 
IsPerUserInstall(const wchar_t * const exe_path)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 
IsMultiInstall(BrowserDistribution * dist,bool system_install)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 
CheckIsChromeSxSProcess()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 
IsChromeSxSProcess()394 bool InstallUtil::IsChromeSxSProcess() {
395   static bool sxs = CheckIsChromeSxSProcess();
396   return sxs;
397 }
398 
GetSentinelFilePath(const base::FilePath::CharType * file,BrowserDistribution * dist,base::FilePath * path)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.
DeleteRegistryKey(HKEY root_key,const string16 & key_path)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.
DeleteRegistryValue(HKEY reg_root,const string16 & key_path,const string16 & value_name)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
DeleteRegistryKeyIf(HKEY root_key,const string16 & key_to_delete_path,const string16 & key_to_test_path,const wchar_t * value_name,const RegistryValuePredicate & predicate)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
DeleteRegistryValueIf(HKEY root_key,const wchar_t * key_path,const wchar_t * value_name,const RegistryValuePredicate & predicate)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 
Evaluate(const string16 & value) const519 bool InstallUtil::ValueEquals::Evaluate(const string16& value) const {
520   return value == value_to_match_;
521 }
522 
523 // static
GetInstallReturnCode(installer::InstallStatus status)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
MakeUninstallCommand(const string16 & program,const string16 & arguments,CommandLine * command_line)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
GetCurrentDate()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
OpenForInfo(const base::FilePath & path,base::win::ScopedHandle * handle)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
GetInfo(const base::win::ScopedHandle & handle,BY_HANDLE_FILE_INFORMATION * info)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 
ProgramCompare(const base::FilePath & path_to_match)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 
~ProgramCompare()595 InstallUtil::ProgramCompare::~ProgramCompare() {
596 }
597 
Evaluate(const string16 & value) const598 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 
EvaluatePath(const base::FilePath & path) const612 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