summaryrefslogtreecommitdiffstats
path: root/chrome/installer
diff options
context:
space:
mode:
authorgrt@chromium.org <grt@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-09-18 02:47:17 +0000
committergrt@chromium.org <grt@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-09-18 02:47:17 +0000
commita6f66bf26ec3118c12710b3bdd325a53e69d1da1 (patch)
tree70ce82d288d7a031cfed00290cab461810c1f8e0 /chrome/installer
parentc135686acf4c9b2a2b9cd10ba6a6ecf1b67a8d05 (diff)
downloadchromium_src-a6f66bf26ec3118c12710b3bdd325a53e69d1da1.zip
chromium_src-a6f66bf26ec3118c12710b3bdd325a53e69d1da1.tar.gz
chromium_src-a6f66bf26ec3118c12710b3bdd325a53e69d1da1.tar.bz2
Added CopyRegKeyWorkItem in support of IE low rights policy fixes.
A subsequent CL will contain the rest of the fixes for the bug. BUG=95810 TEST=installer_util_unittests.exe Review URL: http://codereview.chromium.org/7890069 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@101684 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/installer')
-rw-r--r--chrome/installer/util/copy_reg_key_work_item.cc100
-rw-r--r--chrome/installer/util/copy_reg_key_work_item.h51
-rw-r--r--chrome/installer/util/copy_reg_key_work_item_unittest.cc141
-rw-r--r--chrome/installer/util/delete_reg_key_work_item.cc290
-rw-r--r--chrome/installer/util/delete_reg_key_work_item.h12
-rw-r--r--chrome/installer/util/delete_reg_key_work_item_unittest.cc112
-rw-r--r--chrome/installer/util/registry_key_backup.cc321
-rw-r--r--chrome/installer/util/registry_key_backup.h57
-rw-r--r--chrome/installer/util/registry_key_backup_unittest.cc85
-rw-r--r--chrome/installer/util/registry_test_data.cc108
-rw-r--r--chrome/installer/util/registry_test_data.h59
-rw-r--r--chrome/installer/util/work_item.cc9
-rw-r--r--chrome/installer/util/work_item.h7
-rw-r--r--chrome/installer/util/work_item_list.cc11
-rw-r--r--chrome/installer/util/work_item_list.h15
15 files changed, 1013 insertions, 365 deletions
diff --git a/chrome/installer/util/copy_reg_key_work_item.cc b/chrome/installer/util/copy_reg_key_work_item.cc
new file mode 100644
index 0000000..a0d2b1f
--- /dev/null
+++ b/chrome/installer/util/copy_reg_key_work_item.cc
@@ -0,0 +1,100 @@
+// 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/copy_reg_key_work_item.h"
+
+#include <shlwapi.h>
+
+#include "base/logging.h"
+#include "base/win/registry.h"
+
+using base::win::RegKey;
+
+CopyRegKeyWorkItem::~CopyRegKeyWorkItem() {
+}
+
+CopyRegKeyWorkItem::CopyRegKeyWorkItem(HKEY predefined_root,
+ const std::wstring& source_key_path,
+ const std::wstring& dest_key_path)
+ : predefined_root_(predefined_root),
+ source_key_path_(source_key_path),
+ dest_key_path_(dest_key_path) {
+ DCHECK(predefined_root);
+ // It's a safe bet that we don't want to copy or overwrite one of the root
+ // trees.
+ DCHECK(!source_key_path.empty());
+ DCHECK(!dest_key_path.empty());
+}
+
+bool CopyRegKeyWorkItem::Do() {
+ if (source_key_path_.empty() || dest_key_path_.empty())
+ return false;
+
+ RegistryKeyBackup backup;
+ RegKey dest_key;
+
+ // Only try to make a backup if we're not configured to ignore failures.
+ if (!ignore_failure_) {
+ if (!backup.Initialize(predefined_root_, dest_key_path_.c_str())) {
+ LOG(ERROR) << "Failed to backup destination for registry key copy.";
+ return false;
+ }
+ }
+
+ // Delete the destination before attempting to copy.
+ LONG result = SHDeleteKey(predefined_root_, dest_key_path_.c_str());
+ if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) {
+ LOG(ERROR) << "Failed to delete key at " << dest_key_path_ << ", result: "
+ << result;
+ } else {
+ // We've just modified the registry, so remember any backup we may have
+ // made so that Rollback can take us back where we started.
+ backup_.swap(backup);
+ // Make the copy.
+ result = dest_key.Create(predefined_root_, dest_key_path_.c_str(),
+ KEY_WRITE);
+ if (result != ERROR_SUCCESS) {
+ LOG(ERROR) << "Failed to open destination key at " << dest_key_path_
+ << ", result: " << result;
+ } else {
+ result = SHCopyKey(predefined_root_, source_key_path_.c_str(),
+ dest_key.Handle(), 0);
+ switch (result) {
+ case ERROR_FILE_NOT_FOUND:
+ // The source didn't exist, so neither should the destination.
+ dest_key.Close();
+ SHDeleteKey(predefined_root_, dest_key_path_.c_str());
+ // Handle like a success.
+ result = ERROR_SUCCESS;
+ // -- FALL THROUGH TO SUCCESS CASE --
+ case ERROR_SUCCESS:
+ break;
+ default:
+ LOG(ERROR) << "Failed to copy key from " << source_key_path_ << " to "
+ << dest_key_path_ << ", result: " << result;
+ break;
+ }
+ }
+ }
+
+ return ignore_failure_ ? true : (result == ERROR_SUCCESS);
+}
+
+void CopyRegKeyWorkItem::Rollback() {
+ if (ignore_failure_)
+ return;
+
+ // 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_, dest_key_path_.c_str());
+ if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) {
+ LOG(ERROR) << "Failed to delete key at " << dest_key_path_
+ << " in rollback, result: " << result;
+ }
+
+ // Restore the old contents. The restoration takes on its default security
+ // attributes; any custom attributes are lost.
+ if (!backup_.WriteTo(predefined_root_, dest_key_path_.c_str()))
+ LOG(ERROR) << "Failed to restore key in rollback.";
+}
diff --git a/chrome/installer/util/copy_reg_key_work_item.h b/chrome/installer/util/copy_reg_key_work_item.h
new file mode 100644
index 0000000..b1d4631
--- /dev/null
+++ b/chrome/installer/util/copy_reg_key_work_item.h
@@ -0,0 +1,51 @@
+// 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.
+
+#ifndef CHROME_INSTALLER_UTIL_COPY_REG_KEY_WORK_ITEM_H_
+#define CHROME_INSTALLER_UTIL_COPY_REG_KEY_WORK_ITEM_H_
+#pragma once
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "chrome/installer/util/registry_key_backup.h"
+#include "chrome/installer/util/work_item.h"
+
+// A WorkItem subclass that replaces the contents of one registry key with that
+// of another (i.e., the destination is erased prior to the copy). Be aware
+// that in the event of rollback the destination key's values and subkeys are
+// restored but the key and its subkeys take on their default security
+// descriptors.
+class CopyRegKeyWorkItem : public WorkItem {
+ public:
+ virtual ~CopyRegKeyWorkItem();
+ virtual bool Do() OVERRIDE;
+ virtual void Rollback() OVERRIDE;
+
+ private:
+ friend class WorkItem;
+
+ // Neither |source_key_path| nor |dest_key_path| may be empty.
+ CopyRegKeyWorkItem(HKEY predefined_key,
+ const std::wstring& source_key_path,
+ const std::wstring& dest_key_path);
+
+ // Root key in which we operate. The root key must be one of the predefined
+ // keys on Windows.
+ HKEY predefined_root_;
+
+ // Path of the key to be copied.
+ std::wstring source_key_path_;
+
+ // Path of the destination key.
+ std::wstring dest_key_path_;
+
+ // Backup of the destination key.
+ RegistryKeyBackup backup_;
+
+ DISALLOW_COPY_AND_ASSIGN(CopyRegKeyWorkItem);
+};
+
+#endif // CHROME_INSTALLER_UTIL_COPY_REG_KEY_WORK_ITEM_H_
diff --git a/chrome/installer/util/copy_reg_key_work_item_unittest.cc b/chrome/installer/util/copy_reg_key_work_item_unittest.cc
new file mode 100644
index 0000000..84a6f64
--- /dev/null
+++ b/chrome/installer/util/copy_reg_key_work_item_unittest.cc
@@ -0,0 +1,141 @@
+// 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 <windows.h>
+#include <shlwapi.h> // NOLINT
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/win/registry.h"
+#include "chrome/installer/util/copy_reg_key_work_item.h"
+#include "chrome/installer/util/registry_test_data.h"
+#include "chrome/installer/util/work_item.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::win::RegKey;
+
+class CopyRegKeyWorkItemTest : public testing::Test {
+ protected:
+ static void TearDownTestCase() {
+ logging::CloseLogFile();
+ }
+
+ virtual void SetUp() {
+ ASSERT_TRUE(test_data_.Initialize(HKEY_CURRENT_USER, L"SOFTWARE\\TmpTmp"));
+ destination_path_.assign(test_data_.base_path()).append(L"\\Destination");
+ }
+
+ RegistryTestData test_data_;
+ std::wstring destination_path_;
+};
+
+// Test that copying a key that doesn't exist succeeds, and that rollback does
+// nothing.
+TEST_F(CopyRegKeyWorkItemTest, TestNoKey) {
+ const std::wstring key_paths[] = {
+ std::wstring(test_data_.base_path() + L"\\NoKeyHere"),
+ std::wstring(test_data_.base_path() + L"\\NoKeyHere\\OrHere")
+ };
+ RegKey key;
+ for (size_t i = 0; i < arraysize(key_paths); ++i) {
+ const std::wstring& key_path = key_paths[i];
+ scoped_ptr<CopyRegKeyWorkItem> item(
+ WorkItem::CreateCopyRegKeyWorkItem(test_data_.root_key(), key_path,
+ destination_path_));
+ EXPECT_TRUE(item->Do());
+ EXPECT_NE(ERROR_SUCCESS,
+ key.Open(test_data_.root_key(), destination_path_.c_str(),
+ KEY_READ));
+ item->Rollback();
+ item.reset();
+ EXPECT_NE(ERROR_SUCCESS,
+ key.Open(test_data_.root_key(), destination_path_.c_str(),
+ KEY_READ));
+ }
+}
+
+// Test that copying an empty key succeeds, and that rollback removes the copy.
+TEST_F(CopyRegKeyWorkItemTest, TestEmptyKey) {
+ RegKey key;
+ scoped_ptr<CopyRegKeyWorkItem> item(
+ WorkItem::CreateCopyRegKeyWorkItem(test_data_.root_key(),
+ test_data_.empty_key_path(),
+ destination_path_));
+ EXPECT_TRUE(item->Do());
+ RegistryTestData::ExpectEmptyKey(test_data_.root_key(),
+ destination_path_.c_str());
+ item->Rollback();
+ item.reset();
+ EXPECT_NE(ERROR_SUCCESS,
+ key.Open(test_data_.root_key(), destination_path_.c_str(),
+ KEY_READ));
+}
+
+// Test that copying a key with subkeys and values succeeds, and that rollback
+// removes the copy.
+TEST_F(CopyRegKeyWorkItemTest, TestNonEmptyKey) {
+ RegKey key;
+ scoped_ptr<CopyRegKeyWorkItem> item(
+ WorkItem::CreateCopyRegKeyWorkItem(test_data_.root_key(),
+ test_data_.non_empty_key_path(),
+ destination_path_));
+ EXPECT_TRUE(item->Do());
+ test_data_.ExpectMatchesNonEmptyKey(test_data_.root_key(),
+ destination_path_.c_str());
+ item->Rollback();
+ item.reset();
+ EXPECT_NE(ERROR_SUCCESS,
+ key.Open(test_data_.root_key(), destination_path_.c_str(),
+ KEY_READ));
+}
+
+// Test that copying an empty key over a key with data succeeds, and that
+// rollback restores the original data.
+TEST_F(CopyRegKeyWorkItemTest, TestOverwriteAndRestore) {
+ RegKey key;
+ // First copy the data into the dest.
+ EXPECT_EQ(ERROR_SUCCESS,
+ key.Create(test_data_.root_key(), destination_path_.c_str(),
+ KEY_WRITE));
+ EXPECT_EQ(ERROR_SUCCESS,
+ SHCopyKey(test_data_.root_key(),
+ test_data_.non_empty_key_path().c_str(), key.Handle(),
+ 0));
+ key.Close();
+
+ // Now copy the empty key into the dest.
+ scoped_ptr<CopyRegKeyWorkItem> item(
+ WorkItem::CreateCopyRegKeyWorkItem(test_data_.root_key(),
+ test_data_.empty_key_path(),
+ destination_path_));
+ EXPECT_TRUE(item->Do());
+
+ // Make sure the dest is now empty.
+ RegistryTestData::ExpectEmptyKey(test_data_.root_key(),
+ destination_path_.c_str());
+
+ // Restore the data.
+ item->Rollback();
+ item.reset();
+
+ // Make sure it's all there.
+ test_data_.ExpectMatchesNonEmptyKey(test_data_.root_key(),
+ destination_path_.c_str());
+}
+
+// Test that Rollback does nothing when the item is configured to ignore
+// failures.
+TEST_F(CopyRegKeyWorkItemTest, TestIgnoreFailRollback) {
+ // Copy the empty key onto the non-empty key, ignoring failures.
+ scoped_ptr<CopyRegKeyWorkItem> item(
+ WorkItem::CreateCopyRegKeyWorkItem(test_data_.root_key(),
+ test_data_.empty_key_path(),
+ test_data_.non_empty_key_path()));
+ item->set_ignore_failure(true);
+ EXPECT_TRUE(item->Do());
+ item->Rollback();
+ item.reset();
+ RegistryTestData::ExpectEmptyKey(test_data_.root_key(),
+ test_data_.non_empty_key_path().c_str());
+}
diff --git a/chrome/installer/util/delete_reg_key_work_item.cc b/chrome/installer/util/delete_reg_key_work_item.cc
index b044f44..462698f 100644
--- a/chrome/installer/util/delete_reg_key_work_item.cc
+++ b/chrome/installer/util/delete_reg_key_work_item.cc
@@ -5,265 +5,12 @@
#include "chrome/installer/util/delete_reg_key_work_item.h"
#include <shlwapi.h>
-#include <algorithm>
-#include <limits>
-#include <vector>
#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() {
}
@@ -271,30 +18,22 @@ DeleteRegKeyWorkItem::DeleteRegKeyWorkItem(HKEY predefined_root,
const std::wstring& path)
: predefined_root_(predefined_root),
path_(path) {
+ DCHECK(predefined_root);
// It's a safe bet that we don't want to delete one of the root trees.
DCHECK(!path.empty());
}
bool DeleteRegKeyWorkItem::Do() {
- scoped_ptr<RegKeyBackup> backup;
+ if (path_.empty())
+ return false;
+
+ RegistryKeyBackup backup;
// Only try to make a backup if we're not configured to ignore failures.
if (!ignore_failure_) {
- 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_;
+ if (!backup.Initialize(predefined_root_, path_.c_str())) {
+ LOG(ERROR) << "Failed to backup destination for registry key copy.";
+ return false;
}
}
@@ -313,7 +52,7 @@ bool DeleteRegKeyWorkItem::Do() {
}
void DeleteRegKeyWorkItem::Rollback() {
- if (ignore_failure_ || backup_.get() == NULL)
+ if (ignore_failure_)
return;
// Delete anything in the key before restoring the backup in case someone else
@@ -326,13 +65,6 @@ void DeleteRegKeyWorkItem::Rollback() {
// 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;
- }
+ if (!backup_.WriteTo(predefined_root_, path_.c_str()))
+ LOG(ERROR) << "Failed to restore key in rollback.";
}
diff --git a/chrome/installer/util/delete_reg_key_work_item.h b/chrome/installer/util/delete_reg_key_work_item.h
index 02bc02b..ef92364 100644
--- a/chrome/installer/util/delete_reg_key_work_item.h
+++ b/chrome/installer/util/delete_reg_key_work_item.h
@@ -11,9 +11,12 @@
#include <string>
#include "base/basictypes.h"
-#include "base/memory/scoped_ptr.h"
+#include "base/compiler_specific.h"
+#include "chrome/installer/util/registry_key_backup.h"
#include "chrome/installer/util/work_item.h"
+class RegistryKeyBackup;
+
// A WorkItem subclass that deletes a registry key at the given path. Be aware
// that in the event of rollback the key's values and subkeys are restored but
// the key and its subkeys take on their default security descriptors.
@@ -21,12 +24,11 @@ class DeleteRegKeyWorkItem : public WorkItem {
public:
virtual ~DeleteRegKeyWorkItem();
- virtual bool Do();
+ virtual bool Do() OVERRIDE;
- virtual void Rollback();
+ virtual void Rollback() OVERRIDE;
private:
- class RegKeyBackup;
friend class WorkItem;
DeleteRegKeyWorkItem(HKEY predefined_root, const std::wstring& path);
@@ -39,7 +41,7 @@ class DeleteRegKeyWorkItem : public WorkItem {
std::wstring path_;
// Backup of the deleted key.
- scoped_ptr<RegKeyBackup> backup_;
+ RegistryKeyBackup backup_;
DISALLOW_COPY_AND_ASSIGN(DeleteRegKeyWorkItem);
};
diff --git a/chrome/installer/util/delete_reg_key_work_item_unittest.cc b/chrome/installer/util/delete_reg_key_work_item_unittest.cc
index 827cc86..2127e3d 100644
--- a/chrome/installer/util/delete_reg_key_work_item_unittest.cc
+++ b/chrome/installer/util/delete_reg_key_work_item_unittest.cc
@@ -8,85 +8,59 @@
#include "base/memory/scoped_ptr.h"
#include "base/win/registry.h"
#include "chrome/installer/util/delete_reg_key_work_item.h"
+#include "chrome/installer/util/registry_test_data.h"
#include "chrome/installer/util/work_item.h"
#include "testing/gtest/include/gtest/gtest.h"
using base::win::RegKey;
-namespace {
-wchar_t test_root[] = L"SOFTWARE\\TmpTmp";
-}
-
class DeleteRegKeyWorkItemTest : public testing::Test {
protected:
- virtual void SetUp() {
- // Create a temporary key for testing
- RegKey key(HKEY_CURRENT_USER, L"", KEY_ALL_ACCESS);
- key.DeleteKey(test_root);
- ASSERT_NE(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, test_root, KEY_READ));
- ASSERT_EQ(ERROR_SUCCESS, key.Create(HKEY_CURRENT_USER, test_root,
- KEY_READ));
+ static void TearDownTestCase() {
+ logging::CloseLogFile();
}
- virtual void TearDown() {
- logging::CloseLogFile();
- // Clean up the temporary key
- RegKey key(HKEY_CURRENT_USER, L"", KEY_ALL_ACCESS);
- ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(test_root));
+ virtual void SetUp() {
+ ASSERT_TRUE(test_data_.Initialize(HKEY_CURRENT_USER, L"SOFTWARE\\TmpTmp"));
}
+
+ RegistryTestData test_data_;
};
// Test that deleting a key that doesn't exist succeeds, and that rollback does
// nothing.
TEST_F(DeleteRegKeyWorkItemTest, TestNoKey) {
+ const std::wstring key_paths[] = {
+ std::wstring(test_data_.base_path() + L"\\NoKeyHere"),
+ std::wstring(test_data_.base_path() + L"\\NoKeyHere\\OrHere")
+ };
RegKey key;
- std::wstring key_name(std::wstring(test_root) + L"\\NoKeyHere");
- EXPECT_NE(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, key_name.c_str(),
- KEY_READ));
- scoped_ptr<DeleteRegKeyWorkItem> item(
- WorkItem::CreateDeleteRegKeyWorkItem(HKEY_CURRENT_USER, key_name));
- EXPECT_TRUE(item->Do());
- EXPECT_NE(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, key_name.c_str(),
- KEY_READ));
- item->Rollback();
- item.reset();
- EXPECT_NE(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, key_name.c_str(),
- KEY_READ));
-}
-
-// Test that deleting a subkey of a key that doesn't exist succeeds, and that
-// rollback does nothing.
-TEST_F(DeleteRegKeyWorkItemTest, TestNoSubkey) {
- RegKey key;
- std::wstring key_name(std::wstring(test_root) + L"\\NoKeyHere\\OrHere");
- EXPECT_NE(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, key_name.c_str(),
- KEY_READ));
- scoped_ptr<DeleteRegKeyWorkItem> item(
- WorkItem::CreateDeleteRegKeyWorkItem(HKEY_CURRENT_USER, key_name));
- EXPECT_TRUE(item->Do());
- EXPECT_NE(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, key_name.c_str(),
- KEY_READ));
- item->Rollback();
- item.reset();
- EXPECT_NE(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, key_name.c_str(),
- KEY_READ));
+ for (size_t i = 0; i < arraysize(key_paths); ++i) {
+ const std::wstring& key_path = key_paths[i];
+ scoped_ptr<DeleteRegKeyWorkItem> item(
+ WorkItem::CreateDeleteRegKeyWorkItem(test_data_.root_key(), key_path));
+ EXPECT_TRUE(item->Do());
+ EXPECT_NE(ERROR_SUCCESS, key.Open(test_data_.root_key(), key_path.c_str(),
+ KEY_READ));
+ item->Rollback();
+ item.reset();
+ EXPECT_NE(ERROR_SUCCESS, key.Open(test_data_.root_key(), key_path.c_str(),
+ KEY_READ));
+ }
}
// Test that deleting an empty key succeeds, and that rollback brings it back.
TEST_F(DeleteRegKeyWorkItemTest, TestEmptyKey) {
RegKey key;
- std::wstring key_name(std::wstring(test_root) + L"\\EmptyKey");
- EXPECT_EQ(ERROR_SUCCESS, key.Create(HKEY_CURRENT_USER, key_name.c_str(),
- KEY_WRITE));
- key.Close();
+ const std::wstring& key_path = test_data_.empty_key_path();
scoped_ptr<DeleteRegKeyWorkItem> item(
- WorkItem::CreateDeleteRegKeyWorkItem(HKEY_CURRENT_USER, key_name));
+ WorkItem::CreateDeleteRegKeyWorkItem(test_data_.root_key(), key_path));
EXPECT_TRUE(item->Do());
- EXPECT_NE(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, key_name.c_str(),
+ EXPECT_NE(ERROR_SUCCESS, key.Open(test_data_.root_key(), key_path.c_str(),
KEY_READ));
item->Rollback();
item.reset();
- EXPECT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, key_name.c_str(),
+ EXPECT_EQ(ERROR_SUCCESS, key.Open(test_data_.root_key(), key_path.c_str(),
KEY_READ));
}
@@ -94,29 +68,15 @@ TEST_F(DeleteRegKeyWorkItemTest, TestEmptyKey) {
// brings them all back.
TEST_F(DeleteRegKeyWorkItemTest, TestNonEmptyKey) {
RegKey key;
- std::wstring key_name(std::wstring(test_root) + L"\\NonEmptyKey");
- EXPECT_EQ(ERROR_SUCCESS, key.Create(HKEY_CURRENT_USER, key_name.c_str(),
- KEY_WRITE));
- EXPECT_EQ(ERROR_SUCCESS, key.WriteValue(NULL, key_name.c_str()));
- EXPECT_EQ(ERROR_SUCCESS, key.CreateKey(L"Subkey", KEY_WRITE));
- EXPECT_EQ(ERROR_SUCCESS, key.WriteValue(L"SomeValue", 1U));
- key.Close();
+ const std::wstring& key_path = test_data_.non_empty_key_path();
scoped_ptr<DeleteRegKeyWorkItem> item(
- WorkItem::CreateDeleteRegKeyWorkItem(HKEY_CURRENT_USER, key_name));
+ WorkItem::CreateDeleteRegKeyWorkItem(test_data_.root_key(), key_path));
EXPECT_TRUE(item->Do());
- EXPECT_NE(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, key_name.c_str(),
+ EXPECT_NE(ERROR_SUCCESS, key.Open(test_data_.root_key(), key_path.c_str(),
KEY_READ));
item->Rollback();
item.reset();
- EXPECT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, key_name.c_str(),
- KEY_READ));
- std::wstring str_value;
- EXPECT_EQ(ERROR_SUCCESS, key.ReadValue(NULL, &str_value));
- EXPECT_EQ(key_name, str_value);
- EXPECT_EQ(ERROR_SUCCESS, key.OpenKey(L"Subkey", KEY_READ));
- DWORD dw_value = 0;
- EXPECT_EQ(ERROR_SUCCESS, key.ReadValueDW(L"SomeValue", &dw_value));
- EXPECT_EQ(1U, dw_value);
+ test_data_.ExpectMatchesNonEmptyKey(test_data_.root_key(), key_path.c_str());
}
// Test that deleting a key with subkeys we can't delete fails, and that
@@ -125,8 +85,8 @@ TEST_F(DeleteRegKeyWorkItemTest, TestNonEmptyKey) {
// http://crbug.com/74654
TEST_F(DeleteRegKeyWorkItemTest, DISABLED_TestUndeletableKey) {
RegKey key;
- std::wstring key_name(std::wstring(test_root) + L"\\UndeletableKey");
- EXPECT_EQ(ERROR_SUCCESS, key.Create(HKEY_CURRENT_USER, key_name.c_str(),
+ std::wstring key_name(test_data_.base_path() + L"\\UndeletableKey");
+ EXPECT_EQ(ERROR_SUCCESS, key.Create(test_data_.root_key(), key_name.c_str(),
KEY_WRITE));
EXPECT_EQ(ERROR_SUCCESS, key.WriteValue(NULL, key_name.c_str()));
DWORD dw_value = 1;
@@ -153,13 +113,13 @@ TEST_F(DeleteRegKeyWorkItemTest, DISABLED_TestUndeletableKey) {
subkey.Close();
key.Close();
scoped_ptr<DeleteRegKeyWorkItem> item(
- WorkItem::CreateDeleteRegKeyWorkItem(HKEY_CURRENT_USER, key_name));
+ WorkItem::CreateDeleteRegKeyWorkItem(test_data_.root_key(), key_name));
EXPECT_FALSE(item->Do());
- EXPECT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, key_name.c_str(),
+ EXPECT_EQ(ERROR_SUCCESS, key.Open(test_data_.root_key(), key_name.c_str(),
KEY_QUERY_VALUE));
item->Rollback();
item.reset();
- EXPECT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, key_name.c_str(),
+ EXPECT_EQ(ERROR_SUCCESS, key.Open(test_data_.root_key(), key_name.c_str(),
KEY_QUERY_VALUE));
std::wstring str_value;
EXPECT_EQ(ERROR_SUCCESS, key.ReadValue(NULL, &str_value));
diff --git a/chrome/installer/util/registry_key_backup.cc b/chrome/installer/util/registry_key_backup.cc
new file mode 100644
index 0000000..93734b3
--- /dev/null
+++ b/chrome/installer/util/registry_key_backup.cc
@@ -0,0 +1,321 @@
+// 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 <limits>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/win/registry.h"
+
+using base::win::RegKey;
+
+namespace {
+const REGSAM kKeyReadNoNotify = (KEY_READ) & ~(KEY_NOTIFY);
+} // namespace
+
+// A container for a registry key, its values, and its subkeys.
+class RegistryKeyBackup::KeyData {
+ public:
+ KeyData();
+ ~KeyData();
+ bool Initialize(const RegKey& key);
+ bool WriteTo(RegKey* key) const;
+
+ private:
+ class ValueData;
+
+ scoped_array<ValueData> values_;
+ scoped_array<std::wstring> subkey_names_;
+ scoped_array<KeyData> subkeys_;
+ DWORD num_values_;
+ DWORD num_subkeys_;
+
+ DISALLOW_COPY_AND_ASSIGN(KeyData);
+};
+
+// A container for a registry value.
+class RegistryKeyBackup::KeyData::ValueData {
+ public:
+ ValueData();
+ ~ValueData();
+ 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(ValueData);
+};
+
+RegistryKeyBackup::KeyData::ValueData::ValueData()
+ : type_(REG_NONE) {
+}
+
+RegistryKeyBackup::KeyData::ValueData::~ValueData()
+{
+}
+
+void RegistryKeyBackup::KeyData::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()
+ : num_values_(0),
+ num_subkeys_(0) {
+}
+
+RegistryKeyBackup::KeyData::~KeyData()
+{
+}
+
+// Initializes this object by reading the values and subkeys of |key|.
+// Security descriptors are not backed up.
+bool RegistryKeyBackup::KeyData::Initialize(const RegKey& key) {
+ scoped_array<ValueData> values;
+ scoped_array<std::wstring> subkey_names;
+ scoped_array<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;
+ }
+ 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 ValueData[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(); // Release to heap before new allocation.
+ value_buffer.reset(new uint8[max_value_len]);
+ } else {
+ DCHECK_LT(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(); // Release to heap before new allocation.
+ 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 KeyData[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 RegistryKeyBackup::KeyData::WriteTo(RegKey* key) const {
+ DCHECK(key);
+
+ LONG result = ERROR_SUCCESS;
+
+ // Write the values.
+ for (DWORD i = 0; i < num_values_; ++i) {
+ const ValueData& 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 (DWORD 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;
+}
+
+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;
+}
diff --git a/chrome/installer/util/registry_key_backup.h b/chrome/installer/util/registry_key_backup.h
new file mode 100644
index 0000000..41221bd
--- /dev/null
+++ b/chrome/installer/util/registry_key_backup.h
@@ -0,0 +1,57 @@
+// 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.
+
+#ifndef CHROME_INSTALLER_UTIL_REGISTRY_KEY_BACKUP_H_
+#define CHROME_INSTALLER_UTIL_REGISTRY_KEY_BACKUP_H_
+#pragma once
+
+#include <windows.h>
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace base {
+namespace win {
+class RegKey;
+} // namespace win
+} // namespace base
+
+// 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 RegistryKeyBackup {
+ public:
+ RegistryKeyBackup();
+ ~RegistryKeyBackup();
+
+ // Recursively reads |key_path| into this instance. Backing up a non-existent
+ // key is valid. Returns true if the backup was successful; false otherwise,
+ // in which case the state of this instance is not modified.
+ bool Initialize(HKEY root, const wchar_t* key_path);
+
+ // Writes the contents of this instance into |key|. The contents of
+ // |key_path| are not modified If this instance is uninitialized or was
+ // initialized from a non-existent key.
+ bool WriteTo(HKEY root, const wchar_t* key_path) const;
+
+ void swap(RegistryKeyBackup& other) {
+ key_data_.swap(other.key_data_);
+ }
+
+ private:
+ class KeyData;
+
+ scoped_ptr<KeyData> key_data_;
+
+ DISALLOW_COPY_AND_ASSIGN(RegistryKeyBackup);
+};
+
+#endif // CHROME_INSTALLER_UTIL_REGISTRY_KEY_BACKUP_H_
diff --git a/chrome/installer/util/registry_key_backup_unittest.cc b/chrome/installer/util/registry_key_backup_unittest.cc
new file mode 100644
index 0000000..db33aa4
--- /dev/null
+++ b/chrome/installer/util/registry_key_backup_unittest.cc
@@ -0,0 +1,85 @@
+// 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 <windows.h>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/win/registry.h"
+#include "chrome/installer/util/registry_key_backup.h"
+#include "chrome/installer/util/registry_test_data.h"
+#include "chrome/installer/util/work_item.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::win::RegKey;
+
+class RegistryKeyBackupTest : public testing::Test {
+ protected:
+ static void TearDownTestCase() {
+ logging::CloseLogFile();
+ }
+
+ virtual void SetUp() {
+ ASSERT_TRUE(test_data_.Initialize(HKEY_CURRENT_USER, L"SOFTWARE\\TmpTmp"));
+ destination_path_.assign(test_data_.base_path()).append(L"\\Destination");
+ }
+
+ RegistryTestData test_data_;
+ std::wstring destination_path_;
+};
+
+// Test that writing an uninitialized backup does nothing.
+TEST_F(RegistryKeyBackupTest, Uninitialized) {
+ RegistryKeyBackup backup;
+
+ EXPECT_TRUE(backup.WriteTo(test_data_.root_key(), destination_path_.c_str()));
+ EXPECT_FALSE(RegKey(test_data_.root_key(), destination_path_.c_str(),
+ KEY_READ).Valid());
+}
+
+// Test that initializing a backup with a non-existent key works, and that
+// writing it back out does nothing.
+TEST_F(RegistryKeyBackupTest, MissingKey) {
+ std::wstring non_existent_key_path(test_data_.base_path() + L"\\NoKeyHere");
+ RegistryKeyBackup backup;
+
+ EXPECT_TRUE(backup.Initialize(test_data_.root_key(),
+ non_existent_key_path.c_str()));
+ EXPECT_TRUE(backup.WriteTo(test_data_.root_key(), destination_path_.c_str()));
+ EXPECT_FALSE(RegKey(test_data_.root_key(), destination_path_.c_str(),
+ KEY_READ).Valid());
+}
+
+// Test that reading some data then writing it out does the right thing.
+TEST_F(RegistryKeyBackupTest, ReadWrite) {
+ RegistryKeyBackup backup;
+
+ EXPECT_TRUE(backup.Initialize(test_data_.root_key(),
+ test_data_.non_empty_key_path().c_str()));
+ EXPECT_TRUE(backup.WriteTo(test_data_.root_key(), destination_path_.c_str()));
+ test_data_.ExpectMatchesNonEmptyKey(test_data_.root_key(),
+ destination_path_.c_str());
+}
+
+// Test that reading some data, swapping, then writing it out does the right
+// thing.
+TEST_F(RegistryKeyBackupTest, Swap) {
+ RegistryKeyBackup backup;
+ RegistryKeyBackup other_backup;
+
+ EXPECT_TRUE(backup.Initialize(test_data_.root_key(),
+ test_data_.non_empty_key_path().c_str()));
+ backup.swap(other_backup);
+ EXPECT_TRUE(other_backup.WriteTo(test_data_.root_key(),
+ destination_path_.c_str()));
+
+ // Now make sure the one we started with is truly empty.
+ EXPECT_EQ(ERROR_SUCCESS,
+ RegKey(test_data_.root_key(), L"", KEY_QUERY_VALUE)
+ .DeleteKey(destination_path_.c_str()));
+ EXPECT_TRUE(backup.WriteTo(test_data_.root_key(),
+ destination_path_.c_str()));
+ EXPECT_FALSE(RegKey(test_data_.root_key(), destination_path_.c_str(),
+ KEY_READ).Valid());
+}
diff --git a/chrome/installer/util/registry_test_data.cc b/chrome/installer/util/registry_test_data.cc
new file mode 100644
index 0000000..b28596a
--- /dev/null
+++ b/chrome/installer/util/registry_test_data.cc
@@ -0,0 +1,108 @@
+// 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_test_data.h"
+
+#include "base/logging.h"
+#include "base/win/registry.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::win::RegKey;
+
+RegistryTestData::RegistryTestData()
+ : root_key_(NULL) {
+}
+
+RegistryTestData::~RegistryTestData() {
+ Reset();
+}
+
+// static
+bool RegistryTestData::DeleteKey(HKEY root_key, const wchar_t* path) {
+ LONG result = ERROR_SUCCESS;
+ if (root_key != NULL && path != NULL && *path != L'\0') {
+ result = RegKey(root_key, L"", KEY_QUERY_VALUE).DeleteKey(path);
+ if (result == ERROR_FILE_NOT_FOUND) {
+ result = ERROR_SUCCESS;
+ } else {
+ DLOG_IF(DFATAL, result != ERROR_SUCCESS)
+ << "Failed to delete registry key " << path << ", result: " << result;
+ }
+ }
+ return result == ERROR_SUCCESS;
+}
+
+void RegistryTestData::Reset() {
+ // Clean up behind a previous use (ignore failures).
+ DeleteKey(root_key_, base_path_.c_str());
+
+ // Forget state.
+ root_key_ = NULL;
+ base_path_.clear();
+ empty_key_path_.clear();
+ non_empty_key_path_.clear();
+}
+
+bool RegistryTestData::Initialize(HKEY root_key, const wchar_t* base_path) {
+ LONG result = ERROR_SUCCESS;
+
+ Reset();
+
+ // Take over the new registry location.
+ if (DeleteKey(root_key, base_path)) {
+ // Take on the new values.
+ root_key_ = root_key;
+ base_path_.assign(base_path);
+
+ // Create our data.
+ empty_key_path_.assign(base_path_).append(L"\\EmptyKey");
+ non_empty_key_path_.assign(base_path_).append(L"\\NonEmptyKey");
+
+ RegKey key;
+
+ result = key.Create(root_key_, empty_key_path_.c_str(), KEY_QUERY_VALUE);
+ if (result == ERROR_SUCCESS)
+ result = key.Create(root_key_, non_empty_key_path_.c_str(), KEY_WRITE);
+ if (result == ERROR_SUCCESS)
+ result = key.WriteValue(NULL, non_empty_key_path_.c_str());
+ if (result == ERROR_SUCCESS)
+ result = key.CreateKey(L"SubKey", KEY_WRITE);
+ if (result == ERROR_SUCCESS)
+ result = key.WriteValue(L"SomeValue", 1UL);
+ DLOG_IF(DFATAL, result != ERROR_SUCCESS)
+ << "Failed to create test registry data based at " << base_path_
+ << ", result: " << result;
+ } else {
+ result = ERROR_INVALID_PARAMETER;
+ }
+
+ return result == ERROR_SUCCESS;
+}
+
+void RegistryTestData::ExpectMatchesNonEmptyKey(HKEY root_key,
+ const wchar_t* path) {
+ RegKey key;
+
+ EXPECT_EQ(ERROR_SUCCESS, key.Open(root_key, path, KEY_READ));
+ std::wstring str_value;
+ EXPECT_EQ(ERROR_SUCCESS, key.ReadValue(NULL, &str_value));
+ EXPECT_EQ(non_empty_key_path_, str_value);
+ EXPECT_EQ(ERROR_SUCCESS, key.OpenKey(L"Subkey", KEY_READ));
+ DWORD dw_value = 0;
+ EXPECT_EQ(ERROR_SUCCESS, key.ReadValueDW(L"SomeValue", &dw_value));
+ EXPECT_EQ(1UL, dw_value);
+}
+
+// static
+void RegistryTestData::ExpectEmptyKey(HKEY root_key, const wchar_t* path) {
+ DWORD num_subkeys = 0;
+ DWORD num_values = 0;
+ RegKey key;
+ EXPECT_EQ(ERROR_SUCCESS, key.Open(root_key, path, KEY_READ));
+ EXPECT_EQ(ERROR_SUCCESS,
+ RegQueryInfoKey(key.Handle(), NULL, NULL, NULL, &num_subkeys,
+ NULL, NULL, &num_values, NULL, NULL, NULL, NULL));
+ EXPECT_EQ(0UL, num_subkeys);
+ EXPECT_EQ(0UL, num_values);
+}
diff --git a/chrome/installer/util/registry_test_data.h b/chrome/installer/util/registry_test_data.h
new file mode 100644
index 0000000..cd5e0c4
--- /dev/null
+++ b/chrome/installer/util/registry_test_data.h
@@ -0,0 +1,59 @@
+// 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.
+
+#ifndef CHROME_INSTALLER_UTIL_REGISTRY_TEST_DATA_H_
+#define CHROME_INSTALLER_UTIL_REGISTRY_TEST_DATA_H_
+#pragma once
+
+#include <windows.h>
+
+#include <string>
+
+#include "base/basictypes.h"
+
+// A helper class for use by unit tests that need some registry space and data.
+// BEWARE: Instances of this class irrevocably and recursively delete keys and
+// values from the registry. Carefully read the comments for Initialize and
+// Reset before use.
+class RegistryTestData {
+ public:
+ RegistryTestData();
+ // Invokes Reset() on its way out.
+ ~RegistryTestData();
+
+ // Resets this instance, deletes the key rooted at |base_path|, and then
+ // populates |base_path| with:
+ // \EmptyKey
+ // \NonEmptyKey (default value = "|base_path|\NonEmptyKey")
+ // \NonEmptyKey\Subkey ("SomeValue" = DWORD 1)
+ bool Initialize(HKEY root_key, const wchar_t* base_path);
+
+ // Deletes the key rooted at base_path and clears all state.
+ void Reset();
+
+ // Fires Google Test expectations in the hopes that |path| contains the same
+ // data as originally placed in |non_empty_key| by Initialize().
+ void ExpectMatchesNonEmptyKey(HKEY root_key, const wchar_t* path);
+
+ HKEY root_key() const { return root_key_; }
+ const std::wstring& base_path() const { return base_path_; }
+ const std::wstring& empty_key_path() const { return empty_key_path_; }
+ const std::wstring& non_empty_key_path() const { return non_empty_key_path_; }
+
+ // Fires Google Test expectations in the hopes that |path| is an empty key
+ // (exists but has no values or subkeys).
+ static void ExpectEmptyKey(HKEY root_key, const wchar_t* path);
+
+ private:
+ static bool DeleteKey(HKEY root_key, const wchar_t* path);
+
+ HKEY root_key_;
+ std::wstring base_path_;
+ std::wstring empty_key_path_;
+ std::wstring non_empty_key_path_;
+
+ DISALLOW_COPY_AND_ASSIGN(RegistryTestData);
+};
+
+#endif // CHROME_INSTALLER_UTIL_REGISTRY_TEST_DATA_H_
diff --git a/chrome/installer/util/work_item.cc b/chrome/installer/util/work_item.cc
index 8a047b2..e4982f8 100644
--- a/chrome/installer/util/work_item.cc
+++ b/chrome/installer/util/work_item.cc
@@ -5,6 +5,7 @@
#include "chrome/installer/util/work_item.h"
#include "chrome/installer/util/conditional_work_item_list.h"
+#include "chrome/installer/util/copy_reg_key_work_item.h"
#include "chrome/installer/util/copy_tree_work_item.h"
#include "chrome/installer/util/create_dir_work_item.h"
#include "chrome/installer/util/create_reg_key_work_item.h"
@@ -22,6 +23,14 @@ WorkItem::WorkItem() : ignore_failure_(false) {
WorkItem::~WorkItem() {
}
+CopyRegKeyWorkItem* WorkItem::CreateCopyRegKeyWorkItem(
+ HKEY predefined_root,
+ const std::wstring& source_key_path,
+ const std::wstring& dest_key_path) {
+ return new CopyRegKeyWorkItem(predefined_root, source_key_path,
+ dest_key_path);
+}
+
CopyTreeWorkItem* WorkItem::CreateCopyTreeWorkItem(
const FilePath& source_path,
const FilePath& dest_path,
diff --git a/chrome/installer/util/work_item.h b/chrome/installer/util/work_item.h
index a4c8f6e..cbe9665 100644
--- a/chrome/installer/util/work_item.h
+++ b/chrome/installer/util/work_item.h
@@ -17,6 +17,7 @@
#include "base/basictypes.h"
+class CopyRegKeyWorkItem;
class CopyTreeWorkItem;
class CreateDirWorkItem;
class CreateRegKeyWorkItem;
@@ -58,6 +59,12 @@ class WorkItem {
virtual ~WorkItem();
+ // Create a CopyRegKeyWorkItem that recursively copies a given registry key.
+ static CopyRegKeyWorkItem* CreateCopyRegKeyWorkItem(
+ HKEY predefined_root,
+ const std::wstring& source_key_path,
+ const std::wstring& dest_key_path);
+
// Create a CopyTreeWorkItem that recursively copies a file system hierarchy
// from source path to destination path.
// * If overwrite_option is ALWAYS, the created CopyTreeWorkItem always
diff --git a/chrome/installer/util/work_item_list.cc b/chrome/installer/util/work_item_list.cc
index d32f068..23ef381 100644
--- a/chrome/installer/util/work_item_list.cc
+++ b/chrome/installer/util/work_item_list.cc
@@ -7,6 +7,7 @@
#include "base/logging.h"
#include "base/file_path.h"
#include "chrome/installer/util/logging_installer.h"
+#include "chrome/installer/util/copy_reg_key_work_item.h"
#include "chrome/installer/util/copy_tree_work_item.h"
#include "chrome/installer/util/create_dir_work_item.h"
#include "chrome/installer/util/create_reg_key_work_item.h"
@@ -72,6 +73,16 @@ void WorkItemList::AddWorkItem(WorkItem* work_item) {
list_.push_back(work_item);
}
+WorkItem* WorkItemList::AddCopyRegKeyWorkItem(
+ HKEY predefined_root,
+ const std::wstring& source_key_path,
+ const std::wstring& dest_key_path) {
+ WorkItem* item = WorkItem::CreateCopyRegKeyWorkItem(
+ predefined_root, source_key_path, dest_key_path);
+ AddWorkItem(item);
+ return item;
+}
+
WorkItem* WorkItemList::AddCopyTreeWorkItem(
const std::wstring& source_path,
const std::wstring& dest_path,
diff --git a/chrome/installer/util/work_item_list.h b/chrome/installer/util/work_item_list.h
index f1d7c8f..e73a1ac 100644
--- a/chrome/installer/util/work_item_list.h
+++ b/chrome/installer/util/work_item_list.h
@@ -37,6 +37,11 @@ class WorkItemList : public WorkItem {
// Once a WorkItem is added to the list. The list owns the WorkItem.
virtual void AddWorkItem(WorkItem* work_item);
+ // Add a CopyRegKeyWorkItem that recursively copies a given registry key.
+ virtual WorkItem* AddCopyRegKeyWorkItem(HKEY predefined_root,
+ const std::wstring& source_key_path,
+ const std::wstring& dest_key_path);
+
// Add a CopyTreeWorkItem to the list of work items.
// See the NOTE in the documentation for the CopyTreeWorkItem class for
// special considerations regarding |temp_dir|.
@@ -102,11 +107,11 @@ class WorkItemList : public WorkItem {
// Add a SetRegValueWorkItem that sets a registry value with REG_QWORD type
// at the key with specified path.
- WorkItem* AddSetRegValueWorkItem(HKEY predefined_root,
- const std::wstring& key_path,
- const std::wstring& value_name,
- int64 value_data,
- bool overwrite);
+ virtual WorkItem* AddSetRegValueWorkItem(HKEY predefined_root,
+ const std::wstring& key_path,
+ const std::wstring& value_name,
+ int64 value_data,
+ bool overwrite);
// Add a SelfRegWorkItem that registers or unregisters a DLL at the
// specified path. If user_level_registration is true, then alternate