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; }
VALUE_SEARCH_RETURN_TYPE NTAPI CmpQueryKeyValueData(IN PCM_KEY_CONTROL_BLOCK Kcb, IN PCM_CACHED_VALUE *CachedValue, IN PCM_KEY_VALUE ValueKey, IN BOOLEAN ValueIsCached, IN KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, IN PVOID KeyValueInformation, IN ULONG Length, OUT PULONG ResultLength, OUT PNTSTATUS Status) { PKEY_VALUE_INFORMATION Info = (PKEY_VALUE_INFORMATION)KeyValueInformation; PCELL_DATA CellData; USHORT NameSize; ULONG Size, MinimumSize, SizeLeft, KeySize, AlignedData = 0, DataOffset; PVOID Buffer; BOOLEAN IsSmall, BufferAllocated = FALSE; HCELL_INDEX CellToRelease = HCELL_NIL; VALUE_SEARCH_RETURN_TYPE Result = SearchSuccess; /* Get the value data */ CellData = (PCELL_DATA)ValueKey; /* Check if the value is compressed */ if (CellData->u.KeyValue.Flags & VALUE_COMP_NAME) { /* Get the compressed name size */ NameSize = CmpCompressedNameSize(CellData->u.KeyValue.Name, CellData->u.KeyValue.NameLength); } else { /* Get the real size */ NameSize = CellData->u.KeyValue.NameLength; } /* Check what kind of information the caller is requesting */ switch (KeyValueInformationClass) { /* Basic information */ case KeyValueBasicInformation: /* This is how much size we'll need */ Size = FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name) + NameSize; /* This is the minimum we can work with */ MinimumSize = FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name); /* Return the size we'd like, and assume success */ *ResultLength = Size; *Status = STATUS_SUCCESS; /* Check if the caller gave us below our minimum */ if (Length < MinimumSize) { /* Then we must fail */ *Status = STATUS_BUFFER_TOO_SMALL; break; } /* Fill out the basic information */ Info->KeyValueBasicInformation.TitleIndex = 0; Info->KeyValueBasicInformation.Type = CellData->u.KeyValue.Type; Info->KeyValueBasicInformation.NameLength = NameSize; /* Now only the name is left */ SizeLeft = Length - MinimumSize; Size = NameSize; /* Check if the remaining buffer is too small for the name */ if (SizeLeft < Size) { /* Copy only as much as can fit, and tell the caller */ Size = SizeLeft; *Status = STATUS_BUFFER_OVERFLOW; } /* Check if this is a compressed name */ if (CellData->u.KeyValue.Flags & VALUE_COMP_NAME) { /* Copy as much as we can of the compressed name */ CmpCopyCompressedName(Info->KeyValueBasicInformation.Name, Size, CellData->u.KeyValue.Name, CellData->u.KeyValue.NameLength); } else { /* Copy as much as we can of the raw name */ RtlCopyMemory(Info->KeyValueBasicInformation.Name, CellData->u.KeyValue.Name, Size); } /* We're all done */ break; /* Full key information */ case KeyValueFullInformation: case KeyValueFullInformationAlign64: /* Check if this is a small key and compute key size */ IsSmall = CmpIsKeyValueSmall(&KeySize, CellData->u.KeyValue.DataLength); /* Calculate the total size required */ Size = FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name) + NameSize + KeySize; /* And this is the least we can work with */ MinimumSize = FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name); /* Check if there's any key data */ if (KeySize > 0) { /* Calculate the data offset */ DataOffset = Size - KeySize; #ifdef _WIN64 /* On 64-bit, always align to 8 bytes */ AlignedData = ALIGN_UP(DataOffset, ULONGLONG); #else /* On 32-bit, align the offset to 4 or 8 bytes */ if (KeyValueInformationClass == KeyValueFullInformationAlign64) { AlignedData = ALIGN_UP(DataOffset, ULONGLONG); } else { AlignedData = ALIGN_UP(DataOffset, ULONG); } #endif /* If alignment was required, we'll need more space */ if (AlignedData > DataOffset) Size += (AlignedData-DataOffset); } /* Tell the caller the size we'll finally need, and set success */ *ResultLength = Size; *Status = STATUS_SUCCESS; /* Check if the caller is giving us too little */ if (Length < MinimumSize) { /* Then fail right now */ *Status = STATUS_BUFFER_TOO_SMALL; break; } /* Fill out the basic information */ Info->KeyValueFullInformation.TitleIndex = 0; Info->KeyValueFullInformation.Type = CellData->u.KeyValue.Type; Info->KeyValueFullInformation.DataLength = KeySize; Info->KeyValueFullInformation.NameLength = NameSize; /* Only the name is left now */ SizeLeft = Length - MinimumSize; Size = NameSize; /* Check if the name fits */ if (SizeLeft < Size) { /* It doesn't, truncate what we'll copy, and tell the caller */ Size = SizeLeft; *Status = STATUS_BUFFER_OVERFLOW; } /* Check if this key value is compressed */ if (CellData->u.KeyValue.Flags & VALUE_COMP_NAME) { /* It is, copy the compressed name */ CmpCopyCompressedName(Info->KeyValueFullInformation.Name, Size, CellData->u.KeyValue.Name, CellData->u.KeyValue.NameLength); } else { /* It's not, copy the raw name */ RtlCopyMemory(Info->KeyValueFullInformation.Name, CellData->u.KeyValue.Name, Size); } /* Now check if the key had any data */ if (KeySize > 0) { /* Was it a small key? */ if (IsSmall) { /* Then the data is directly into the cell */ Buffer = &CellData->u.KeyValue.Data; } else { /* Otherwise, we must retrieve it from the value cache */ Result = CmpGetValueDataFromCache(Kcb, CachedValue, CellData, ValueIsCached, &Buffer, &BufferAllocated, &CellToRelease); if (Result != SearchSuccess) { /* We failed, nothing should be allocated */ ASSERT(Buffer == NULL); ASSERT(BufferAllocated == FALSE); *Status = STATUS_INSUFFICIENT_RESOURCES; } } /* Now that we know we truly have data, set its offset */ Info->KeyValueFullInformation.DataOffset = AlignedData; /* Only the data remains to be copied */ SizeLeft = (((LONG)Length - (LONG)AlignedData) < 0) ? 0 : (Length - AlignedData); Size = KeySize; /* Check if the caller has no space for it */ if (SizeLeft < Size) { /* Truncate what we'll copy, and tell the caller */ Size = SizeLeft; *Status = STATUS_BUFFER_OVERFLOW; } /* Sanity check */ ASSERT((IsSmall ? (Size <= CM_KEY_VALUE_SMALL) : TRUE)); /* Make sure we have a valid buffer */ if (Buffer) { /* Copy the data into the aligned offset */ RtlCopyMemory((PVOID)((ULONG_PTR)Info + AlignedData), Buffer, Size); } } else { /* We don't have any data, set the offset to -1, not 0! */ Info->KeyValueFullInformation.DataOffset = 0xFFFFFFFF; } /* We're done! */ break; /* Partial information requested (no name or alignment!) */ case KeyValuePartialInformation: /* Check if this is a small key and compute key size */ IsSmall = CmpIsKeyValueSmall(&KeySize, CellData->u.KeyValue.DataLength); /* Calculate the total size required */ Size = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) + KeySize; /* And this is the least we can work with */ MinimumSize = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data); /* Tell the caller the size we'll finally need, and set success */ *ResultLength = Size; *Status = STATUS_SUCCESS; /* Check if the caller is giving us too little */ if (Length < MinimumSize) { /* Then fail right now */ *Status = STATUS_BUFFER_TOO_SMALL; break; } /* Fill out the basic information */ Info->KeyValuePartialInformation.TitleIndex = 0; Info->KeyValuePartialInformation.Type = CellData->u.KeyValue.Type; Info->KeyValuePartialInformation.DataLength = KeySize; /* Now check if the key had any data */ if (KeySize > 0) { /* Was it a small key? */ if (IsSmall) { /* Then the data is directly into the cell */ Buffer = &CellData->u.KeyValue.Data; } else { /* Otherwise, we must retrieve it from the value cache */ Result = CmpGetValueDataFromCache(Kcb, CachedValue, CellData, ValueIsCached, &Buffer, &BufferAllocated, &CellToRelease); if (Result != SearchSuccess) { /* We failed, nothing should be allocated */ ASSERT(Buffer == NULL); ASSERT(BufferAllocated == FALSE); *Status = STATUS_INSUFFICIENT_RESOURCES; } } /* Only the data remains to be copied */ SizeLeft = Length - MinimumSize; Size = KeySize; /* Check if the caller has no space for it */ if (SizeLeft < Size) { /* Truncate what we'll copy, and tell the caller */ Size = SizeLeft; *Status = STATUS_BUFFER_OVERFLOW; } /* Sanity check */ ASSERT((IsSmall ? (Size <= CM_KEY_VALUE_SMALL) : TRUE)); /* Make sure we have a valid buffer */ if (Buffer) { /* Copy the data into the aligned offset */ RtlCopyMemory(Info->KeyValuePartialInformation.Data, Buffer, Size); } } /* We're done! */ break; /* Other information class */ default: /* We got some class that we don't support */ DPRINT1("Caller requested unknown class: %lx\n", KeyValueInformationClass); *Status = STATUS_INVALID_PARAMETER; break; } /* Return the search result as well */ return Result; }
NTSTATUS CmpQueryKeyDataFromCache( PCM_KEY_CONTROL_BLOCK Kcb, KEY_INFORMATION_CLASS KeyInformationClass, PVOID KeyInformation, ULONG Length, PULONG ResultLength ) /*++ Routine Description: Do the actual copy of data for a key into caller's buffer. If KeyInformation is not long enough to hold all requested data, STATUS_BUFFER_OVERFLOW will be returned, and ResultLength will be set to the number of bytes actually required. Works only for the information cached into kcb. I.e. KeyBasicInformation and KeyCachedInfo Arguments: Kcb - Supplies pointer to the kcb to be queried KeyInformationClass - Specifies the type of information returned in Buffer. One of the following types: KeyBasicInformation - return last write time, title index, and name. (see KEY_BASIC_INFORMATION structure) KeyCachedInformation - return last write time, title index, name .... (see KEY_CACHED_INFORMATION structure) KeyInformation -Supplies pointer to buffer to receive the data. Length - Length of KeyInformation in bytes. ResultLength - Number of bytes actually written into KeyInformation. Return Value: NTSTATUS --*/ { NTSTATUS status; PKEY_INFORMATION pbuffer; ULONG requiredlength; USHORT NameLength; PCM_KEY_NODE Node; // this is to be used only in case of cache incoherency CM_PAGED_CODE(); // // we cannot afford to return the kcb NameBlock as the key name // for KeyBasicInformation as there are lots of callers expecting // the name to be case-sensitive; KeyCachedInformation is new // and used only by the Win32 layer, which is not case sensitive // Note: future clients of KeyCachedInformation must be made aware // that name is NOT case-sensitive // ASSERT( KeyInformationClass == KeyCachedInformation ); // // we are going to need the nameblock; if it is NULL, bail out // if( Kcb->NameBlock == NULL ) { return STATUS_INSUFFICIENT_RESOURCES; } pbuffer = (PKEY_INFORMATION)KeyInformation; if (Kcb->NameBlock->Compressed) { NameLength = CmpCompressedNameSize(Kcb->NameBlock->Name,Kcb->NameBlock->NameLength); } else { NameLength = Kcb->NameBlock->NameLength; } // Assume success status = STATUS_SUCCESS; switch (KeyInformationClass) { case KeyCachedInformation: // // LastWriteTime, TitleIndex, // SubKeys, MaxNameLen, Values, MaxValueNameLen, // MaxValueDataLen, Name // requiredlength = sizeof(KEY_CACHED_INFORMATION); *ResultLength = requiredlength; if (Length < requiredlength) { status = STATUS_BUFFER_TOO_SMALL; } else { pbuffer->KeyCachedInformation.LastWriteTime = Kcb->KcbLastWriteTime; pbuffer->KeyCachedInformation.TitleIndex = 0; pbuffer->KeyCachedInformation.NameLength = NameLength; pbuffer->KeyCachedInformation.Values = Kcb->ValueCache.Count; pbuffer->KeyCachedInformation.MaxNameLen = Kcb->KcbMaxNameLen; pbuffer->KeyCachedInformation.MaxValueNameLen = Kcb->KcbMaxValueNameLen; pbuffer->KeyCachedInformation.MaxValueDataLen = Kcb->KcbMaxValueDataLen; if( !(Kcb->ExtFlags & CM_KCB_INVALID_CACHED_INFO) ) { // there is some cached info if( Kcb->ExtFlags & CM_KCB_NO_SUBKEY ) { pbuffer->KeyCachedInformation.SubKeys = 0; } else if( Kcb->ExtFlags & CM_KCB_SUBKEY_ONE ) { pbuffer->KeyCachedInformation.SubKeys = 1; } else if( Kcb->ExtFlags & CM_KCB_SUBKEY_HINT ) { pbuffer->KeyCachedInformation.SubKeys = Kcb->IndexHint->Count; } else { pbuffer->KeyCachedInformation.SubKeys = Kcb->SubKeyCount; } } else { // // kcb cache is not coherent; get the info from knode // CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"Kcb cache incoherency detected, kcb = %p\n",Kcb)); Node = (PCM_KEY_NODE)HvGetCell(Kcb->KeyHive,Kcb->KeyCell); if( Node == NULL ) { // // couldn't map view for this cell // status = STATUS_INSUFFICIENT_RESOURCES; break; } pbuffer->KeyCachedInformation.SubKeys = Node->SubKeyCounts[Stable] + Node->SubKeyCounts[Volatile]; HvReleaseCell(Kcb->KeyHive,Kcb->KeyCell); } } break; default: status = STATUS_INVALID_PARAMETER; break; } return status; }