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

Unified 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, 10 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 side-by-side diff with in-line comments
Download patch
Index: chrome/installer/util/delete_reg_key_work_item.cc
===================================================================
--- chrome/installer/util/delete_reg_key_work_item.cc (revision 76372)
+++ chrome/installer/util/delete_reg_key_work_item.cc (working copy)
@@ -2,14 +2,269 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "chrome/installer/util/delete_reg_key_work_item.h"
+
#include <shlwapi.h>
+#include <algorithm>
+#include <limits>
+#include <vector>
-#include "chrome/installer/util/delete_reg_key_work_item.h"
-
#include "base/logging.h"
+#include "base/rand_util.h"
+#include "base/stringprintf.h"
+#include "base/win/registry.h"
#include "chrome/installer/util/logging_installer.h"
+using base::win::RegKey;
+namespace {
+const REGSAM kKeyReadNoNotify = (KEY_READ) & ~(KEY_NOTIFY);
+}
+
+// A container for a registry key, its values, and its subkeys. We don't use
+// more obvious methods for various reasons:
+// - RegCopyTree isn't supported pre-Vista, so we'd have to do something
+// different for XP anyway.
+// - SHCopyKey can't copy subkeys into a volatile destination, so we'd have to
+// worry about polluting the registry.
+// We don't persist security attributes since we only delete keys that we own,
+// and we don't set custom attributes on them anyway.
+class DeleteRegKeyWorkItem::RegKeyBackup {
+ public:
+ RegKeyBackup();
+ bool Initialize(const RegKey& key);
+ bool WriteTo(RegKey* key) const;
+
+ private:
+ // A container for a registry value.
+ class RegValueBackup {
+ public:
+ RegValueBackup();
+ void Initialize(const wchar_t* name_buffer, DWORD name_size,
+ DWORD type, const uint8* data, DWORD data_size);
+ const std::wstring& name_str() const { return name_; }
+ const wchar_t* name() const { return name_.empty() ? NULL : name_.c_str(); }
+ DWORD type() const { return type_; }
+ const uint8* data() const { return data_.empty() ? NULL : &data_[0]; }
+ DWORD data_len() const { return static_cast<DWORD>(data_.size()); }
+
+ private:
+ std::wstring name_;
+ std::vector<uint8> data_;
+ DWORD type_;
+
+ DISALLOW_COPY_AND_ASSIGN(RegValueBackup);
+ };
+
+ scoped_array<RegValueBackup> values_;
+ scoped_array<std::wstring> subkey_names_;
+ scoped_array<RegKeyBackup> subkeys_;
+ ptrdiff_t num_values_;
+ ptrdiff_t num_subkeys_;
+
+ DISALLOW_COPY_AND_ASSIGN(RegKeyBackup);
+};
+
+DeleteRegKeyWorkItem::RegKeyBackup::RegValueBackup::RegValueBackup()
+ : type_(REG_NONE) {
+}
+
+void DeleteRegKeyWorkItem::RegKeyBackup::RegValueBackup::Initialize(
+ const wchar_t* name_buffer,
+ DWORD name_size,
+ DWORD type, const uint8* data,
+ DWORD data_size) {
+ name_.assign(name_buffer, name_size);
+ type_ = type;
+ data_.assign(data, data + data_size);
+}
+
+DeleteRegKeyWorkItem::RegKeyBackup::RegKeyBackup()
+ : num_values_(0),
+ num_subkeys_(0) {
+}
+
+// Initializes this object by reading the values and subkeys of |key|.
+// Security descriptors are not backed up.
+bool DeleteRegKeyWorkItem::RegKeyBackup::Initialize(const RegKey& key) {
+ DCHECK(key.Valid());
+
+ scoped_array<RegValueBackup> values;
+ scoped_array<std::wstring> subkey_names;
+ scoped_array<RegKeyBackup> subkeys;
+
+ DWORD num_subkeys = 0;
+ DWORD max_subkey_name_len = 0;
+ DWORD num_values = 0;
+ DWORD max_value_name_len = 0;
+ DWORD max_value_len = 0;
+ LONG result = RegQueryInfoKey(key.Handle(), NULL, NULL, NULL,
+ &num_subkeys, &max_subkey_name_len, NULL,
+ &num_values, &max_value_name_len,
+ &max_value_len, NULL, NULL);
+ if (result != ERROR_SUCCESS) {
+ LOG(ERROR) << "Failed getting info of key to backup, result: " << result;
+ return false;
+ }
+ if (max_subkey_name_len >= std::numeric_limits<DWORD>::max() - 1 ||
+ max_value_name_len >= std::numeric_limits<DWORD>::max() - 1) {
+ LOG(ERROR)
+ << "Failed backing up key; subkeys and/or names are out of range.";
+ return false;
+ }
+ DWORD max_name_len = std::max(max_subkey_name_len, max_value_name_len) + 1;
+ scoped_array<wchar_t> name_buffer(new wchar_t[max_name_len]);
+
+ // Backup the values.
+ if (num_values != 0) {
+ values.reset(new RegValueBackup[num_values]);
+ scoped_array<uint8> value_buffer(new uint8[max_value_len]);
+ DWORD name_size = 0;
+ DWORD value_type = REG_NONE;
+ DWORD value_size = 0;
+
+ for (DWORD i = 0; i < num_values; ) {
+ name_size = max_name_len;
+ value_size = max_value_len;
+ result = RegEnumValue(key.Handle(), i, name_buffer.get(), &name_size,
+ NULL, &value_type, value_buffer.get(), &value_size);
+ switch (result) {
+ case ERROR_NO_MORE_ITEMS:
+ num_values = i;
+ break;
+ case ERROR_SUCCESS:
+ values[i].Initialize(name_buffer.get(), name_size, value_type,
+ value_buffer.get(), value_size);
+ ++i;
+ break;
+ case ERROR_MORE_DATA:
+ if (value_size > max_value_len) {
+ max_value_len = value_size;
+ value_buffer.reset(new uint8[max_value_len]);
+ } else {
+ 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.
+ max_name_len - 1 < name_size);
+ if (name_size >= std::numeric_limits<DWORD>::max() - 1) {
+ LOG(ERROR) << "Failed backing up key; value name out of range.";
+ return false;
+ }
+ max_name_len = name_size + 1;
+ name_buffer.reset(new wchar_t[max_name_len]);
+ }
+ break;
+ default:
+ LOG(ERROR) << "Failed backing up value " << i << ", result: "
+ << result;
+ return false;
+ }
+ }
+ DLOG_IF(WARNING, RegEnumValue(key.Handle(), num_values, name_buffer.get(),
+ &name_size, NULL, &value_type, NULL,
+ NULL) != ERROR_NO_MORE_ITEMS)
+ << "Concurrent modifications to registry key during backup operation.";
+ }
+
+ // Backup the subkeys.
+ if (num_subkeys != 0) {
+ subkey_names.reset(new std::wstring[num_subkeys]);
+ subkeys.reset(new RegKeyBackup[num_subkeys]);
+ DWORD name_size = 0;
+
+ // Get the names of them.
+ for (DWORD i = 0; i < num_subkeys; ) {
+ name_size = max_name_len;
+ result = RegEnumKeyEx(key.Handle(), i, name_buffer.get(), &name_size,
+ NULL, NULL, NULL, NULL);
+ switch (result) {
+ case ERROR_NO_MORE_ITEMS:
+ num_subkeys = i;
+ break;
+ case ERROR_SUCCESS:
+ subkey_names[i].assign(name_buffer.get(), name_size);
+ ++i;
+ break;
+ case ERROR_MORE_DATA:
+ if (name_size >= std::numeric_limits<DWORD>::max() - 1) {
+ LOG(ERROR) << "Failed backing up key; subkey name out of range.";
+ return false;
+ }
+ max_name_len = name_size + 1;
+ name_buffer.reset(new wchar_t[max_name_len]);
+ break;
+ default:
+ LOG(ERROR) << "Failed getting name of subkey " << i
+ << " for backup, result: " << result;
+ return false;
+ }
+ }
+ DLOG_IF(WARNING,
+ RegEnumKeyEx(key.Handle(), num_subkeys, NULL, &name_size, NULL,
+ NULL, NULL, NULL) != ERROR_NO_MORE_ITEMS)
+ << "Concurrent modifications to registry key during backup operation.";
+
+ // Get their values.
+ RegKey subkey;
+ for (DWORD i = 0; i < num_subkeys; ++i) {
+ result = subkey.Open(key.Handle(), subkey_names[i].c_str(),
+ kKeyReadNoNotify);
+ if (result != ERROR_SUCCESS) {
+ LOG(ERROR) << "Failed opening subkey \"" << subkey_names[i]
+ << "\" for backup, result: " << result;
+ return false;
+ }
+ if (!subkeys[i].Initialize(subkey)) {
+ LOG(ERROR) << "Failed backing up subkey \"" << subkey_names[i] << "\"";
+ return false;
+ }
+ }
+ }
+
+ values_.swap(values);
+ subkey_names_.swap(subkey_names);
+ subkeys_.swap(subkeys);
+ num_values_ = num_values;
+ num_subkeys_ = num_subkeys;
+
+ return true;
+}
+
+// Writes the values and subkeys of this object into |key|.
+bool DeleteRegKeyWorkItem::RegKeyBackup::WriteTo(RegKey* key) const {
+ LONG result = ERROR_SUCCESS;
+
+ // Write the values.
+ for (int i = 0; i < num_values_; ++i) {
+ const RegValueBackup& value = values_[i];
+ result = RegSetValueEx(key->Handle(), value.name(), 0, value.type(),
+ value.data(), value.data_len());
+ if (result != ERROR_SUCCESS) {
+ LOG(ERROR) << "Failed writing value \"" << value.name_str()
+ << "\", result: " << result;
+ return false;
+ }
+ }
+
+ // Write the subkeys.
+ RegKey subkey;
+ for (int i = 0; i < num_subkeys_; ++i) {
+ const std::wstring& name = subkey_names_[i];
+
+ result = subkey.Create(key->Handle(), name.c_str(), KEY_WRITE);
+ if (result != ERROR_SUCCESS) {
+ LOG(ERROR) << "Failed creating subkey \"" << name << "\", result: "
+ << result;
+ return false;
+ }
+ if (!subkeys_[i].WriteTo(&subkey)) {
+ LOG(ERROR) << "Failed writing subkey \"" << name << "\", result: "
+ << result;
+ return false;
+ }
+ }
+
+ return true;
+}
+
DeleteRegKeyWorkItem::~DeleteRegKeyWorkItem() {
}
@@ -17,28 +272,68 @@
const std::wstring& path)
: predefined_root_(predefined_root),
path_(path) {
+ // It's a safe bet that we don't want to delete one of the root trees.
+ DCHECK(!path.empty());
}
bool DeleteRegKeyWorkItem::Do() {
- // TODO(robertshield): Implement rollback for this work item. Consider using
- // RegSaveKey or RegCopyKey.
+ scoped_ptr<RegKeyBackup> backup;
+
+ // Only try to make a backup if we're not configured to ignore failures.
if (!ignore_failure_) {
- NOTREACHED();
- LOG(ERROR) << "Unexpected configuration in DeleteRegKeyWorkItem.";
- return false;
+ RegKey original_key;
+
+ // Does the key exist?
+ LONG result = original_key.Open(predefined_root_, path_.c_str(),
+ kKeyReadNoNotify);
+ if (result == ERROR_SUCCESS) {
+ backup.reset(new RegKeyBackup());
+ if (!backup->Initialize(original_key)) {
+ LOG(ERROR) << "Failed to backup key at " << path_;
+ return ignore_failure_;
+ }
+ } else if (result != ERROR_FILE_NOT_FOUND) {
+ LOG(ERROR) << "Failed to open key at " << path_
+ << " to create backup, result: " << result;
+ return ignore_failure_;
+ }
}
- LSTATUS result = SHDeleteKey(predefined_root_, path_.c_str());
- if (result != ERROR_SUCCESS) {
- LOG(ERROR) << "Failed to delete key at " << path_ << ", result: " << result;
+ // Delete the key.
+ LONG result = SHDeleteKey(predefined_root_, path_.c_str());
+ if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) {
+ LOG(ERROR) << "Failed to delete key at " << path_ << ", result: "
+ << result;
+ return ignore_failure_;
}
+ // We've succeeded, so remember any backup we may have made.
+ backup_.swap(backup);
+
return true;
}
void DeleteRegKeyWorkItem::Rollback() {
- if (ignore_failure_)
+ if (ignore_failure_ || backup_.get() == NULL)
return;
- NOTREACHED();
+
+ // Delete anything in the key before restoring the backup in case someone else
+ // put new data in the key after Do().
+ LONG result = SHDeleteKey(predefined_root_, path_.c_str());
+ if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) {
+ LOG(ERROR) << "Failed to delete key at " << path_ << " in rollback, "
+ "result: " << result;
+ }
+
+ // Restore the old contents. The restoration takes on its default security
+ // attributes; any custom attributes are lost.
+ RegKey original_key;
+ result = original_key.Create(predefined_root_, path_.c_str(), KEY_WRITE);
+ if (result != ERROR_SUCCESS) {
+ LOG(ERROR) << "Failed to create original key at " << path_
+ << " in rollback, result: " << result;
+ } else {
+ if (!backup_->WriteTo(&original_key))
+ LOG(ERROR) << "Failed to restore key in rollback, result: " << result;
+ }
}
-

Powered by Google App Engine
This is Rietveld 408576698