622 lines
17 KiB
C
622 lines
17 KiB
C
//
|
|
// REGKEY.C
|
|
//
|
|
// Copyright (C) Microsoft Corporation, 1995-1996
|
|
//
|
|
// 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;
|
|
#ifdef REALMODE
|
|
BOOL secondTry;
|
|
#endif
|
|
|
|
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.
|
|
// This also ignores "\"
|
|
//
|
|
|
|
if (IsNullPtr(lpSubKey) || RgGetNextSubSubKey(lpSubKey, &lpSubSubKey,
|
|
&SubSubKeyLength) == 0) {
|
|
hSubKey = hKey;
|
|
goto HaveSubKeyHandle;
|
|
}
|
|
|
|
//
|
|
// The next two lines fix the problem with Publisher 97. It tries to open
|
|
// HKEY_LOCAL_MACHINE, \KEY_NAME. This was being allowed to succeed by
|
|
// this API. That is because, RlGetNextSubSubKey blasts off the starting
|
|
// '\' and then this api is equivalent to HKEY_LOCAL_MACHINE, KEY_NAME.
|
|
// This is a no-no since Publisher 97 proceeds to blast off the whole
|
|
// registry if we let this call succeed.
|
|
//
|
|
|
|
if (!(Flags & LK_CREATE)) {
|
|
SubSubKeyLength += (DWORD)lpSubSubKey - (DWORD)lpSubKey;
|
|
lpSubSubKey = lpSubKey;
|
|
}
|
|
|
|
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)) {
|
|
#ifdef REALMODE
|
|
secondTry = FALSE;
|
|
tryAgain:
|
|
#endif // REALMODE
|
|
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);
|
|
#ifdef REALMODE
|
|
if (!secondTry)
|
|
{
|
|
// What happens in real mode, is that we get wedged with the
|
|
// Keynode block allocated and locked in the middle of the free
|
|
// space, and there is not a free block large enough for the data block.
|
|
// So, by unlocking and freeing the Keynode block and then restarting
|
|
// the operation, the Keynode block gets allocated at the bottom of the
|
|
// heap, leaving room for the data block.
|
|
secondTry = TRUE;
|
|
RgEnumFileInfos(RgSweepFileInfo);
|
|
RgEnumFileInfos(RgSweepFileInfo);
|
|
goto tryAgain;
|
|
}
|
|
#endif // REALMODE
|
|
return ErrorCode;
|
|
}
|
|
|
|
|
|
fFound = (!(Flags & LK_BIGKEYEXT) == !(lpKeynode-> Flags & KNF_BIGKEYEXT) &&
|
|
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 ((IsDynDataKey(hKey) && !(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.
|
|
//
|
|
|
|
#ifdef WANT_HIVE_SUPPORT
|
|
LookupComplete:
|
|
#endif
|
|
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;
|
|
|
|
if (lpKeynode-> Flags & KNF_BIGKEYROOT)
|
|
hSubKey-> Flags |= KEYF_BIGKEYROOT;
|
|
}
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|