windows-nt/Source/XPSP1/NT/sdktools/vmmreg32/regkey.c
2020-09-26 16:20:57 +08:00

580 lines
15 KiB
C

//
// REGKEY.C
//
// Copyright (C) Microsoft Corporation, 1995
//
// Implementation of RegCreateKey, RegOpenKey, RegCloseKey, and supporting
// functions.
//
#include "pch.h"
//
// RgIsBadSubKey
//
// Returns TRUE if lpSubKey is a invalid subkey string. An invalid subkey
// string may be an invalid pointer or contain double-backslashes or elements
// greater than MAXIMUM_SUB_KEY_LENGTH.
//
BOOL
INTERNAL
RgIsBadSubKey(
LPCSTR lpSubKey
)
{
LPCSTR lpString;
UINT SubSubKeyLength;
BYTE Char;
if (IsNullPtr(lpSubKey))
return FALSE;
if (!IsBadStringPtr(lpSubKey, (UINT) -1)) {
lpString = lpSubKey;
SubSubKeyLength = 0;
while (TRUE) {
Char = *((LPBYTE) lpString);
if (Char == '\0')
return FALSE;
else if (Char == '\\') {
// Catch double-backslashes and leading backslashes. One
// leading backslash is acceptable...
if (SubSubKeyLength == 0 && lpString != lpSubKey)
break;
SubSubKeyLength = 0;
}
else {
if (IsDBCSLeadByte(Char)) {
SubSubKeyLength++;
// Catch an unpaired DBCS pair...
if (*lpString++ == '\0')
break;
}
// Win95 compatibility: don't accept strings with control
// characters.
else if (Char < ' ')
break;
if (++SubSubKeyLength >= MAXIMUM_SUB_KEY_LENGTH)
break;
}
lpString++;
}
}
return TRUE;
}
//
// RgGetNextSubSubKey
//
// Extracts the next subkey component tokenized by backslashes. Works like
// strtok where on the first call, lpSubKey points to the start of the subkey.
// On subsequent calls, lpSubKey is NULL and the last offset is used to find
// the next component.
//
// Returns the length of the SubSubKey string.
//
UINT
INTERNAL
RgGetNextSubSubKey(
LPCSTR lpSubKey,
LPCSTR FAR* lplpSubSubKey,
UINT FAR* lpSubSubKeyLength
)
{
static LPCSTR lpLastSubSubKey = NULL;
LPCSTR lpString;
UINT SubSubKeyLength;
if (!IsNullPtr(lpSubKey))
lpLastSubSubKey = lpSubKey;
lpString = lpLastSubSubKey;
if (*lpString == '\0') {
*lplpSubSubKey = NULL;
*lpSubSubKeyLength = 0;
return 0;
}
if (*lpString == '\\')
lpString++;
*lplpSubSubKey = lpString;
while (*lpString != '\0') {
if (*lpString == '\\')
break;
// The subkey has already been validated, so we know there's a matching
// trail byte.
if (IsDBCSLeadByte(*lpString))
lpString++; // Trail byte skipped immediately below
lpString++;
}
lpLastSubSubKey = lpString;
SubSubKeyLength = lpString - *lplpSubSubKey;
*lpSubSubKeyLength = SubSubKeyLength;
return SubSubKeyLength;
}
//
// RgLookupKey
//
int
INTERNAL
RgLookupKey(
HKEY hKey,
LPCSTR lpSubKey,
LPHKEY lphSubKey,
UINT Flags
)
{
int ErrorCode;
LPCSTR lpSubSubKey;
UINT SubSubKeyLength;
BOOL fCreatedKeynode;
LPFILE_INFO lpFileInfo;
DWORD KeynodeIndex;
#ifdef WANT_HIVE_SUPPORT
LPHIVE_INFO lpHiveInfo;
#endif
BOOL fPrevIsNextIndex;
DWORD SubSubKeyHash;
LPKEYNODE lpKeynode;
LPKEY_RECORD lpKeyRecord;
BOOL fFound;
DWORD PrevKeynodeIndex;
#ifdef WANT_NOTIFY_CHANGE_SUPPORT
DWORD NotifyKeynodeIndex;
#endif
LPKEYNODE lpNewKeynode;
HKEY hSubKey;
fCreatedKeynode = FALSE;
//
// Check if the caller is trying to open a key with a NULL or zero-length
// sub key string. If so, simply return hKey.
//
if (IsNullPtr(lpSubKey) || RgGetNextSubSubKey(lpSubKey, &lpSubSubKey,
&SubSubKeyLength) == 0) {
hSubKey = hKey;
goto HaveSubKeyHandle;
}
lpFileInfo = hKey-> lpFileInfo;
KeynodeIndex = hKey-> ChildKeynodeIndex;
PrevKeynodeIndex = hKey-> KeynodeIndex;
#ifdef WANT_HIVE_SUPPORT
//
// If this key can have hives attached to it, check there for the first
// part of the subkey. If we have a match, then switch into that
// FILE_INFO.
//
if (hKey-> Flags & KEYF_HIVESALLOWED) {
lpHiveInfo = lpFileInfo-> lpHiveInfoList;
while (!IsNullPtr(lpHiveInfo)) {
if (SubSubKeyLength == lpHiveInfo-> NameLength &&
RgStrCmpNI(lpSubSubKey, lpHiveInfo-> Name,
SubSubKeyLength) == 0) {
lpFileInfo = lpHiveInfo-> lpFileInfo;
KeynodeIndex = lpFileInfo-> KeynodeHeader.RootIndex;
if ((ErrorCode = RgLockInUseKeynode(lpFileInfo, KeynodeIndex,
&lpKeynode)) != ERROR_SUCCESS)
return ErrorCode;
if (!RgGetNextSubSubKey(NULL, &lpSubSubKey, &SubSubKeyLength))
goto LookupComplete;
PrevKeynodeIndex = KeynodeIndex;
KeynodeIndex = lpKeynode-> ChildIndex;
RgUnlockKeynode(lpFileInfo, PrevKeynodeIndex, FALSE);
break;
}
lpHiveInfo = lpHiveInfo-> lpNextHiveInfo;
}
}
#endif
//
// Walk as deep as we can into the registry tree using existing key
// records. For each subkey component, move to the child of the current
// tree position and walk each sibling looking for a match. Repeat until
// we're out of subkey components or we hit the end of a branch.
//
fPrevIsNextIndex = FALSE;
for (;;) {
SubSubKeyHash = RgHashString(lpSubSubKey, SubSubKeyLength);
while (!IsNullKeynodeIndex(KeynodeIndex)) {
if ((ErrorCode = RgLockInUseKeynode(lpFileInfo, KeynodeIndex,
&lpKeynode)) != ERROR_SUCCESS)
return ErrorCode;
if (lpKeynode-> Hash == SubSubKeyHash) {
if ((ErrorCode = RgLockKeyRecord(lpFileInfo, lpKeynode->
BlockIndex, (BYTE) lpKeynode-> KeyRecordIndex,
&lpKeyRecord)) != ERROR_SUCCESS) {
RgUnlockKeynode(lpFileInfo, KeynodeIndex, FALSE);
return ErrorCode;
}
fFound = (SubSubKeyLength == lpKeyRecord-> NameLength &&
RgStrCmpNI(lpSubSubKey, lpKeyRecord-> Name,
SubSubKeyLength) == 0);
RgUnlockDatablock(lpFileInfo, lpKeynode-> BlockIndex, FALSE);
if (fFound)
break;
}
// Unlock the current keynode and advance to its sibling. Set
// fPrevIsNextIndex so that if we have to create, we know that
// we'll be inserting the new keynode as a sibling.
fPrevIsNextIndex = TRUE;
PrevKeynodeIndex = KeynodeIndex;
KeynodeIndex = lpKeynode-> NextIndex;
RgUnlockKeynode(lpFileInfo, PrevKeynodeIndex, FALSE);
}
// Break out if we looped over all the siblings of the previous keynode
// or if the previous keynode didn't have any children. If we're in
// create mode, then fPrevIsNextIndex and PrevKeynodeIndex will
// represent where we need to start inserting.
if (IsNullKeynodeIndex(KeynodeIndex))
break;
// Break out there are no more subkey components to lookup.
// KeynodeIndex represents the index of the matching key. It's
// corresponding keynode is locked.
if (!RgGetNextSubSubKey(NULL, &lpSubSubKey, &SubSubKeyLength))
break;
// Unlock the current keynode and advance to its child. Clear
// fPrevIsNextIndex so that if we have to create, we know that we'll
// be inserting the new keynode as a child.
fPrevIsNextIndex = FALSE;
PrevKeynodeIndex = KeynodeIndex;
KeynodeIndex = lpKeynode-> ChildIndex;
RgUnlockKeynode(lpFileInfo, PrevKeynodeIndex, FALSE);
}
if (IsNullKeynodeIndex(KeynodeIndex)) {
if (!(Flags & LK_CREATE))
return ERROR_CANTOPEN16_FILENOTFOUND32;
if (((hKey-> PredefinedKeyIndex == INDEX_DYN_DATA) && !(Flags &
LK_CREATEDYNDATA)) || (lpFileInfo-> Flags & FI_READONLY))
return ERROR_ACCESS_DENIED;
if ((ErrorCode = RgLockInUseKeynode(lpFileInfo, PrevKeynodeIndex,
&lpKeynode)) != ERROR_SUCCESS) {
TRACE(("RgLookupKey: failed to lock keynode we just had?\n"));
return ErrorCode;
}
#ifdef WANT_NOTIFY_CHANGE_SUPPORT
// Which keynode index we'll notify of the subkeys we're creating
// depends on the state of fPrevIsNextIndex.
NotifyKeynodeIndex = fPrevIsNextIndex ? lpKeynode-> ParentIndex :
PrevKeynodeIndex;
#endif
// See if there's an open handle on the parent so that we can patch up
// its child keynode index member. We only need this on the first
// pass.
hSubKey = RgFindOpenKeyHandle(lpFileInfo, PrevKeynodeIndex);
do {
if ((ErrorCode = RgAllocKeynode(lpFileInfo, &KeynodeIndex,
&lpNewKeynode)) != ERROR_SUCCESS)
goto CreateAllocFailed1;
if ((ErrorCode = RgAllocKeyRecord(lpFileInfo, sizeof(KEY_RECORD) +
SubSubKeyLength - 1, &lpKeyRecord)) != ERROR_SUCCESS) {
RgUnlockKeynode(lpFileInfo, KeynodeIndex, FALSE);
RgFreeKeynode(lpFileInfo, KeynodeIndex);
CreateAllocFailed1:
RgUnlockKeynode(lpFileInfo, PrevKeynodeIndex, fCreatedKeynode);
DEBUG_OUT(("RgLookupKey: allocation failed\n"));
goto SignalAndReturnErrorCode;
}
// Fixup the previous keynode's next offset.
if (fPrevIsNextIndex) {
fPrevIsNextIndex = FALSE;
hSubKey = NULL;
lpNewKeynode-> ParentIndex = lpKeynode-> ParentIndex;
lpKeynode-> NextIndex = KeynodeIndex;
}
// Fixup the previous keynode's child offset.
else {
lpNewKeynode-> ParentIndex = PrevKeynodeIndex;
lpKeynode-> ChildIndex = KeynodeIndex;
// If hSubKey is not NULL, then we may have to patch up the
// child offset cache to point to the newly created keynode.
if (!IsNullPtr(hSubKey)) {
if (IsNullKeynodeIndex(hSubKey-> ChildKeynodeIndex))
hSubKey-> ChildKeynodeIndex = KeynodeIndex;
hSubKey = NULL;
}
}
// Fill in the keynode.
lpNewKeynode-> NextIndex = REG_NULL;
lpNewKeynode-> ChildIndex = REG_NULL;
lpNewKeynode-> BlockIndex = lpKeyRecord-> BlockIndex;
lpNewKeynode-> KeyRecordIndex = lpKeyRecord-> KeyRecordIndex;
lpNewKeynode-> Hash = (WORD) RgHashString(lpSubSubKey,
SubSubKeyLength);
// Fill in the key record.
lpKeyRecord-> RecordSize = sizeof(KEY_RECORD) + SubSubKeyLength - 1;
lpKeyRecord-> NameLength = (WORD) SubSubKeyLength;
MoveMemory(lpKeyRecord-> Name, lpSubSubKey, SubSubKeyLength);
lpKeyRecord-> ValueCount = 0;
lpKeyRecord-> ClassLength = 0;
lpKeyRecord-> Reserved = 0;
// Unlock the keynode that points to the new keynode and advance
// to the next keynode.
RgUnlockKeynode(lpFileInfo, PrevKeynodeIndex, TRUE);
PrevKeynodeIndex = KeynodeIndex;
lpKeynode = lpNewKeynode;
RgUnlockDatablock(lpFileInfo, lpKeyRecord-> BlockIndex, TRUE);
fCreatedKeynode = TRUE;
// Following should already be zeroed for subsequent iterations.
ASSERT(!fPrevIsNextIndex);
ASSERT(IsNullPtr(hSubKey));
} while (RgGetNextSubSubKey(NULL, &lpSubSubKey, &SubSubKeyLength));
}
ASSERT(!IsNullKeynodeIndex(KeynodeIndex));
//
// Now we've got the keynode for the request subkey. Check if it has been
// previously opened. If not, then allocate a new key handle for it and
// initialize it.
//
LookupComplete:
if (IsNullPtr(hSubKey = RgFindOpenKeyHandle(lpFileInfo, KeynodeIndex))) {
if (IsNullPtr(hSubKey = RgCreateKeyHandle()))
ErrorCode = ERROR_OUTOFMEMORY;
else {
hSubKey-> lpFileInfo = lpFileInfo;
hSubKey-> KeynodeIndex = KeynodeIndex;
hSubKey-> ChildKeynodeIndex = lpKeynode-> ChildIndex;
hSubKey-> BlockIndex = (WORD) lpKeynode-> BlockIndex;
hSubKey-> KeyRecordIndex = (BYTE) lpKeynode-> KeyRecordIndex;
hSubKey-> PredefinedKeyIndex = hKey-> PredefinedKeyIndex;
}
}
RgUnlockKeynode(lpFileInfo, KeynodeIndex, fCreatedKeynode);
//
// Now we've got a key handle that references the requested subkey.
// Increment the reference count on the handle and return it to the caller.
// Note that this differs from NT semantic where they return a unique
// handle for every open.
//
if (!IsNullPtr(hSubKey)) {
HaveSubKeyHandle:
RgIncrementKeyReferenceCount(hSubKey);
*lphSubKey = hSubKey;
ErrorCode = ERROR_SUCCESS;
}
SignalAndReturnErrorCode:
// If we managed to create any keynodes, regardless of what ErrorCode is
// set to now, then we must signal any waiting events.
if (fCreatedKeynode) {
RgSignalWaitingNotifies(lpFileInfo, NotifyKeynodeIndex,
REG_NOTIFY_CHANGE_NAME);
}
return ErrorCode;
}
//
// RgCreateOrOpenKey
//
// Common routine for VMMRegCreateKey and VMMRegOpenKey. Valids parameters,
// locks the registry, and calls the real worker routine.
//
int
INTERNAL
RgCreateOrOpenKey(
HKEY hKey,
LPCSTR lpSubKey,
LPHKEY lphKey,
UINT Flags
)
{
int ErrorCode;
if (RgIsBadSubKey(lpSubKey))
return ERROR_BADKEY;
if (IsBadHugeWritePtr(lphKey, sizeof(HKEY)))
return ERROR_INVALID_PARAMETER;
if (!RgLockRegistry())
return ERROR_LOCK_FAILED;
if ((ErrorCode = RgValidateAndConvertKeyHandle(&hKey)) == ERROR_SUCCESS)
ErrorCode = RgLookupKey(hKey, lpSubKey, lphKey, Flags);
RgUnlockRegistry();
return ErrorCode;
}
//
// VMMRegCreateKey
//
// See Win32 documentation of RegCreateKey.
//
LONG
REGAPI
VMMRegCreateKey(
HKEY hKey,
LPCSTR lpSubKey,
LPHKEY lphKey
)
{
return RgCreateOrOpenKey(hKey, lpSubKey, lphKey, LK_CREATE);
}
//
// VMMRegOpenKey
//
// See Win32 documentation of RegOpenKey.
//
LONG
REGAPI
VMMRegOpenKey(
HKEY hKey,
LPCSTR lpSubKey,
LPHKEY lphKey
)
{
return RgCreateOrOpenKey(hKey, lpSubKey, lphKey, LK_OPEN);
}
//
// VMMRegCloseKey
//
// See Win32 documentation of RegCloseKey.
//
LONG
REGAPI
VMMRegCloseKey(
HKEY hKey
)
{
int ErrorCode;
if (!RgLockRegistry())
return ERROR_LOCK_FAILED;
ErrorCode = RgValidateAndConvertKeyHandle(&hKey);
if (ErrorCode == ERROR_SUCCESS || ErrorCode == ERROR_KEY_DELETED) {
RgDestroyKeyHandle(hKey);
ErrorCode = ERROR_SUCCESS;
}
RgUnlockRegistry();
return ErrorCode;
}