Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(838)

Side by Side Diff: chrome/installer/util/delete_reg_key_work_item.cc

Issue 6598065: Add rollback support to DeleteRegKeyWorkItem.... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 9 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698