1// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5// Implementation of a work item that replaces the contents of one registry key 6// with that of another (i.e., the destination is erased prior to the copy). 7 8#include "chrome/installer/util/copy_reg_key_work_item.h" 9 10#include <shlwapi.h> 11 12#include "base/logging.h" 13#include "base/win/registry.h" 14 15using base::win::RegKey; 16 17CopyRegKeyWorkItem::~CopyRegKeyWorkItem() { 18} 19 20CopyRegKeyWorkItem::CopyRegKeyWorkItem(HKEY predefined_root, 21 const std::wstring& source_key_path, 22 const std::wstring& dest_key_path, 23 CopyOverWriteOption overwrite_option) 24 : predefined_root_(predefined_root), 25 source_key_path_(source_key_path), 26 dest_key_path_(dest_key_path), 27 overwrite_option_(overwrite_option), 28 cleared_destination_(false) { 29 DCHECK(predefined_root); 30 // It's a safe bet that we don't want to copy or overwrite one of the root 31 // trees. 32 DCHECK(!source_key_path.empty()); 33 DCHECK(!dest_key_path.empty()); 34 DCHECK(overwrite_option == ALWAYS || overwrite_option == IF_NOT_PRESENT); 35} 36 37bool CopyRegKeyWorkItem::Do() { 38 if (source_key_path_.empty() || dest_key_path_.empty()) 39 return false; 40 41 // Leave immediately if we're not supposed to overwrite an existing key and 42 // one is there. 43 if (overwrite_option_ == IF_NOT_PRESENT && 44 RegKey(predefined_root_, dest_key_path_.c_str(), 45 KEY_QUERY_VALUE).Valid()) { 46 return true; 47 } 48 49 RegistryKeyBackup backup; 50 RegKey dest_key; 51 52 // Only try to make a backup if we're not configured to ignore failures. 53 if (!ignore_failure_) { 54 if (!backup.Initialize(predefined_root_, dest_key_path_.c_str())) { 55 LOG(ERROR) << "Failed to backup destination for registry key copy."; 56 return false; 57 } 58 } 59 60 // Delete the destination before attempting to copy. 61 LONG result = SHDeleteKey(predefined_root_, dest_key_path_.c_str()); 62 if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) { 63 LOG(ERROR) << "Failed to delete key at " << dest_key_path_ << ", result: " 64 << result; 65 } else { 66 cleared_destination_ = true; 67 // We've just modified the registry, so remember any backup we may have 68 // made so that Rollback can take us back where we started. 69 backup_.swap(backup); 70 // Make the copy. 71 result = dest_key.Create(predefined_root_, dest_key_path_.c_str(), 72 KEY_WRITE); 73 if (result != ERROR_SUCCESS) { 74 LOG(ERROR) << "Failed to open destination key at " << dest_key_path_ 75 << ", result: " << result; 76 } else { 77 result = SHCopyKey(predefined_root_, source_key_path_.c_str(), 78 dest_key.Handle(), 0); 79 switch (result) { 80 case ERROR_FILE_NOT_FOUND: 81 // The source didn't exist, so neither should the destination. 82 dest_key.Close(); 83 SHDeleteKey(predefined_root_, dest_key_path_.c_str()); 84 // Handle like a success. 85 result = ERROR_SUCCESS; 86 // -- Fall through to success case. -- 87 case ERROR_SUCCESS: 88 break; 89 default: 90 LOG(ERROR) << "Failed to copy key from " << source_key_path_ << " to " 91 << dest_key_path_ << ", result: " << result; 92 break; 93 } 94 } 95 } 96 97 return ignore_failure_ ? true : (result == ERROR_SUCCESS); 98} 99 100void CopyRegKeyWorkItem::Rollback() { 101 if (ignore_failure_) 102 return; 103 104 if (cleared_destination_) { 105 // Delete anything in the key before restoring the backup in case new data 106 // was written after Do(). 107 LONG result = SHDeleteKey(predefined_root_, dest_key_path_.c_str()); 108 if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) { 109 LOG(ERROR) << "Failed to delete key at " << dest_key_path_ 110 << " in rollback, result: " << result; 111 } 112 113 // Restore the old contents. The restoration takes on its default security 114 // attributes; any custom attributes are lost. 115 if (!backup_.WriteTo(predefined_root_, dest_key_path_.c_str())) 116 LOG(ERROR) << "Failed to restore key in rollback."; 117 } 118} 119