VOID NTAPI INIT_FUNCTION CmpInitializeCache(VOID) { ULONG Length, i; /* Calculate length for the table */ Length = CmpHashTableSize * sizeof(CM_KEY_HASH_TABLE_ENTRY); /* Allocate it */ CmpCacheTable = CmpAllocate(Length, TRUE, TAG_CM); if (!CmpCacheTable) { /* Take the system down */ KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 3, 1, 0, 0); } /* Zero out the table */ RtlZeroMemory(CmpCacheTable, Length); /* Initialize the locks */ for (i = 0; i < CmpHashTableSize; i++) { /* Setup the pushlock */ ExInitializePushLock(&CmpCacheTable[i].Lock); } /* Calculate length for the name cache */ Length = CmpHashTableSize * sizeof(CM_NAME_HASH_TABLE_ENTRY); /* Now allocate the name cache table */ CmpNameCacheTable = CmpAllocate(Length, TRUE, TAG_CM); if (!CmpNameCacheTable) { /* Take the system down */ KeBugCheckEx(CONFIG_INITIALIZATION_FAILED, 3, 3, 0, 0); } /* Zero out the table */ RtlZeroMemory(CmpNameCacheTable, Length); /* Initialize the locks */ for (i = 0; i < CmpHashTableSize; i++) { /* Setup the pushlock */ ExInitializePushLock(&CmpNameCacheTable[i].Lock); } /* Setup the delayed close table */ CmpInitializeDelayedCloseTable(); }
PVOID NTAPI CmpAllocateDelayItem(VOID) { PCM_DELAY_ALLOC Entry; PCM_ALLOC_PAGE AllocPage; ULONG i; PLIST_ENTRY NextEntry; PAGED_CODE(); /* Lock the allocation buckets */ KeAcquireGuardedMutex(&CmpDelayAllocBucketLock); /* Look for an item on the free list */ SearchList: if (!IsListEmpty(&CmpFreeDelayItemsListHead)) { /* Get the current entry in the list */ NextEntry = RemoveHeadList(&CmpFreeDelayItemsListHead); /* Grab the item */ Entry = CONTAINING_RECORD(NextEntry, CM_DELAY_ALLOC, ListEntry); /* Clear the list */ Entry->ListEntry.Flink = Entry->ListEntry.Blink = NULL; /* Grab the alloc page */ AllocPage = CmpGetAllocPageFromDelayAlloc(Entry); /* Decrease free entries */ ASSERT(AllocPage->FreeCount != 0); AllocPage->FreeCount--; /* Release the lock */ KeReleaseGuardedMutex(&CmpDelayAllocBucketLock); return Entry; } /* Allocate an allocation page */ AllocPage = CmpAllocate(PAGE_SIZE, TRUE, TAG_CM); if (AllocPage) { /* Set default entries */ AllocPage->FreeCount = CM_DELAYS_PER_PAGE; /* Loop each entry */ for (i = 0; i < CM_DELAYS_PER_PAGE; i++) { /* Get this entry and link it */ Entry = (PVOID)((ULONG_PTR)AllocPage + FIELD_OFFSET(CM_ALLOC_PAGE, AllocPage) + i * sizeof(CM_DELAY_ALLOC)); InsertTailList(&CmpFreeDelayItemsListHead, &Entry->ListEntry); /* Clear the KCB pointer */ Entry->Kcb = NULL; } } else { /* Release the lock */ KeReleaseGuardedMutex(&CmpDelayAllocBucketLock); return NULL; } /* Do the search again */ goto SearchList; }
PCM_KEY_CONTROL_BLOCK NTAPI CmpAllocateKeyControlBlock(VOID) { PLIST_ENTRY NextEntry; PCM_KEY_CONTROL_BLOCK CurrentKcb; PCM_ALLOC_PAGE AllocPage; ULONG i; PAGED_CODE(); /* Check if private allocations are initialized */ if (CmpAllocInited) { /* They are, acquire the bucket lock */ KeAcquireGuardedMutex(&CmpAllocBucketLock); /* See if there's something on the free KCB list */ SearchKcbList: if (!IsListEmpty(&CmpFreeKCBListHead)) { /* Remove the entry */ NextEntry = RemoveHeadList(&CmpFreeKCBListHead); /* Get the KCB */ CurrentKcb = CONTAINING_RECORD(NextEntry, CM_KEY_CONTROL_BLOCK, FreeListEntry); /* Get the allocation page */ AllocPage = CmpGetAllocPageFromKcb(CurrentKcb); /* Decrease the free count */ ASSERT(AllocPage->FreeCount != 0); AllocPage->FreeCount--; /* Make sure this KCB is privately allocated */ ASSERT(CurrentKcb->PrivateAlloc == 1); /* Release the allocation lock */ KeReleaseGuardedMutex(&CmpAllocBucketLock); /* Return the KCB */ return CurrentKcb; } /* Allocate an allocation page */ AllocPage = CmpAllocate(PAGE_SIZE, TRUE, TAG_CM); if (AllocPage) { /* Set default entries */ AllocPage->FreeCount = CM_KCBS_PER_PAGE; /* Loop each entry */ for (i = 0; i < CM_KCBS_PER_PAGE; i++) { /* Get this entry */ CurrentKcb = (PVOID)((ULONG_PTR)AllocPage + FIELD_OFFSET(CM_ALLOC_PAGE, AllocPage) + i * sizeof(CM_KEY_CONTROL_BLOCK)); /* Set it up */ CurrentKcb->PrivateAlloc = TRUE; CurrentKcb->DelayCloseEntry = NULL; InsertTailList(&CmpFreeKCBListHead, &CurrentKcb->FreeListEntry); } /* Now go back and search the list */ goto SearchKcbList; } } /* Allocate a KCB only */ CurrentKcb = CmpAllocate(sizeof(CM_KEY_CONTROL_BLOCK), TRUE, TAG_CM); if (CurrentKcb) { /* Set it up */ CurrentKcb->PrivateAlloc = 0; CurrentKcb->DelayCloseEntry = NULL; } /* Return it */ return CurrentKcb; }
BOOLEAN RegImportBinaryHive( PCHAR ChunkBase, ULONG ChunkSize) { PCM_KEY_NODE KeyCell; PCM_KEY_FAST_INDEX HashCell; PCM_KEY_NODE SubKeyCell; FRLDRHKEY SystemKey; ULONG i; LONG Error; PCMHIVE CmHive; PHHIVE Hive; NTSTATUS Status; TRACE("RegImportBinaryHive(%x, %u) called\n", ChunkBase, ChunkSize); CmHive = CmpAllocate(sizeof(CMHIVE), TRUE, 0); Status = HvInitialize(&CmHive->Hive, HINIT_FLAT, 0, 0, ChunkBase, CmpAllocate, CmpFree, NULL, NULL, NULL, NULL, 1, NULL); if (!NT_SUCCESS(Status)) { CmpFree(CmHive, 0); ERR("Invalid hive Signature!\n"); return FALSE; } Hive = &CmHive->Hive; KeyCell = (PCM_KEY_NODE)HvGetCell(Hive, Hive->BaseBlock->RootCell); TRACE("KeyCell: %x\n", KeyCell); TRACE("KeyCell->Signature: %x\n", KeyCell->Signature); if (KeyCell->Signature != CM_KEY_NODE_SIGNATURE) { ERR("Invalid key cell Signature!\n"); return FALSE; } TRACE("Subkeys: %u\n", KeyCell->SubKeyCounts); TRACE("Values: %u\n", KeyCell->ValueList.Count); /* Open 'System' key */ Error = RegOpenKey(NULL, L"\\Registry\\Machine\\SYSTEM", &SystemKey); if (Error != ERROR_SUCCESS) { ERR("Failed to open 'system' key!\n"); return FALSE; } /* Enumerate and add subkeys */ if (KeyCell->SubKeyCounts[Stable] > 0) { HashCell = (PCM_KEY_FAST_INDEX)HvGetCell(Hive, KeyCell->SubKeyLists[Stable]); TRACE("HashCell: %x\n", HashCell); TRACE("SubKeyCounts: %x\n", KeyCell->SubKeyCounts[Stable]); for (i = 0; i < KeyCell->SubKeyCounts[Stable]; i++) { TRACE("Cell[%d]: %x\n", i, HashCell->List[i].Cell); SubKeyCell = (PCM_KEY_NODE)HvGetCell(Hive, HashCell->List[i].Cell); TRACE("SubKeyCell[%d]: %x\n", i, SubKeyCell); if (!RegImportSubKey(Hive, SubKeyCell, SystemKey)) return FALSE; } } return TRUE; }
PUNICODE_STRING NTAPI CmpConstructName(IN PCM_KEY_CONTROL_BLOCK Kcb) { PUNICODE_STRING KeyName; ULONG NameLength, i; PCM_KEY_CONTROL_BLOCK MyKcb; PCM_KEY_NODE KeyNode; BOOLEAN DeletedKey = FALSE; PWCHAR TargetBuffer, CurrentNameW; PUCHAR CurrentName; /* Calculate how much size our key name is going to occupy */ NameLength = 0; MyKcb = Kcb; while (MyKcb) { /* Add length of the name */ if (!MyKcb->NameBlock->Compressed) { NameLength += MyKcb->NameBlock->NameLength; } else { NameLength += CmpCompressedNameSize(MyKcb->NameBlock->Name, MyKcb->NameBlock->NameLength); } /* Sum up the separator too */ NameLength += sizeof(WCHAR); /* Go to the parent KCB */ MyKcb = MyKcb->ParentKcb; } /* Allocate the unicode string now */ KeyName = CmpAllocate(NameLength + sizeof(UNICODE_STRING), TRUE, TAG_CM); if (!KeyName) return NULL; /* Set it up */ KeyName->Buffer = (PWSTR)(KeyName + 1); KeyName->Length = NameLength; KeyName->MaximumLength = NameLength; /* Loop the keys again, now adding names */ NameLength = 0; MyKcb = Kcb; while (MyKcb) { /* Sanity checks for deleted and fake keys */ if ((!MyKcb->KeyCell && !MyKcb->Delete) || !MyKcb->KeyHive || MyKcb->ExtFlags & CM_KCB_KEY_NON_EXIST) { /* Failure */ CmpFree(KeyName, 0); return NULL; } /* Try to get the name from the keynode, if the key is not deleted */ if (!DeletedKey && !MyKcb->Delete) { KeyNode = HvGetCell(MyKcb->KeyHive, MyKcb->KeyCell); if (!KeyNode) { /* Failure */ CmpFree(KeyName, 0); return NULL; } } else { /* The key was deleted */ KeyNode = NULL; DeletedKey = TRUE; } /* Get the pointer to the beginning of the current key name */ NameLength += (MyKcb->NameBlock->NameLength + 1) * sizeof(WCHAR); TargetBuffer = &KeyName->Buffer[(KeyName->Length - NameLength) / sizeof(WCHAR)]; /* Add a separator */ TargetBuffer[0] = OBJ_NAME_PATH_SEPARATOR; /* Add the name, but remember to go from the end to the beginning */ if (!MyKcb->NameBlock->Compressed) { /* Get the pointer to the name (from the keynode, if possible) */ if ((MyKcb->Flags & (KEY_HIVE_ENTRY | KEY_HIVE_EXIT)) || !KeyNode) { CurrentNameW = MyKcb->NameBlock->Name; } else { CurrentNameW = KeyNode->Name; } /* Copy the name */ for (i=0; i < MyKcb->NameBlock->NameLength; i++) { TargetBuffer[i+1] = *CurrentNameW; CurrentNameW++; } } else { /* Get the pointer to the name (from the keynode, if possible) */ if ((MyKcb->Flags & (KEY_HIVE_ENTRY | KEY_HIVE_EXIT)) || !KeyNode) { CurrentName = (PUCHAR)MyKcb->NameBlock->Name; } else { CurrentName = (PUCHAR)KeyNode->Name; } /* Copy the name */ for (i=0; i < MyKcb->NameBlock->NameLength; i++) { TargetBuffer[i+1] = (WCHAR)*CurrentName; CurrentName++; } } /* Release the cell, if needed */ if (KeyNode) HvReleaseCell(MyKcb->KeyHive, MyKcb->KeyCell); /* Go to the parent KCB */ MyKcb = MyKcb->ParentKcb; } /* Return resulting buffer (both UNICODE_STRING and its buffer following it) */ return KeyName; }
PCM_NAME_CONTROL_BLOCK NTAPI CmpGetNameControlBlock(IN PUNICODE_STRING NodeName) { PCM_NAME_CONTROL_BLOCK Ncb = NULL; ULONG ConvKey = 0; PWCHAR p, pp; ULONG i; BOOLEAN IsCompressed = TRUE, Found = FALSE; PCM_NAME_HASH HashEntry; ULONG Length, NcbSize; /* Loop the name */ p = NodeName->Buffer; for (i = 0; i < NodeName->Length; i += sizeof(WCHAR)) { /* Make sure it's not a slash */ if (*p != OBJ_NAME_PATH_SEPARATOR) { /* Add it to the hash */ ConvKey = 37 * ConvKey + RtlUpcaseUnicodeChar(*p); } /* Next character */ p++; } /* Set assumed lengh and loop to check */ Length = NodeName->Length / sizeof(WCHAR); for (i = 0; i < (NodeName->Length / sizeof(WCHAR)); i++) { /* Check if this is a 16-bit character */ if (NodeName->Buffer[i] > (UCHAR)-1) { /* This is the actual size, and we know we're not compressed */ Length = NodeName->Length; IsCompressed = FALSE; break; } } /* Lock the NCB entry */ CmpAcquireNcbLockExclusiveByKey(ConvKey); /* Get the hash entry */ HashEntry = GET_HASH_ENTRY(CmpNameCacheTable, ConvKey).Entry; while (HashEntry) { /* Get the current NCB */ Ncb = CONTAINING_RECORD(HashEntry, CM_NAME_CONTROL_BLOCK, NameHash); /* Check if the hash matches */ if ((ConvKey == HashEntry->ConvKey) && (Length == Ncb->NameLength)) { /* Assume success */ Found = TRUE; /* If the NCB is compressed, do a compressed name compare */ if (Ncb->Compressed) { /* Compare names */ if (CmpCompareCompressedName(NodeName, Ncb->Name, Length)) { /* We failed */ Found = FALSE; } } else { /* Do a manual compare */ p = NodeName->Buffer; pp = Ncb->Name; for (i = 0; i < Ncb->NameLength; i += sizeof(WCHAR)) { /* Compare the character */ if (RtlUpcaseUnicodeChar(*p) != RtlUpcaseUnicodeChar(*pp)) { /* Failed */ Found = FALSE; break; } /* Next chars */ p++; pp++; } } /* Check if we found a name */ if (Found) { /* Reference it */ ASSERT(Ncb->RefCount != 0xFFFF); Ncb->RefCount++; break; } } /* Go to the next hash */ HashEntry = HashEntry->NextHash; } /* Check if we didn't find it */ if (!Found) { /* Allocate one */ NcbSize = FIELD_OFFSET(CM_NAME_CONTROL_BLOCK, Name) + Length; Ncb = CmpAllocate(NcbSize, TRUE, TAG_CM); if (!Ncb) { /* Release the lock and fail */ CmpReleaseNcbLockByKey(ConvKey); return NULL; } /* Clear it out */ RtlZeroMemory(Ncb, NcbSize); /* Check if the name was compressed */ if (IsCompressed) { /* Copy the compressed name */ for (i = 0; i < NodeName->Length / sizeof(WCHAR); i++) { /* Copy Unicode to ANSI */ ((PCHAR)Ncb->Name)[i] = (CHAR)RtlUpcaseUnicodeChar(NodeName->Buffer[i]); } } else { /* Copy the name directly */ for (i = 0; i < NodeName->Length / sizeof(WCHAR); i++) { /* Copy each unicode character */ Ncb->Name[i] = RtlUpcaseUnicodeChar(NodeName->Buffer[i]); } } /* Setup the rest of the NCB */ Ncb->Compressed = IsCompressed; Ncb->ConvKey = ConvKey; Ncb->RefCount++; Ncb->NameLength = Length; /* Insert the name in the hash table */ HashEntry = &Ncb->NameHash; HashEntry->NextHash = GET_HASH_ENTRY(CmpNameCacheTable, ConvKey).Entry; GET_HASH_ENTRY(CmpNameCacheTable, ConvKey).Entry = HashEntry; } /* Release NCB lock */ CmpReleaseNcbLockByKey(ConvKey); /* Return the NCB found */ return Ncb; }