You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
319 lines
10 KiB
319 lines
10 KiB
//
|
|
// REGDKEY.C
|
|
//
|
|
// Copyright (C) Microsoft Corporation, 1995
|
|
//
|
|
// Implementation of RegDeleteKey and supporting functions.
|
|
//
|
|
|
|
#include "pch.h"
|
|
|
|
//
|
|
// RgFreeDatablockStructures
|
|
//
|
|
// Helper routine for RgDeleteKey. Deletes the specified datablock structures.
|
|
// The datablock is not assumed to be locked. We don't care about the success
|
|
// of this routine-- in the worst case, some stuff will be orphaned in the
|
|
// file.
|
|
//
|
|
|
|
VOID
|
|
INTERNAL
|
|
RgFreeDatablockStructures(
|
|
LPFILE_INFO lpFileInfo,
|
|
UINT BlockIndex,
|
|
UINT KeyRecordIndex
|
|
)
|
|
{
|
|
|
|
LPDATABLOCK_INFO lpDatablockInfo;
|
|
LPKEY_RECORD lpKeyRecord;
|
|
|
|
if (RgLockKeyRecord(lpFileInfo, BlockIndex, (BYTE) KeyRecordIndex,
|
|
&lpKeyRecord) == ERROR_SUCCESS) {
|
|
lpDatablockInfo = RgIndexDatablockInfoPtr(lpFileInfo, BlockIndex);
|
|
RgFreeKeyRecord(lpDatablockInfo, lpKeyRecord);
|
|
RgFreeKeyRecordIndex(lpDatablockInfo, KeyRecordIndex);
|
|
RgUnlockDatablock(lpFileInfo, BlockIndex, TRUE);
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// RgDeleteKey
|
|
//
|
|
// Worker routine for VMMRegDeleteKey. The given key handle references a key
|
|
// that has already been validated as "deleteable".
|
|
//
|
|
|
|
int
|
|
INTERNAL
|
|
RgDeleteKey(
|
|
HKEY hKey
|
|
)
|
|
{
|
|
|
|
int ErrorCode;
|
|
LPFILE_INFO lpFileInfo;
|
|
DWORD KeynodeIndex;
|
|
LPKEYNODE lpKeynode;
|
|
DWORD NextKeynodeIndex;
|
|
LPKEYNODE lpNextKeynode;
|
|
DWORD ReplacementKeynodeIndex;
|
|
HKEY hTempKey;
|
|
|
|
lpFileInfo = hKey-> lpFileInfo;
|
|
|
|
//
|
|
// Stage one: unlink the keynode of the specified key from the keynode
|
|
// tree and free all associate file structures with the key.
|
|
//
|
|
|
|
if ((ErrorCode = RgLockInUseKeynode(lpFileInfo, hKey-> KeynodeIndex,
|
|
&lpKeynode)) != ERROR_SUCCESS)
|
|
return ErrorCode;
|
|
|
|
KeynodeIndex = lpKeynode-> ParentIndex;
|
|
ReplacementKeynodeIndex = lpKeynode-> NextIndex;
|
|
RgUnlockKeynode(lpFileInfo, hKey-> KeynodeIndex, FALSE);
|
|
|
|
// Signal any waiting notifies on the parent that this key is about to be
|
|
// deleted.
|
|
//
|
|
// Note that we may fail below, but NT does _exactly_ the same thing in
|
|
// this case: doesn't care. If we get an error and don't actually delete
|
|
// this key, then we'll have sent a spurious notify.
|
|
//
|
|
// Note also that we don't send any notification that the key itself has
|
|
// been deleted. REG_NOTIFY_CHANGE_NAME is supposed to be for subkey
|
|
// changes only, not changes to the key itself. But because of the
|
|
// incompatible way we must deal with subkeys of the key we're about to
|
|
// delete, we may well end up notifying the key if it has subkeys.
|
|
RgSignalWaitingNotifies(lpFileInfo, KeynodeIndex, REG_NOTIFY_CHANGE_NAME);
|
|
|
|
if ((ErrorCode = RgLockInUseKeynode(lpFileInfo, KeynodeIndex,
|
|
&lpKeynode)) != ERROR_SUCCESS)
|
|
return ErrorCode;
|
|
|
|
// The per-key cache that we use for RegEnumKey may be invalid, so it must
|
|
// be zapped.
|
|
if (!IsNullPtr(hTempKey = RgFindOpenKeyHandle(lpFileInfo, KeynodeIndex)))
|
|
hTempKey-> Flags &= ~KEYF_ENUMKEYCACHED;
|
|
|
|
NextKeynodeIndex = lpKeynode-> ChildIndex;
|
|
|
|
if (NextKeynodeIndex == hKey-> KeynodeIndex) {
|
|
|
|
// Update the cached child keynode index in the open handle on the
|
|
// parent.
|
|
if (!IsNullPtr(hTempKey))
|
|
hTempKey-> ChildKeynodeIndex = ReplacementKeynodeIndex;
|
|
|
|
// This is the parent of the keynode that we need to delete. Replace
|
|
// it's "child" link.
|
|
lpKeynode-> ChildIndex = ReplacementKeynodeIndex;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Loop through the siblings of the keynode we're trying to delete.
|
|
do {
|
|
|
|
RgUnlockKeynode(lpFileInfo, KeynodeIndex, FALSE);
|
|
KeynodeIndex = NextKeynodeIndex;
|
|
|
|
if (IsNullKeynodeIndex(KeynodeIndex)) {
|
|
DEBUG_OUT(("RgDeleteKey: couldn't find the keynode to delete\n"));
|
|
return ERROR_BADDB;
|
|
}
|
|
|
|
if ((ErrorCode = RgLockInUseKeynode(lpFileInfo, KeynodeIndex,
|
|
&lpKeynode)) != ERROR_SUCCESS)
|
|
return ErrorCode;
|
|
|
|
NextKeynodeIndex = lpKeynode-> NextIndex;
|
|
|
|
} while (NextKeynodeIndex != hKey-> KeynodeIndex);
|
|
|
|
// This is the previous sibling of the keynode that we need to delete.
|
|
// Replace it's "next" link.
|
|
lpKeynode-> NextIndex = ReplacementKeynodeIndex;
|
|
|
|
}
|
|
|
|
// Unlock the updated "parent" or "next" of this keynode.
|
|
RgUnlockKeynode(lpFileInfo, KeynodeIndex, TRUE);
|
|
|
|
// Free the structures associated with the datablock.
|
|
RgFreeDatablockStructures(lpFileInfo, hKey-> BlockIndex, hKey->
|
|
KeyRecordIndex);
|
|
|
|
// Free the structures associated with the keynode tables.
|
|
RgFreeKeynode(lpFileInfo, hKey-> KeynodeIndex);
|
|
|
|
// The key is definitely toast now.
|
|
hKey-> Flags |= KEYF_DELETED;
|
|
|
|
//
|
|
// Stage two: the specified key is unlinked, but any of its subkeys now
|
|
// have to be freed. Errors are ignored at this point: we won't try to
|
|
// undo the stuff we did in stage one. The worst thing that can happen is
|
|
// that some file structures are orphaned.
|
|
//
|
|
|
|
NextKeynodeIndex = hKey-> ChildKeynodeIndex;
|
|
|
|
if (IsNullKeynodeIndex(NextKeynodeIndex) || RgLockInUseKeynode(lpFileInfo,
|
|
NextKeynodeIndex, &lpNextKeynode) != ERROR_SUCCESS)
|
|
return ERROR_SUCCESS;
|
|
|
|
while (!IsNullKeynodeIndex(NextKeynodeIndex)) {
|
|
|
|
KeynodeIndex = NextKeynodeIndex;
|
|
lpKeynode = lpNextKeynode;
|
|
|
|
// Check if the keynode has any children. If it does and we can lock
|
|
// it down, then move to it.
|
|
NextKeynodeIndex = lpKeynode-> ChildIndex;
|
|
|
|
if (!IsNullKeynodeIndex(NextKeynodeIndex) &&
|
|
RgLockInUseKeynode(lpFileInfo, NextKeynodeIndex, &lpNextKeynode) ==
|
|
ERROR_SUCCESS) {
|
|
|
|
ASSERT(KeynodeIndex == lpNextKeynode-> ParentIndex);
|
|
|
|
RgYield();
|
|
|
|
// "Burn" the link to our child, so that on the way back out of
|
|
// the tree, we don't end up recursing. Plus, if we hit any errors
|
|
// deep in the tree deletion, the child of the current keynode
|
|
// could have already been toasted, so we have to zap our link to
|
|
// it.
|
|
lpKeynode-> ChildIndex = REG_NULL;
|
|
RgUnlockKeynode(lpFileInfo, KeynodeIndex, TRUE);
|
|
|
|
// We've now caused a change in the subkeys of the current key.
|
|
// Note that we don't bother signaling notifies that are doing a
|
|
// subtree watch because any such notifies should have already been
|
|
// signaled by the above call or they've already been signaled
|
|
// during our recursion. In the off chance that we have a lot of
|
|
// notifications registered, this will avoid a lot of unnecessary
|
|
// checking.
|
|
RgSignalWaitingNotifies(lpFileInfo, KeynodeIndex,
|
|
REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_NO_WATCH_SUBTREE);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// The keynode doesn't have any children. Check for sibling keynodes.
|
|
NextKeynodeIndex = lpKeynode-> NextIndex;
|
|
|
|
if (IsNullKeynodeIndex(NextKeynodeIndex) ||
|
|
RgLockInUseKeynode(lpFileInfo, NextKeynodeIndex, &lpNextKeynode) !=
|
|
ERROR_SUCCESS) {
|
|
|
|
// The keynode doesn't have any siblings or we were unable to get
|
|
// at them. Move back to the parent.
|
|
NextKeynodeIndex = lpKeynode-> ParentIndex;
|
|
|
|
// If we wrapped back up to the top of the deleted branch or if we
|
|
// just can't access the parent keynode, then set next to REG_NULL
|
|
// and bail out on the next iteration.
|
|
if ((NextKeynodeIndex == hKey-> KeynodeIndex) ||
|
|
RgLockInUseKeynode(lpFileInfo, NextKeynodeIndex,
|
|
&lpNextKeynode) != ERROR_SUCCESS)
|
|
NextKeynodeIndex = REG_NULL;
|
|
|
|
}
|
|
|
|
// If an open key refers to this file and keynode index, mark it as
|
|
// deleted.
|
|
if (!IsNullPtr(hTempKey = RgFindOpenKeyHandle(lpFileInfo,
|
|
KeynodeIndex)))
|
|
hTempKey-> Flags |= KEYF_DELETED;
|
|
|
|
// Free the structures associated with the datablock.
|
|
RgFreeDatablockStructures(lpFileInfo, lpKeynode-> BlockIndex,
|
|
(BYTE) lpKeynode-> KeyRecordIndex);
|
|
|
|
// Free the structures associated with the keynode tables.
|
|
RgUnlockKeynode(lpFileInfo, KeynodeIndex, TRUE);
|
|
RgFreeKeynode(lpFileInfo, KeynodeIndex);
|
|
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|
|
|
|
//
|
|
// VMMRegDeleteKey
|
|
//
|
|
// See Win32 documentation for a description of the behavior.
|
|
//
|
|
// Although the Win32 documentation states that lpSubKey must be NULL, NT
|
|
// actually allows this to pass through. Win95 rejected the call, but the only
|
|
// reason we didn't change it then was because we realized too late in the
|
|
// product that it was different.
|
|
//
|
|
|
|
LONG
|
|
REGAPI
|
|
VMMRegDeleteKey(
|
|
HKEY hKey,
|
|
LPCSTR lpSubKey
|
|
)
|
|
{
|
|
|
|
LONG ErrorCode;
|
|
HKEY hSubKey;
|
|
|
|
if (IsNullPtr(lpSubKey))
|
|
{
|
|
if (IsNullPtr(hKey))
|
|
return ERROR_BADKEY;
|
|
else
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ((ErrorCode = VMMRegOpenKey(hKey, lpSubKey, &hSubKey)) != ERROR_SUCCESS)
|
|
return ErrorCode;
|
|
|
|
// Don't allow HKEY_LOCAL_MACHINE or HKEY_USERS to be deleted.
|
|
if (hSubKey == &g_RgLocalMachineKey || hSubKey == &g_RgUsersKey)
|
|
{
|
|
if (*lpSubKey == '\0')
|
|
{
|
|
ErrorCode = ERROR_ACCESS_DENIED;
|
|
goto SkipDelete;
|
|
}
|
|
else if (lpSubKey[0] == '\\' && lpSubKey[1] == '\0')
|
|
{
|
|
ErrorCode = ERROR_CANTOPEN16_FILENOTFOUND32;
|
|
goto SkipDelete;
|
|
}
|
|
}
|
|
|
|
if (!RgLockRegistry())
|
|
ErrorCode = ERROR_LOCK_FAILED;
|
|
|
|
else {
|
|
if (IsKeyRootOfHive(hSubKey) || (hSubKey-> lpFileInfo-> Flags &
|
|
FI_READONLY))
|
|
ErrorCode = ERROR_ACCESS_DENIED;
|
|
else
|
|
ErrorCode = RgDeleteKey(hSubKey);
|
|
|
|
RgUnlockRegistry();
|
|
|
|
}
|
|
|
|
SkipDelete:
|
|
|
|
VMMRegCloseKey(hSubKey);
|
|
|
|
return ErrorCode;
|
|
|
|
}
|