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