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