| // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/installer/util/registry_key_backup.h" |
| |
| #include <stdint.h> |
| |
| #include <algorithm> |
| #include <map> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/logging.h" |
| #include "base/win/registry.h" |
| |
| using base::win::RegKey; |
| |
| namespace { |
| |
| const REGSAM kKeyReadNoNotify = (KEY_READ) & ~(KEY_NOTIFY); |
| |
| // A container for a registry value. |
| class ValueData { |
| public: |
| ValueData(); |
| ~ValueData(); |
| |
| // Initializes this object with a name (the first |name_size| characters in |
| // |name_buffer|, |type|, and data (the first |data_size| bytes in |data|). |
| void Initialize(const wchar_t* name_buffer, |
| DWORD name_size, |
| DWORD type, |
| const uint8_t* data, |
| DWORD data_size); |
| |
| // The possibly empty name of this value. |
| const std::wstring& name_str() const { return name_; } |
| |
| // The name of this value, or NULL for the default (unnamed) value. |
| const wchar_t* name() const { return name_.empty() ? NULL : name_.c_str(); } |
| |
| // The type of this value. |
| DWORD type() const { return type_; } |
| |
| // A pointer to a buffer of |data_len()| bytes containing the value's data, |
| // or NULL if the value has no data. |
| const uint8_t* data() const { return data_.empty() ? NULL : &data_[0]; } |
| |
| // The size, in bytes, of the value's data. |
| DWORD data_len() const { return static_cast<DWORD>(data_.size()); } |
| |
| private: |
| // This value's name, or the empty string if this is the default (unnamed) |
| // value. |
| std::wstring name_; |
| // This value's data. |
| std::vector<uint8_t> data_; |
| // This value's type (e.g., REG_DWORD, REG_SZ, REG_QWORD, etc). |
| DWORD type_; |
| |
| // Copy constructible and assignable for use in STL containers. |
| }; |
| |
| } // namespace |
| |
| // A container for a registry key, its values, and its subkeys. |
| class RegistryKeyBackup::KeyData { |
| public: |
| KeyData(); |
| ~KeyData(); |
| |
| // Initializes this object by reading the values and subkeys of |key|. |
| // Security descriptors are not backed up. Returns true if the operation was |
| // successful; false otherwise, in which case the state of this object is not |
| // modified. |
| bool Initialize(const RegKey& key); |
| |
| // Writes the contents of this object to |key|, which must have been opened |
| // with at least REG_SET_VALUE and KEY_CREATE_SUB_KEY access rights. Returns |
| // true if the operation was successful; false otherwise, in which case the |
| // contents of |key| may have been modified. |
| bool WriteTo(RegKey* key) const; |
| |
| private: |
| // The values of this key. |
| std::vector<ValueData> values_; |
| // Map of subkey names to the corresponding KeyData. |
| std::map<std::wstring, KeyData> subkeys_; |
| |
| // Copy constructible and assignable for use in STL containers. |
| }; |
| |
| ValueData::ValueData() : type_(REG_NONE) { |
| } |
| |
| ValueData::~ValueData() { |
| } |
| |
| void ValueData::Initialize(const wchar_t* name_buffer, |
| DWORD name_size, |
| DWORD type, |
| const uint8_t* data, |
| DWORD data_size) { |
| name_.assign(name_buffer, name_size); |
| type_ = type; |
| data_.assign(data, data + data_size); |
| } |
| |
| RegistryKeyBackup::KeyData::KeyData() { |
| } |
| |
| RegistryKeyBackup::KeyData::~KeyData() { |
| } |
| |
| bool RegistryKeyBackup::KeyData::Initialize(const RegKey& key) { |
| std::vector<ValueData> values; |
| std::map<std::wstring, KeyData> 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; |
| } |
| DWORD max_name_len = std::max(max_subkey_name_len, max_value_name_len) + 1; |
| std::vector<wchar_t> name_buffer(max_name_len); |
| |
| // Backup the values. |
| if (num_values != 0) { |
| values.reserve(num_values); |
| std::vector<uint8_t> value_buffer(max_value_len != 0 ? max_value_len : 1); |
| DWORD name_size = 0; |
| DWORD value_type = REG_NONE; |
| DWORD value_size = 0; |
| |
| for (DWORD i = 0; i < num_values; ) { |
| name_size = static_cast<DWORD>(name_buffer.size()); |
| value_size = static_cast<DWORD>(value_buffer.size()); |
| result = RegEnumValue(key.Handle(), i, &name_buffer[0], &name_size, |
| NULL, &value_type, &value_buffer[0], &value_size); |
| switch (result) { |
| case ERROR_NO_MORE_ITEMS: |
| num_values = i; |
| break; |
| case ERROR_SUCCESS: |
| values.push_back(ValueData()); |
| values.back().Initialize(&name_buffer[0], name_size, value_type, |
| &value_buffer[0], value_size); |
| ++i; |
| break; |
| case ERROR_MORE_DATA: |
| if (value_size > value_buffer.size()) |
| value_buffer.resize(value_size); |
| // |name_size| does not include space for the terminating NULL. |
| if (name_size + 1 > name_buffer.size()) |
| name_buffer.resize(name_size + 1); |
| break; |
| default: |
| LOG(ERROR) << "Failed backing up value " << i << ", result: " |
| << result; |
| return false; |
| } |
| } |
| DLOG_IF(WARNING, RegEnumValue(key.Handle(), num_values, &name_buffer[0], |
| &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) { |
| DWORD name_size = 0; |
| |
| // Get the names of them. |
| for (DWORD i = 0; i < num_subkeys; ) { |
| name_size = static_cast<DWORD>(name_buffer.size()); |
| result = RegEnumKeyEx(key.Handle(), i, &name_buffer[0], &name_size, |
| NULL, NULL, NULL, NULL); |
| switch (result) { |
| case ERROR_NO_MORE_ITEMS: |
| num_subkeys = i; |
| break; |
| case ERROR_SUCCESS: |
| subkeys.insert(std::make_pair(&name_buffer[0], KeyData())); |
| ++i; |
| break; |
| case ERROR_MORE_DATA: |
| name_buffer.resize(name_size + 1); |
| 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 (std::map<std::wstring, KeyData>::iterator it = subkeys.begin(); |
| it != subkeys.end(); ++it) { |
| result = subkey.Open(key.Handle(), it->first.c_str(), kKeyReadNoNotify); |
| if (result != ERROR_SUCCESS) { |
| LOG(ERROR) << "Failed opening subkey \"" << it->first |
| << "\" for backup, result: " << result; |
| return false; |
| } |
| if (!it->second.Initialize(subkey)) { |
| LOG(ERROR) << "Failed backing up subkey \"" << it->first << "\""; |
| return false; |
| } |
| } |
| } |
| |
| values_.swap(values); |
| subkeys_.swap(subkeys); |
| |
| return true; |
| } |
| |
| bool RegistryKeyBackup::KeyData::WriteTo(RegKey* key) const { |
| DCHECK(key); |
| |
| LONG result = ERROR_SUCCESS; |
| |
| // Write the values. |
| for (std::vector<ValueData>::const_iterator it = values_.begin(); |
| it != values_.end(); ++it) { |
| const ValueData& value = *it; |
| 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 (std::map<std::wstring, KeyData>::const_iterator it = subkeys_.begin(); |
| it != subkeys_.end(); ++it) { |
| const std::wstring& name = it->first; |
| |
| 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 (!it->second.WriteTo(&subkey)) { |
| LOG(ERROR) << "Failed writing subkey \"" << name << "\", result: " |
| << result; |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| RegistryKeyBackup::RegistryKeyBackup() { |
| } |
| |
| RegistryKeyBackup::~RegistryKeyBackup() { |
| } |
| |
| bool RegistryKeyBackup::Initialize(HKEY root, |
| const wchar_t* key_path, |
| REGSAM wow64_access) { |
| DCHECK(key_path); |
| DCHECK(wow64_access == 0 || |
| wow64_access == KEY_WOW64_32KEY || |
| wow64_access == KEY_WOW64_64KEY); |
| |
| RegKey key; |
| std::unique_ptr<KeyData> key_data; |
| |
| // Does the key exist? |
| LONG result = key.Open(root, key_path, kKeyReadNoNotify | wow64_access); |
| if (result == ERROR_SUCCESS) { |
| key_data.reset(new KeyData()); |
| if (!key_data->Initialize(key)) { |
| LOG(ERROR) << "Failed to backup key at " << key_path; |
| return false; |
| } |
| } else if (result != ERROR_FILE_NOT_FOUND) { |
| LOG(ERROR) << "Failed to open key at " << key_path |
| << " to create backup, result: " << result; |
| return false; |
| } |
| |
| key_data_.swap(key_data); |
| return true; |
| } |
| |
| bool RegistryKeyBackup::WriteTo(HKEY root, |
| const wchar_t* key_path, |
| REGSAM wow64_access) const { |
| DCHECK(key_path); |
| DCHECK(wow64_access == 0 || |
| wow64_access == KEY_WOW64_32KEY || |
| wow64_access == KEY_WOW64_64KEY); |
| |
| bool success = false; |
| |
| if (key_data_.get() != NULL) { |
| RegKey dest_key; |
| LONG result = dest_key.Create(root, key_path, KEY_WRITE | wow64_access); |
| if (result != ERROR_SUCCESS) { |
| LOG(ERROR) << "Failed to create destination key at " << key_path |
| << " to write backup, result: " << result; |
| } else { |
| success = key_data_->WriteTo(&dest_key); |
| LOG_IF(ERROR, !success) << "Failed to write key data."; |
| } |
| } else { |
| success = true; |
| } |
| |
| return success; |
| } |