| // Copyright 2022 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/installer/util/registry_util.h" |
| |
| #include <windows.h> |
| |
| #include <string> |
| |
| #include "base/command_line.h" |
| #include "base/logging.h" |
| #include "base/win/registry.h" |
| #include "base/win/windows_types.h" |
| |
| using base::win::RegKey; |
| |
| namespace installer { |
| |
| bool DeleteRegistryKey(HKEY root_key, |
| const std::wstring& key_path, |
| REGSAM wow64_access) { |
| VLOG(1) << "Deleting registry key " << key_path; |
| RegKey target_key; |
| LONG result = |
| target_key.Open(root_key, key_path.c_str(), DELETE | wow64_access); |
| |
| if (result == ERROR_FILE_NOT_FOUND) |
| return true; |
| |
| if (result == ERROR_SUCCESS) |
| result = target_key.DeleteKey(L""); |
| |
| if (result != ERROR_SUCCESS) { |
| LOG(ERROR) << "Failed to delete registry key: " << key_path |
| << " error: " << result; |
| return false; |
| } |
| return true; |
| } |
| |
| bool DeleteRegistryValue(HKEY reg_root, |
| const std::wstring& key_path, |
| REGSAM wow64_access, |
| const std::wstring& value_name) { |
| RegKey key; |
| LONG result = |
| key.Open(reg_root, key_path.c_str(), KEY_SET_VALUE | wow64_access); |
| if (result == ERROR_SUCCESS) |
| result = key.DeleteValue(value_name.c_str()); |
| if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) { |
| LOG(ERROR) << "Failed to delete registry value: " << value_name |
| << " error: " << result; |
| return false; |
| } |
| return true; |
| } |
| |
| ConditionalDeleteResult DeleteRegistryKeyIf( |
| HKEY root_key, |
| const std::wstring& key_to_delete_path, |
| const std::wstring& key_to_test_path, |
| const REGSAM wow64_access, |
| const wchar_t* value_name, |
| const RegistryValuePredicate& predicate) { |
| DCHECK(root_key); |
| ConditionalDeleteResult delete_result = ConditionalDeleteResult::NOT_FOUND; |
| RegKey key; |
| std::wstring actual_value; |
| if (key.Open(root_key, key_to_test_path.c_str(), |
| KEY_QUERY_VALUE | wow64_access) == ERROR_SUCCESS && |
| key.ReadValue(value_name, &actual_value) == ERROR_SUCCESS && |
| predicate.Evaluate(actual_value)) { |
| key.Close(); |
| delete_result = |
| installer::DeleteRegistryKey(root_key, key_to_delete_path, wow64_access) |
| ? ConditionalDeleteResult::DELETED |
| : ConditionalDeleteResult::DELETE_FAILED; |
| } |
| return delete_result; |
| } |
| |
| // static |
| ConditionalDeleteResult DeleteRegistryValueIf( |
| HKEY root_key, |
| const wchar_t* key_path, |
| REGSAM wow64_access, |
| const wchar_t* value_name, |
| const RegistryValuePredicate& predicate) { |
| DCHECK(root_key); |
| DCHECK(key_path); |
| ConditionalDeleteResult delete_result = ConditionalDeleteResult::NOT_FOUND; |
| RegKey key; |
| std::wstring actual_value; |
| if (key.Open(root_key, key_path, |
| KEY_QUERY_VALUE | KEY_SET_VALUE | wow64_access) == |
| ERROR_SUCCESS && |
| key.ReadValue(value_name, &actual_value) == ERROR_SUCCESS && |
| predicate.Evaluate(actual_value)) { |
| LONG result = key.DeleteValue(value_name); |
| if (result != ERROR_SUCCESS) { |
| LOG(ERROR) << "Failed to delete registry value: " |
| << (value_name ? value_name : L"(Default)") |
| << " error: " << result; |
| delete_result = ConditionalDeleteResult::DELETE_FAILED; |
| } else { |
| delete_result = ConditionalDeleteResult::DELETED; |
| } |
| } |
| return delete_result; |
| } |
| |
| bool ValueEquals::Evaluate(const std::wstring& value) const { |
| return value == value_to_match_; |
| } |
| |
| // Open |path| with minimal access to obtain information about it, returning |
| // true and populating |file| on success. |
| // static |
| bool ProgramCompare::OpenForInfo(const base::FilePath& path, base::File* file) { |
| DCHECK(file); |
| file->Initialize(path, |
| base::File::FLAG_OPEN | base::File::FLAG_WIN_SHARE_DELETE); |
| return file->IsValid(); |
| } |
| |
| // Populate |info| for |file|, returning true on success. |
| // static |
| bool ProgramCompare::GetInfo(const base::File& file, |
| BY_HANDLE_FILE_INFORMATION* info) { |
| DCHECK(file.IsValid()); |
| return GetFileInformationByHandle(file.GetPlatformFile(), info) != 0; |
| } |
| |
| ProgramCompare::ProgramCompare(const base::FilePath& path_to_match) |
| : path_to_match_(path_to_match), file_info_() { |
| DCHECK(!path_to_match_.empty()); |
| if (!OpenForInfo(path_to_match_, &file_)) { |
| PLOG(WARNING) << "Failed opening " << path_to_match_.value() |
| << "; falling back to path string comparisons."; |
| } else if (!GetInfo(file_, &file_info_)) { |
| PLOG(WARNING) << "Failed getting information for " << path_to_match_.value() |
| << "; falling back to path string comparisons."; |
| file_.Close(); |
| } |
| } |
| |
| ProgramCompare::~ProgramCompare() = default; |
| |
| bool ProgramCompare::Evaluate(const std::wstring& value) const { |
| // Suss out the exe portion of the value, which is expected to be a command |
| // line kinda (or exactly) like: |
| // "c:\foo\bar\chrome.exe" -- "%1" |
| base::FilePath program(base::CommandLine::FromString(value).GetProgram()); |
| if (program.empty()) { |
| LOG(WARNING) << "Failed to parse an executable name from command line: \"" |
| << value << "\""; |
| return false; |
| } |
| |
| return EvaluatePath(program); |
| } |
| |
| bool ProgramCompare::EvaluatePath(const base::FilePath& path) const { |
| // Try the simple thing first: do the paths happen to match? |
| if (base::FilePath::CompareEqualIgnoreCase(path_to_match_.value(), |
| path.value())) |
| return true; |
| |
| // If the paths don't match and we couldn't open the expected file, we've done |
| // our best. |
| if (!file_.IsValid()) |
| return false; |
| |
| // Open the program and see if it references the expected file. |
| base::File file; |
| BY_HANDLE_FILE_INFORMATION info = {}; |
| |
| return (OpenForInfo(path, &file) && GetInfo(file, &info) && |
| info.dwVolumeSerialNumber == file_info_.dwVolumeSerialNumber && |
| info.nFileIndexHigh == file_info_.nFileIndexHigh && |
| info.nFileIndexLow == file_info_.nFileIndexLow); |
| } |
| |
| } // namespace installer |