// 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 <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* 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* 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> 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* 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> 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) { DCHECK(key_path); RegKey key; scoped_ptr<KeyData> key_data; // Does the key exist? LONG result = key.Open(root, key_path, kKeyReadNoNotify); 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) const { DCHECK(key_path); bool success = false; if (key_data_.get() != NULL) { RegKey dest_key; LONG result = dest_key.Create(root, key_path, KEY_WRITE); 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; }