Index: chrome/installer/setup/setup_util.cc |
diff --git a/chrome/installer/setup/setup_util.cc b/chrome/installer/setup/setup_util.cc |
index 0565488cc33bd1ba9913fbf74dc578d8ea2dec38..af474401c9709d9dbd29faca40c870ee59937a4a 100644 |
--- a/chrome/installer/setup/setup_util.cc |
+++ b/chrome/installer/setup/setup_util.cc |
@@ -8,12 +8,17 @@ |
#include <windows.h> |
+#include <algorithm> |
+#include <iterator> |
+#include <set> |
+ |
#include "base/command_line.h" |
#include "base/cpu.h" |
#include "base/files/file_enumerator.h" |
#include "base/files/file_path.h" |
#include "base/files/file_util.h" |
#include "base/logging.h" |
+#include "base/numerics/safe_conversions.h" |
#include "base/process/kill.h" |
#include "base/process/launch.h" |
#include "base/process/process_handle.h" |
@@ -478,6 +483,152 @@ base::string16 GetRegistrationDataCommandKey( |
return cmd_key; |
} |
+void DeleteRegistryKeyPartial( |
+ HKEY root, |
+ const base::string16& path, |
+ const std::vector<base::string16>& keys_to_preserve) { |
+ // Downcase the list of keys to preserve (all must be ASCII strings). |
+ std::set<base::string16> lowered_keys_to_preserve; |
+ std::transform( |
+ keys_to_preserve.begin(), keys_to_preserve.end(), |
+ std::inserter(lowered_keys_to_preserve, lowered_keys_to_preserve.begin()), |
+ [](const base::string16& str) { |
+ DCHECK(!str.empty()); |
+ DCHECK(base::IsStringASCII(str)); |
+ return base::ToLowerASCII(str); |
+ }); |
+ base::win::RegKey key; |
+ LONG result = key.Open(root, path.c_str(), (KEY_ENUMERATE_SUB_KEYS | |
+ KEY_QUERY_VALUE | KEY_SET_VALUE)); |
+ if (result != ERROR_SUCCESS) { |
+ LOG_IF(ERROR, result != ERROR_FILE_NOT_FOUND) << "Failed to open " << path |
+ << "; result = " << result; |
+ return; |
+ } |
+ |
+ // Repeatedly iterate over all subkeys deleting those that should not be |
+ // preserved until only those remain. Multiple passes are needed since |
+ // deleting one key may change the enumeration order of all remaining keys. |
+ |
+ // Subkeys or values to be skipped on subsequent passes. |
+ std::set<base::string16> to_skip; |
+ DWORD index = 0; |
+ const size_t kMaxKeyNameLength = 256; // MSDN says 255; +1 for terminator. |
+ base::string16 name(kMaxKeyNameLength, base::char16()); |
+ bool did_delete = false; // True if at least one item was deleted. |
+ while (true) { |
+ DWORD name_length = base::saturated_cast<DWORD>(name.capacity()); |
+ name.resize(name_length); |
+ result = ::RegEnumKeyEx(key.Handle(), index, &name[0], &name_length, |
+ nullptr, nullptr, nullptr, nullptr); |
robertshield
2015/09/03 20:23:33
This may work, but the thing that gives me pause i
grt (UTC plus 2)
2015/09/04 02:22:47
That sentence is saying "hey, the enumeration orde
|
+ if (result == ERROR_MORE_DATA) { |
+ // Unexpected, but perhaps the max key name length was raised. MSDN |
+ // doesn't clearly say that name_length will contain the necessary |
+ // length in this case, so double the buffer and try again. |
+ name.reserve(name.capacity() * 2); |
+ continue; |
+ } |
+ if (result == ERROR_NO_MORE_ITEMS) { |
+ if (!did_delete) |
+ break; // All subkeys were deleted. The job is done. |
+ // Otherwise, loop again. |
+ did_delete = false; |
+ index = 0; |
+ continue; |
+ } |
+ if (result != ERROR_SUCCESS) |
+ break; |
+ // Shrink the string to the actual length of the name. |
+ name.resize(name_length); |
+ |
+ // Skip over this key if it couldn't be deleted on a previous iteration. |
+ if (to_skip.count(name)) { |
+ ++index; |
+ continue; |
+ } |
+ |
+ // Skip over this key if it is one of the keys to preserve. |
+ if (base::IsStringASCII(name) && |
+ lowered_keys_to_preserve.count(base::ToLowerASCII(name))) { |
+ // Add the true name of the key to the list of keys to skip for subsequent |
+ // iterations. |
+ to_skip.insert(name); |
+ ++index; |
+ continue; |
+ } |
+ |
+ // Delete this key. |
+ result = key.DeleteKey(name.c_str()); |
+ if (result != ERROR_SUCCESS) { |
+ LOG(ERROR) << "Failed to delete subkey " << name << " under path " |
+ << path; |
+ // Skip over this key on subsequent iterations. |
+ to_skip.insert(name); |
robertshield
2015/09/03 20:23:33
Hrmm, needing to preserve a list of items to skip
|
+ ++index; |
+ continue; |
+ } |
+ did_delete = true; |
+ } |
+ |
+ // Delete the key if it no longer has any subkeys. |
+ if (to_skip.empty()) { |
robertshield
2015/09/03 20:23:34
doesn't DeleteEmptyKey already check whether the k
grt (UTC plus 2)
2015/09/04 02:22:48
Yes, though there's no need to call it if you know
|
+ result = key.DeleteEmptyKey(L""); |
+ LOG_IF(ERROR, result != ERROR_SUCCESS) << "Failed to delete empty key " |
+ << path << "; result: " << result; |
+ return; |
+ } |
+ |
+ // Delete all values since subkeys are being preserved. |
+ to_skip.clear(); |
+ did_delete = false; |
+ index = 0; |
+ while (true) { |
+ DWORD name_length = base::saturated_cast<int16_t>(name.capacity()); |
+ name.resize(name_length); |
+ result = ::RegEnumValue(key.Handle(), index, &name[0], &name_length, |
+ nullptr, nullptr, nullptr, nullptr); |
+ if (result == ERROR_MORE_DATA) { |
+ if (name_length < |
+ static_cast<DWORD>(std::numeric_limits<int16_t>::max())) { |
+ // Double the space to hold the value name and try again. |
+ name.reserve(name.capacity() * 2); |
+ continue; |
+ } |
+ // Otherwise, the max has been exceeded. Nothing more to be done. |
+ break; |
+ } |
+ if (result == ERROR_NO_MORE_ITEMS) { |
+ if (!did_delete) |
+ break; // All values were deleted. The job is done. |
+ // Otherwise, loop again. |
+ did_delete = false; |
+ index = 0; |
+ continue; |
+ } |
+ if (result != ERROR_SUCCESS) |
+ break; |
+ // Shrink the string to the actual length of the name. |
+ name.resize(name_length); |
+ |
+ // Skip over this value if it couldn't be deleted on a previous iteration. |
+ if (to_skip.count(name)) { |
+ ++index; |
+ continue; |
+ } |
+ |
+ // Delete this value. |
+ result = key.DeleteValue(name.c_str()); |
+ if (result != ERROR_SUCCESS) { |
+ LOG(ERROR) << "Failed to delete value " << name << " in key " << path; |
+ // Skip over this value on subsequent iterations. |
+ to_skip.insert(name); |
+ ++index; |
+ continue; |
+ } |
+ did_delete = true; |
+ } |
+} |
+ |
ScopedTokenPrivilege::ScopedTokenPrivilege(const wchar_t* privilege_name) |
: is_enabled_(false) { |
HANDLE temp_handle; |