Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/installer/util/delete_reg_key_work_item.h" | |
| 6 | |
| 5 #include <shlwapi.h> | 7 #include <shlwapi.h> |
| 6 | 8 #include <algorithm> |
| 7 #include "chrome/installer/util/delete_reg_key_work_item.h" | 9 #include <limits> |
| 10 #include <vector> | |
| 8 | 11 |
| 9 #include "base/logging.h" | 12 #include "base/logging.h" |
| 13 #include "base/rand_util.h" | |
| 14 #include "base/stringprintf.h" | |
| 15 #include "base/win/registry.h" | |
| 10 #include "chrome/installer/util/logging_installer.h" | 16 #include "chrome/installer/util/logging_installer.h" |
| 11 | 17 |
| 18 using base::win::RegKey; | |
| 19 | |
| 20 // A container for a registry key, its values, and its subkeys. We don't use | |
| 21 // more obvious methods for various reasons: | |
| 22 // - RegCopyTree isn't supported pre-Vista, so we'd have to do something | |
| 23 // different for XP anyway. | |
| 24 // - SHCopyKey can't copy subkeys into a volatile destination, so we'd have to | |
| 25 // worry about polluting the registry. Also, we've observed crashes in other | |
| 26 // shell lightweight API funcs, so perhaps it's best to avoid them. | |
|
robertshield
2011/03/01 16:04:43
I feel we have insufficient evidence for shlwapi p
grt (UTC plus 2)
2011/03/01 20:54:02
Done.
| |
| 27 // We don't persist security attributes since we only delete keys that we own, | |
| 28 // and we don't set custom attributes on them anyway. | |
| 29 class DeleteRegKeyWorkItem::RegKeyBackup { | |
| 30 public: | |
| 31 RegKeyBackup(); | |
| 32 bool Initialize(const RegKey& key); | |
| 33 bool WriteTo(RegKey* key) const; | |
| 34 | |
| 35 private: | |
| 36 // A container for a registry value. | |
| 37 class RegValueBackup { | |
| 38 public: | |
| 39 RegValueBackup(); | |
| 40 void Initialize(const wchar_t* name_buffer, DWORD name_size, | |
| 41 DWORD type, const uint8* data, DWORD data_size); | |
| 42 const std::wstring& name_str() const { return name_; } | |
| 43 const wchar_t* name() const { return name_.empty() ? NULL : name_.c_str(); } | |
| 44 DWORD type() const { return type_; } | |
| 45 const uint8* data() const { return data_.empty() ? NULL : &data_[0]; } | |
| 46 DWORD data_len() const { return static_cast<DWORD>(data_.size()); } | |
| 47 | |
| 48 private: | |
| 49 std::wstring name_; | |
| 50 std::vector<uint8> data_; | |
| 51 DWORD type_; | |
| 52 | |
| 53 DISALLOW_COPY_AND_ASSIGN(RegValueBackup); | |
| 54 }; | |
| 55 | |
| 56 scoped_array<RegValueBackup> values_; | |
| 57 scoped_array<std::wstring> subkey_names_; | |
| 58 scoped_array<RegKeyBackup> subkeys_; | |
| 59 ptrdiff_t num_values_; | |
| 60 ptrdiff_t num_subkeys_; | |
| 61 | |
| 62 DISALLOW_COPY_AND_ASSIGN(RegKeyBackup); | |
| 63 }; | |
| 64 | |
| 65 DeleteRegKeyWorkItem::RegKeyBackup::RegValueBackup::RegValueBackup() | |
| 66 : type_(REG_NONE) { | |
| 67 } | |
| 68 | |
| 69 void DeleteRegKeyWorkItem::RegKeyBackup::RegValueBackup::Initialize( | |
| 70 const wchar_t* name_buffer, | |
| 71 DWORD name_size, | |
| 72 DWORD type, const uint8* data, | |
| 73 DWORD data_size) { | |
| 74 name_.assign(name_buffer, name_size); | |
| 75 type_ = type; | |
| 76 data_.assign(data, data + data_size); | |
| 77 } | |
| 78 | |
| 79 DeleteRegKeyWorkItem::RegKeyBackup::RegKeyBackup() | |
| 80 : num_values_(0), | |
| 81 num_subkeys_(0) { | |
| 82 } | |
| 83 | |
| 84 // Initializes this object by reading the values and subkeys of |key|. | |
| 85 // Security descriptors are not backed up. | |
| 86 bool DeleteRegKeyWorkItem::RegKeyBackup::Initialize(const RegKey& key) { | |
| 87 DCHECK(key.Valid()); | |
| 88 | |
| 89 scoped_array<RegValueBackup> values; | |
| 90 scoped_array<std::wstring> subkey_names; | |
| 91 scoped_array<RegKeyBackup> subkeys; | |
| 92 | |
| 93 DWORD num_subkeys = 0; | |
| 94 DWORD max_subkey_name_len = 0; | |
| 95 DWORD num_values = 0; | |
| 96 DWORD max_value_name_len = 0; | |
| 97 DWORD max_value_len = 0; | |
| 98 LONG result = RegQueryInfoKey(key.Handle(), NULL, NULL, NULL, | |
| 99 &num_subkeys, &max_subkey_name_len, NULL, | |
| 100 &num_values, &max_value_name_len, | |
| 101 &max_value_len, NULL, NULL); | |
| 102 if (result != ERROR_SUCCESS) { | |
| 103 LOG(ERROR) << "Failed getting info of key to backup, result: " << result; | |
| 104 return false; | |
| 105 } | |
| 106 if (max_subkey_name_len >= std::numeric_limits<DWORD>::max() - 1 || | |
| 107 max_value_name_len >= std::numeric_limits<DWORD>::max() - 1) { | |
| 108 LOG(ERROR) | |
| 109 << "Failed backing up key; subkeys and/or names are out of range."; | |
| 110 return false; | |
| 111 } | |
| 112 DWORD max_name_len = std::max(max_subkey_name_len, max_value_name_len) + 1; | |
| 113 scoped_array<wchar_t> name_buffer(new wchar_t[max_name_len]); | |
| 114 | |
| 115 // Backup the values. | |
| 116 if (num_values != 0) { | |
| 117 values.reset(new RegValueBackup[num_values]); | |
| 118 scoped_array<uint8> value_buffer(new uint8[max_value_len]); | |
| 119 DWORD name_size = 0; | |
| 120 DWORD value_type = REG_NONE; | |
| 121 DWORD value_size = 0; | |
| 122 | |
| 123 for (DWORD i = 0; i < num_values; ++i) { | |
| 124 name_size = max_name_len; | |
| 125 value_size = max_value_len; | |
| 126 result = RegEnumValue(key.Handle(), i, name_buffer.get(), &name_size, | |
| 127 NULL, &value_type, value_buffer.get(), &value_size); | |
| 128 if (result != ERROR_SUCCESS) { | |
|
robertshield
2011/03/01 16:04:43
do you want to handle ERROR_MORE_DATA here (which
grt (UTC plus 2)
2011/03/01 20:54:02
Done. Also handles the case where values and/or k
| |
| 129 LOG(ERROR) << "Failed backing up value " << i << ", result: " << result; | |
| 130 return false; | |
| 131 } | |
| 132 values[i].Initialize(name_buffer.get(), name_size, value_type, | |
| 133 value_buffer.get(), value_size); | |
| 134 } | |
| 135 DLOG_IF(WARNING, RegEnumValue(key.Handle(), num_values, NULL, NULL, NULL, | |
| 136 &value_type, NULL, NULL) == ERROR_SUCCESS) | |
| 137 << "Concurrent modifications to registry key during backup operation."; | |
| 138 } | |
| 139 | |
| 140 // Backup the subkeys. | |
| 141 if (num_subkeys != 0) { | |
| 142 subkey_names.reset(new std::wstring[num_subkeys]); | |
| 143 subkeys.reset(new RegKeyBackup[num_subkeys]); | |
| 144 DWORD name_size = 0; | |
| 145 | |
| 146 // Get the names of them. | |
| 147 for (DWORD i = 0; i < num_subkeys; ++i) { | |
| 148 name_size = max_name_len; | |
| 149 result = RegEnumKeyEx(key.Handle(), i, name_buffer.get(), &name_size, | |
| 150 NULL, NULL, NULL, NULL); | |
|
robertshield
2011/03/01 16:04:43
ditto
grt (UTC plus 2)
2011/03/01 20:54:02
Done.
| |
| 151 if (result != ERROR_SUCCESS) { | |
| 152 LOG(ERROR) << "Failed getting name of up subkey " << i | |
| 153 << " for backup, result: " << result; | |
| 154 return false; | |
| 155 } | |
| 156 subkey_names[i].assign(name_buffer.get(), name_size); | |
| 157 } | |
| 158 DLOG_IF(WARNING, RegEnumKeyEx(key.Handle(), num_subkeys, NULL, &name_size, | |
| 159 NULL, NULL, NULL, NULL) == ERROR_SUCCESS) | |
| 160 << "Concurrent modifications to registry key during backup operation."; | |
| 161 | |
| 162 // Get their values. | |
| 163 RegKey subkey; | |
| 164 for (DWORD i = 0; i < num_subkeys; ++i) { | |
| 165 result = subkey.Open(key.Handle(), subkey_names[i].c_str(), KEY_READ); | |
| 166 if (result != ERROR_SUCCESS) { | |
| 167 LOG(ERROR) << "Failed opening subkey \"" << subkey_names[i] | |
| 168 << "\" for backup, result: " << result; | |
| 169 return false; | |
| 170 } | |
| 171 if (!subkeys[i].Initialize(subkey)) { | |
| 172 LOG(ERROR) << "Failed backing up subkey \"" << subkey_names[i] << "\""; | |
| 173 return false; | |
| 174 } | |
| 175 } | |
| 176 } | |
| 177 | |
| 178 values_.swap(values); | |
| 179 subkey_names_.swap(subkey_names); | |
| 180 subkeys_.swap(subkeys); | |
| 181 num_values_ = num_values; | |
| 182 num_subkeys_ = num_subkeys; | |
| 183 | |
| 184 return true; | |
| 185 } | |
| 186 | |
| 187 // Writes the values and subkeys of this object into |key|. | |
| 188 bool DeleteRegKeyWorkItem::RegKeyBackup::WriteTo(RegKey* key) const { | |
| 189 LONG result = ERROR_SUCCESS; | |
| 190 | |
| 191 // Write the values. | |
| 192 for (int i = 0; i < num_values_; ++i) { | |
| 193 const RegValueBackup& value = values_[i]; | |
| 194 result = RegSetValueEx(key->Handle(), value.name(), 0, value.type(), | |
| 195 value.data(), value.data_len()); | |
| 196 if (result != ERROR_SUCCESS) { | |
| 197 LOG(ERROR) << "Failed writing value \"" << value.name_str() | |
| 198 << "\", result: " << result; | |
| 199 return false; | |
| 200 } | |
| 201 } | |
| 202 | |
| 203 // Write the subkeys. | |
| 204 RegKey subkey; | |
| 205 for (int i = 0; i < num_subkeys_; ++i) { | |
| 206 const std::wstring& name = subkey_names_[i]; | |
| 207 | |
| 208 result = subkey.Create(key->Handle(), name.c_str(), KEY_WRITE); | |
| 209 if (result != ERROR_SUCCESS) { | |
| 210 LOG(ERROR) << "Failed creating subkey \"" << name << "\", result: " | |
| 211 << result; | |
| 212 return false; | |
| 213 } | |
| 214 if (!subkeys_[i].WriteTo(&subkey)) { | |
| 215 LOG(ERROR) << "Failed writing subkey \"" << name << "\", result: " | |
| 216 << result; | |
| 217 return false; | |
| 218 } | |
| 219 } | |
| 220 | |
| 221 return true; | |
| 222 } | |
| 12 | 223 |
| 13 DeleteRegKeyWorkItem::~DeleteRegKeyWorkItem() { | 224 DeleteRegKeyWorkItem::~DeleteRegKeyWorkItem() { |
| 14 } | 225 } |
| 15 | 226 |
| 16 DeleteRegKeyWorkItem::DeleteRegKeyWorkItem(HKEY predefined_root, | 227 DeleteRegKeyWorkItem::DeleteRegKeyWorkItem(HKEY predefined_root, |
| 17 const std::wstring& path) | 228 const std::wstring& path) |
| 18 : predefined_root_(predefined_root), | 229 : predefined_root_(predefined_root), |
| 19 path_(path) { | 230 path_(path) { |
| 231 // It's a safe bet that we don't want to delete one of the root trees. | |
| 232 DCHECK(!path.empty()); | |
| 20 } | 233 } |
| 21 | 234 |
| 22 bool DeleteRegKeyWorkItem::Do() { | 235 bool DeleteRegKeyWorkItem::Do() { |
| 23 // TODO(robertshield): Implement rollback for this work item. Consider using | 236 scoped_ptr<RegKeyBackup> backup; |
|
robertshield
2011/03/01 16:04:43
nit: 1 space
grt (UTC plus 2)
2011/03/01 20:54:02
Nice catch. Done.
| |
| 24 // RegSaveKey or RegCopyKey. | 237 |
| 238 // Only try to make a backup if we're not configured to ignore failures. | |
| 25 if (!ignore_failure_) { | 239 if (!ignore_failure_) { |
| 26 NOTREACHED(); | 240 RegKey original_key; |
| 27 LOG(ERROR) << "Unexpected configuration in DeleteRegKeyWorkItem."; | 241 |
| 28 return false; | 242 // Does the key exist? |
| 243 LONG result = original_key.Open(predefined_root_, path_.c_str(), KEY_READ); | |
| 244 if (result == ERROR_SUCCESS) { | |
| 245 backup.reset(new RegKeyBackup()); | |
| 246 if (!backup->Initialize(original_key)) { | |
| 247 LOG(ERROR) << "Failed to backup key at " << path_; | |
| 248 return ignore_failure_; | |
| 249 } | |
| 250 } else if (result != ERROR_FILE_NOT_FOUND) { | |
| 251 LOG(ERROR) << "Failed to open key at " << path_ | |
| 252 << " to create backup, result: " << result; | |
| 253 return ignore_failure_; | |
| 254 } | |
| 29 } | 255 } |
| 30 | 256 |
| 31 LSTATUS result = SHDeleteKey(predefined_root_, path_.c_str()); | 257 // Delete the key. |
| 32 if (result != ERROR_SUCCESS) { | 258 LONG result = SHDeleteKey(predefined_root_, path_.c_str()); |
| 33 LOG(ERROR) << "Failed to delete key at " << path_ << ", result: " << result; | 259 if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) { |
| 260 LOG(ERROR) << "Failed to delete key at " << path_ << ", result: " | |
| 261 << result; | |
| 262 return ignore_failure_; | |
| 34 } | 263 } |
| 35 | 264 |
| 265 // We've succeeded, so rememeber any backup we may have made. | |
| 266 backup_.swap(backup); | |
| 267 | |
| 36 return true; | 268 return true; |
| 37 } | 269 } |
| 38 | 270 |
| 39 void DeleteRegKeyWorkItem::Rollback() { | 271 void DeleteRegKeyWorkItem::Rollback() { |
| 40 if (ignore_failure_) | 272 if (ignore_failure_ || backup_.get() == NULL) |
| 41 return; | 273 return; |
| 42 NOTREACHED(); | 274 |
| 275 // Delete anything in the key before restoring the backup in case someone else | |
| 276 // put new data in the key after Do(). | |
| 277 LONG result = SHDeleteKey(predefined_root_, path_.c_str()); | |
| 278 if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) { | |
| 279 LOG(ERROR) << "Failed to delete key at " << path_ << " in rollback, " | |
| 280 "result: " << result; | |
| 281 } | |
| 282 | |
| 283 // Restore the old contents. The restoration takes on its default security | |
| 284 // attributes; any custom attributes are lost. | |
| 285 RegKey original_key; | |
| 286 result = original_key.Create(predefined_root_, path_.c_str(), KEY_WRITE); | |
| 287 if (result != ERROR_SUCCESS) { | |
| 288 LOG(ERROR) << "Failed to create original key at " << path_ | |
| 289 << " in rollback, result: " << result; | |
| 290 } else { | |
| 291 if (!backup_->WriteTo(&original_key)) | |
| 292 LOG(ERROR) << "Failed to restore key in rollback, result: " << result; | |
| 293 } | |
| 43 } | 294 } |
| 44 | |
| OLD | NEW |