| 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,268 @@
|
| // 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 - 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 +271,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;
|
| + }
|
| }
|
| -
|
|
|