PCM_KEY_CONTROL_BLOCK CmpCreateKeyControlBlock( PHHIVE Hive, HCELL_INDEX Cell, PCM_KEY_NODE Node, PCM_KEY_CONTROL_BLOCK ParentKcb, BOOLEAN FakeKey, PUNICODE_STRING KeyName ) /*++ Routine Description: Allocate and initialize a key control block, insert it into the kcb tree. Full path will be BaseName + '\' + KeyName, unless BaseName NULL, in which case the full path is simply KeyName. RefCount of returned KCB WILL have been incremented to reflect callers ref. Arguments: Hive - Supplies Hive that holds the key we are creating a KCB for. Cell - Supplies Cell that contains the key we are creating a KCB for. Node - Supplies pointer to key node. ParentKcb - Parent kcb of the kcb to be created FakeKey - Whether the kcb to be create is a fake one or not KeyName - the subkey name to of the KCB to be created. NOTE: We need the parameter instead of just using the name in the KEY_NODE because there is no name in the root cell of a hive. Return Value: NULL - failure (insufficient memory) else a pointer to the new kcb. --*/ { PCM_KEY_CONTROL_BLOCK kcb; PCM_KEY_CONTROL_BLOCK kcbmatch=NULL; PCMHIVE CmHive; ULONG namelength; PUNICODE_STRING fullname; ULONG Size; ULONG i; UNICODE_STRING NodeName; ULONG ConvKey=0; ULONG Cnt; WCHAR *Cp; // // ParentKCb has the base hash value. // if (ParentKcb) { ConvKey = ParentKcb->ConvKey; } NodeName = *KeyName; while ((NodeName.Length > 0) && (NodeName.Buffer[0] == OBJ_NAME_PATH_SEPARATOR)) { // // This must be the \REGISTRY. // Strip off the leading OBJ_NAME_PATH_SEPARATOR // NodeName.Buffer++; NodeName.Length -= sizeof(WCHAR); } // // Manually compute the hash to use. // ASSERT(NodeName.Length > 0); if (NodeName.Length) { Cp = NodeName.Buffer; for (Cnt=0; Cnt<NodeName.Length; Cnt += sizeof(WCHAR)) { if ((*Cp != OBJ_NAME_PATH_SEPARATOR) && (*Cp != UNICODE_NULL)) { ConvKey = 37 * ConvKey + (ULONG)RtlUpcaseUnicodeChar(*Cp); } ++Cp; } } // // Create a new kcb, which we will free if one already exists // for this key. // Now it is a fixed size structure. // kcb = ExAllocatePoolWithTag(PagedPool, sizeof(CM_KEY_CONTROL_BLOCK), CM_KCB_TAG | PROTECTED_POOL); if (kcb == NULL) { return(NULL); } else { SET_KCB_SIGNATURE(kcb, KCB_SIGNATURE); INIT_KCB_KEYBODY_LIST(kcb); kcb->Delete = FALSE; kcb->RefCount = 1; kcb->KeyHive = Hive; kcb->KeyCell = Cell; kcb->KeyNode = Node; kcb->ConvKey = ConvKey; } ASSERT_KCB(kcb); // // Find location to insert kcb in kcb tree. // LOCK_KCB_TREE(); // // Add the KCB to the hash table // kcbmatch = CmpInsertKeyHash(&kcb->KeyHash, FakeKey); if (kcbmatch != NULL) { // // A match was found. // ASSERT(!kcbmatch->Delete); SET_KCB_SIGNATURE(kcb, '1FmC'); ASSERT_KEYBODY_LIST_EMPTY(kcb); ExFreePoolWithTag(kcb, CM_KCB_TAG | PROTECTED_POOL); ASSERT_KCB(kcbmatch); kcb = kcbmatch; if (kcb->RefCount == 0) { // // This kcb is on the delayed close list. Remove it from that // list. // CmpRemoveFromDelayedClose(kcb); } if ((USHORT)(kcb->RefCount + 1) == 0) { // // We have maxed out the ref count on this key. Probably // some bogus app has opened the same key 64K times without // ever closing it. Just fail the open, they've got enough // handles already. // ASSERT(kcb->RefCount + 1 != 0); kcb = NULL; } else { ++kcb->RefCount; } } else { // // No kcb created previously, fill in all the data. // // // Now try to reference the parentkcb // if (ParentKcb) { if (CmpReferenceKeyControlBlock(ParentKcb)) { kcb->ParentKcb = ParentKcb; kcb->TotalLevels = ParentKcb->TotalLevels + 1; } else { // // We have maxed out the ref count on the parent. // Since it has been cached in the cachetable, // remove it first before we free the allocation. // CmpRemoveKeyControlBlock(kcb); SET_KCB_SIGNATURE(kcb, '2FmC'); ASSERT_KEYBODY_LIST_EMPTY(kcb); ExFreePoolWithTag(kcb, CM_KCB_TAG | PROTECTED_POOL); kcb = NULL; } } else { // // It is the \REGISTRY node. // kcb->ParentKcb = NULL; kcb->TotalLevels = 1; } if (kcb) { // // Now try to find the Name Control block that has the name for this node. // kcb->NameBlock = CmpGetNameControlBlock (&NodeName); if (kcb->NameBlock) { // // Now fill in all the data needed for the cache. // kcb->ValueCache.Count = Node->ValueList.Count; kcb->ValueCache.ValueList = (ULONG_PTR) Node->ValueList.List; kcb->Flags = Node->Flags; kcb->ExtFlags = 0; kcb->DelayedCloseIndex = 0; // // Cache the security cells in the kcb // kcb->Security = Node->Security; if (FakeKey) { // // The KCb to be created is a fake one // kcb->ExtFlags |= CM_KCB_KEY_NON_EXIST; } } else { // // We have maxed out the ref count on the Name. // // // First dereference the parent KCB. // CmpDereferenceKeyControlBlockWithLock(ParentKcb); CmpRemoveKeyControlBlock(kcb); SET_KCB_SIGNATURE(kcb, '3FmC'); ASSERT_KEYBODY_LIST_EMPTY(kcb); ExFreePoolWithTag(kcb, CM_KCB_TAG | PROTECTED_POOL); kcb = NULL; } } } UNLOCK_KCB_TREE(); return kcb; }
PCM_KEY_CONTROL_BLOCK NTAPI CmpCreateKeyControlBlock(IN PHHIVE Hive, IN HCELL_INDEX Index, IN PCM_KEY_NODE Node, IN PCM_KEY_CONTROL_BLOCK Parent, IN ULONG Flags, IN PUNICODE_STRING KeyName) { PCM_KEY_CONTROL_BLOCK Kcb, FoundKcb = NULL; UNICODE_STRING NodeName; ULONG ConvKey = 0, i; BOOLEAN IsFake, HashLock; PWCHAR p; /* Make sure we own this hive in case it's being unloaded */ if ((Hive->HiveFlags & HIVE_IS_UNLOADING) && (((PCMHIVE)Hive)->CreatorOwner != KeGetCurrentThread())) { /* Fail */ return NULL; } /* Check if this is a fake KCB */ IsFake = Flags & CMP_CREATE_FAKE_KCB ? TRUE : FALSE; /* If we have a parent, use its ConvKey */ if (Parent) ConvKey = Parent->ConvKey; /* Make a copy of the name */ NodeName = *KeyName; /* Remove leading slash */ while ((NodeName.Length) && (*NodeName.Buffer == OBJ_NAME_PATH_SEPARATOR)) { /* Move the buffer by one */ NodeName.Buffer++; NodeName.Length -= sizeof(WCHAR); } /* Make sure we didn't get just a slash or something */ ASSERT(NodeName.Length > 0); /* Now setup the hash */ p = NodeName.Buffer; for (i = 0; i < NodeName.Length; i += sizeof(WCHAR)) { /* Make sure it's a valid character */ if (*p != OBJ_NAME_PATH_SEPARATOR) { /* Add this key to the hash */ ConvKey = 37 * ConvKey + RtlUpcaseUnicodeChar(*p); } /* Move on */ p++; } /* Allocate the KCB */ Kcb = CmpAllocateKeyControlBlock(); if (!Kcb) return NULL; /* Initailize the key list */ InitializeKCBKeyBodyList(Kcb); /* Set it up */ Kcb->Signature = CM_KCB_SIGNATURE; Kcb->Delete = FALSE; Kcb->RefCount = 1; Kcb->KeyHive = Hive; Kcb->KeyCell = Index; Kcb->ConvKey = ConvKey; Kcb->DelayedCloseIndex = CmpDelayedCloseSize; Kcb->InDelayClose = 0; ASSERT_KCB_VALID(Kcb); /* Check if we have two hash entires */ HashLock = Flags & CMP_LOCK_HASHES_FOR_KCB ? TRUE : FALSE; if (!HashLock) { /* It's not locked, do we have a parent? */ if (Parent) { /* Lock the parent KCB and ourselves */ CmpAcquireTwoKcbLocksExclusiveByKey(ConvKey, Parent->ConvKey); } else { /* Lock only ourselves */ CmpAcquireKcbLockExclusive(Kcb); } } /* Check if we already have a KCB */ FoundKcb = CmpInsertKeyHash(&Kcb->KeyHash, IsFake); if (FoundKcb) { /* Sanity check */ ASSERT(!FoundKcb->Delete); Kcb->Signature = CM_KCB_INVALID_SIGNATURE; /* Free the one we allocated and reference this one */ CmpFreeKeyControlBlock(Kcb); ASSERT_KCB_VALID(FoundKcb); Kcb = FoundKcb; if (!CmpReferenceKeyControlBlock(Kcb)) { /* We got too many handles */ ASSERT(Kcb->RefCount + 1 != 0); Kcb = NULL; } else { /* Check if we're not creating a fake one, but it used to be fake */ if ((Kcb->ExtFlags & CM_KCB_KEY_NON_EXIST) && !(IsFake)) { /* Set the hive and cell */ Kcb->KeyHive = Hive; Kcb->KeyCell = Index; /* This means that our current information is invalid */ Kcb->ExtFlags = CM_KCB_INVALID_CACHED_INFO; } /* Check if we didn't have any valid data */ if (!(Kcb->ExtFlags & (CM_KCB_NO_SUBKEY | CM_KCB_SUBKEY_ONE | CM_KCB_SUBKEY_HINT))) { /* Calculate the index hint */ Kcb->SubKeyCount = Node->SubKeyCounts[Stable] + Node->SubKeyCounts[Volatile]; /* Cached information is now valid */ Kcb->ExtFlags &= ~CM_KCB_INVALID_CACHED_INFO; } /* Setup the other data */ Kcb->KcbLastWriteTime = Node->LastWriteTime; Kcb->KcbMaxNameLen = (USHORT)Node->MaxNameLen; Kcb->KcbMaxValueNameLen = (USHORT)Node->MaxValueNameLen; Kcb->KcbMaxValueDataLen = Node->MaxValueDataLen; } } else { /* No KCB, do we have a parent? */ if (Parent) { /* Reference the parent */ if (((Parent->TotalLevels + 1) < 512) && (CmpReferenceKeyControlBlock(Parent))) { /* Link it */ Kcb->ParentKcb = Parent; Kcb->TotalLevels = Parent->TotalLevels + 1; } else { /* Remove the KCB and free it */ CmpRemoveKeyControlBlock(Kcb); Kcb->Signature = CM_KCB_INVALID_SIGNATURE; CmpFreeKeyControlBlock(Kcb); Kcb = NULL; } } else { /* No parent, this is the root node */ Kcb->ParentKcb = NULL; Kcb->TotalLevels = 1; } /* Check if we have a KCB */ if (Kcb) { /* Get the NCB */ Kcb->NameBlock = CmpGetNameControlBlock(&NodeName); if (Kcb->NameBlock) { /* Fill it out */ Kcb->ValueCache.Count = Node->ValueList.Count; Kcb->ValueCache.ValueList = Node->ValueList.List; Kcb->Flags = Node->Flags; Kcb->ExtFlags = 0; Kcb->DelayedCloseIndex = CmpDelayedCloseSize; /* Remember if this is a fake key */ if (IsFake) Kcb->ExtFlags |= CM_KCB_KEY_NON_EXIST; /* Setup the other data */ Kcb->SubKeyCount = Node->SubKeyCounts[Stable] + Node->SubKeyCounts[Volatile]; Kcb->KcbLastWriteTime = Node->LastWriteTime; Kcb->KcbMaxNameLen = (USHORT)Node->MaxNameLen; Kcb->KcbMaxValueNameLen = (USHORT)Node->MaxValueNameLen; Kcb->KcbMaxValueDataLen = (USHORT)Node->MaxValueDataLen; } else { /* Dereference the KCB */ CmpDereferenceKeyControlBlockWithLock(Parent, FALSE); /* Remove the KCB and free it */ CmpRemoveKeyControlBlock(Kcb); Kcb->Signature = CM_KCB_INVALID_SIGNATURE; CmpFreeKeyControlBlock(Kcb); Kcb = NULL; } } } /* Check if this is a KCB inside a frozen hive */ if ((Kcb) && (((PCMHIVE)Hive)->Frozen) && (!(Kcb->Flags & KEY_SYM_LINK))) { /* Don't add these to the delay close */ Kcb->ExtFlags |= CM_KCB_NO_DELAY_CLOSE; } /* Sanity check */ ASSERT((!Kcb) || (Kcb->Delete == FALSE)); /* Check if we had locked the hashes */ if (!HashLock) { /* We locked them manually, do we have a parent? */ if (Parent) { /* Unlock the parent KCB and ourselves */ CmpReleaseTwoKcbLockByKey(ConvKey, Parent->ConvKey); } else { /* Unlock only ourselves */ CmpReleaseKcbLockByKey(ConvKey); } } /* Return the KCB */ return Kcb; }