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