windows-nt/Source/XPSP1/NT/base/ntsetup/win95upg/common/regw32d/regdkey.c
2020-09-26 16:20:57 +08:00

320 lines
9.8 KiB
C

//
// 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;
}