//+---------------------------------------------------------------------------- // // Copyright (C) 1992, Microsoft Corporation // // File: regsups.c // // Contents: Support routines for registry - mainly wrappers for NtAPI. // The native NT registry API deal with unweildy OBJECT_ATTRIBUTES // structures to simply name a registry entry, and also deal // with open-ended structures. This module wraps around the native // NT api and presents a more logical interface tot the registry. // // The routines all have a uniform theme: // // o Declare a KEY_XXX (NT defined) structure on the stack, // with an "arbitrary guess" for size of data. // // o Make the NT call. // // o If NT call returns STATUS_BUFFER_OVERFLOW, allocate a // KEY_XXX structure of the correct size from heap, and make // NT call again. // // o If success, copy data to caller's data buffer, and free // anything we allocated on the heap. // // With a generous initial guess for data buffer size, most of // the time these calls will make a single NT call. Only for // large data will we pay the overhead of memory allocation. // // Classes: // // Functions: KRegpAttributes // KRegpGetValueByName // KRegpGetKeyInfo // KRegpEnumKeyValues // KRegpEnumSubKeys // // History: 18 Sep 92 Milans created. // //----------------------------------------------------------------------------- #include "registry.h" #include "regsups.h" #ifdef ALLOC_PRAGMA #pragma alloc_text( PAGE, KRegpAttributes ) #pragma alloc_text( PAGE, KRegpGetValueByName ) #pragma alloc_text( PAGE, KRegpGetKeyInfo ) #pragma alloc_text( PAGE, KRegpEnumKeyValues ) #pragma alloc_text( PAGE, KRegpEnumSubKeys ) #endif // ALLOC_PRAGMA //----------------------------------------------------------------------------- // // The NT Reg API uses a few open ended structs for info related calls. This // defines the max size of our structs. // // This is just an "optimization". When the NT API's want an open ended data // buffer, we will initially supply one of MAX_INFO_LENGTH. If this is not // sufficient, the NT API will return STATUS_BUFFER_OVERFLOW. At that point, // we will allocate on the heap the proper size buffer, and call again. // //----------------------------------------------------------------------------- #define MAX_INFO_LENGTH 512 // Maximum length of info bufs //----------------------------------------------------------------------------- // // Struct for Value information (ie, value name, data, etc.) // typedef struct tag_KEY_VALUE_INFORMATION { KEY_VALUE_FULL_INFORMATION vi; BYTE buffer[MAX_INFO_LENGTH]; } KEY_VALUE_INFORMATION; #define KEY_VALUE_INFORMATION_LENGTH sizeof(KEY_VALUE_FULL_INFORMATION) + \ MAX_INFO_LENGTH //----------------------------------------------------------------------------- // // Struct for Key information (ie, # of subkeys, # of values, etc.) // typedef struct tag_KEY_INFORMATION { KEY_FULL_INFORMATION ki; BYTE buffer[MAX_INFO_LENGTH]; } KEY_INFORMATION; #define KEY_INFORMATION_LENGTH sizeof(KEY_FULL_INFORMATION) + MAX_INFO_LENGTH //----------------------------------------------------------------------------- // // Struct for a particular subkey's information // typedef struct tag_KEY_SUBKEY_INFORMATION { KEY_NODE_INFORMATION ni; BYTE buffer[MAX_INFO_LENGTH]; } KEY_SUBKEY_INFORMATION; #define KEY_SUBKEY_INFORMATION_LENGTH sizeof(KEY_NODE_INFORMATION) + \ MAX_INFO_LENGTH //+---------------------------------------------------------------------------- // // Function: KRegpAttributes // // Synopsis: The NT reg API often calls for an Object Attributes structure // for things like names etc. This routine bundles a name and its // parent into such a structure. // // Arguments: [hParent] Handle to parent of object // [wszName] Name of object // // Returns: Address of initialized object attributes structure. // // Notes: Return value is address to static variable. // //----------------------------------------------------------------------------- OBJECT_ATTRIBUTES * KRegpAttributes( IN HANDLE hParent, IN PWSTR wszName) { static OBJECT_ATTRIBUTES objAttributes; static UNICODE_STRING usName; RtlInitUnicodeString(&usName, wszName); InitializeObjectAttributes( &objAttributes, // Destination &usName, // Name OBJ_CASE_INSENSITIVE, // Attributes hParent, // Parent NULL); // Security return(&objAttributes); } //+---------------------------------------------------------------------------- // // Function: KRegpGetValueByName // // Synopsis: Given a Value Name, returns the value's data. // // Arguments: [hkey] Handle of key // [wszValueName] Value Name to query // [pbData] Pointer to data buffer // [pcbSize] Pointer to ULONG. On entry, it must show size of // data buffer. On successful exit, it will be set to // actual length of data retrieved. // // Returns: STATUS_BUFFER_TOO_SMALL, Status from NT Reg API. // //----------------------------------------------------------------------------- NTSTATUS KRegpGetValueByName( IN HKEY hKey, IN PWSTR wszValueName, OUT PBYTE pbData, IN OUT PULONG pcbSize) { KEY_VALUE_INFORMATION viValue, *pviValue; UNICODE_STRING ustrValueName; ULONG cbActualLength, cbRequiredSize; NTSTATUS Status; RtlInitUnicodeString(&ustrValueName, wszValueName); pviValue = &viValue; Status = NtQueryValueKey( hKey, &ustrValueName, KeyValueFullInformation, (PVOID) pviValue, KEY_VALUE_INFORMATION_LENGTH, &cbActualLength ); if (Status == STATUS_BUFFER_OVERFLOW) { // // Our default buffer of MAX_INFO_LENGTH size didn't quite cut it // Forced to allocate from heap and make call again. // pviValue = (KEY_VALUE_INFORMATION *) ExAllocatePoolWithTag( PagedPool, cbActualLength, ' sfD'); if (!pviValue) { return(STATUS_NO_MEMORY); } Status = NtQueryValueKey( hKey, &ustrValueName, KeyValueFullInformation, (PVOID) pviValue, cbActualLength, &cbActualLength); } if (!NT_SUCCESS(Status)) { goto Cleanup; } if (pviValue->vi.Type == REG_SZ) { cbRequiredSize = pviValue->vi.DataLength + sizeof(UNICODE_NULL); } else { cbRequiredSize = pviValue->vi.DataLength; } if (cbRequiredSize > *pcbSize) { Status = STATUS_BUFFER_TOO_SMALL; goto Cleanup; } RtlMoveMemory(pbData, ((BYTE *) pviValue) + pviValue->vi.DataOffset, pviValue->vi.DataLength); if (pviValue->vi.Type == REG_SZ) { ((PWSTR) pbData)[cbRequiredSize/sizeof(WCHAR) - 1] = UNICODE_NULL; } *pcbSize = cbRequiredSize; Cleanup: if (pviValue != &viValue) { // // Must have had to allocate from heap, so free the heap // DfsFree(pviValue); } return(Status); } //+---------------------------------------------------------------------------- // // Function: KRegpGetKeyInfo // // Synopsis: Given a key, return the number of subkeys, values, and their // max sizes. // // Arguments: [hkey] Handle to key. // [pcNumSubKeys] Receives # of subkeys that hkey has // [pcbMaxSubKeyLength] Receives max length of subkey name // [pcNumValues] Receives # of values that hkey has // [pcbMaxValueLength] Receives max length of value name // [pcbMaxValueData] Receives max length of value data // // Returns: // //----------------------------------------------------------------------------- NTSTATUS KRegpGetKeyInfo( IN HKEY hKey, OUT PULONG pcNumSubKeys, OUT PULONG pcbMaxSubKeyLength, OUT PULONG pcNumValues, OUT PULONG pcbMaxValueName, OUT PULONG pcbMaxValueData) { KEY_INFORMATION kiInfo, *pkiInfo; ULONG lActualLength; NTSTATUS Status; pkiInfo = &kiInfo; Status = NtQueryKey( hKey, KeyFullInformation, pkiInfo, KEY_INFORMATION_LENGTH, &lActualLength); if (Status == STATUS_BUFFER_OVERFLOW) { // // Our default buffer of MAX_INFO_LENGTH size didn't quite cut it // Forced to allocate from heap and make call again. // pkiInfo = (KEY_INFORMATION *) ExAllocatePoolWithTag( PagedPool, lActualLength, ' sfD'); if (!pkiInfo) { return(STATUS_NO_MEMORY); } Status = NtQueryKey( hKey, KeyFullInformation, pkiInfo, lActualLength, &lActualLength); } if (NT_SUCCESS(Status)) { *pcNumSubKeys = pkiInfo->ki.SubKeys; *pcbMaxSubKeyLength = pkiInfo->ki.MaxNameLen; *pcNumValues = pkiInfo->ki.Values; *pcbMaxValueName = pkiInfo->ki.MaxValueNameLen; *pcbMaxValueData = pkiInfo->ki.MaxValueDataLen; } if (pkiInfo != &kiInfo) { DfsFree(pkiInfo); } return(Status); } //+---------------------------------------------------------------------------- // // Function: KRegpEnumKeyValues // // Synopsis: Given a key, return the name and data of the i'th value, where // the first value has an index of 0. // // Arguments: [hkey] -- Handle to key // [i] -- Index of value to get // [wszValueName] -- Pointer to buffer to hold value name. // [pcbMaxValueName] -- on entry, size in bytes of wszValueName. // On exit, will hold size of wszValueName. // [pbData] -- pointer to buffer to hold value data. // [pcbMaxDataSize] -- on entry, size of pbData. On successful // return, size of pbData. // // Returns: // //----------------------------------------------------------------------------- NTSTATUS KRegpEnumKeyValues( HKEY hKey, ULONG iValue, PWSTR wszValueName, PULONG pcbMaxValueName, PBYTE pbData, PULONG pcbMaxDataSize) { KEY_VALUE_INFORMATION viValue, *pviValue; ULONG dwActualLength; NTSTATUS Status; pviValue = &viValue; Status = NtEnumerateValueKey( hKey, iValue, KeyValueFullInformation, pviValue, KEY_VALUE_INFORMATION_LENGTH, &dwActualLength); if (Status == STATUS_BUFFER_OVERFLOW) { // // Our default buffer of MAX_INFO_LENGTH size didn't quite cut it // Forced to allocate from heap and make call again. // pviValue = (KEY_VALUE_INFORMATION *) ExAllocatePoolWithTag( PagedPool, dwActualLength, ' sfD'); if (!pviValue) { return(STATUS_NO_MEMORY); } Status = NtEnumerateValueKey( hKey, iValue, KeyValueFullInformation, pviValue, dwActualLength, &dwActualLength); } if (NT_SUCCESS(Status)) { if ( (*pcbMaxValueName < pviValue->vi.NameLength) || (*pcbMaxDataSize < pviValue->vi.DataLength) ) { Status = STATUS_BUFFER_TOO_SMALL; goto Cleanup; } *pcbMaxValueName = pviValue->vi.NameLength; RtlMoveMemory( (BYTE *) wszValueName, (BYTE *) pviValue->vi.Name, pviValue->vi.NameLength ); wszValueName[pviValue->vi.NameLength / sizeof(WCHAR)] = L'\0'; RtlMoveMemory( pbData, ((BYTE *) &viValue) + pviValue->vi.DataOffset, pviValue->vi.DataLength ); *pcbMaxDataSize = pviValue->vi.DataLength; } Cleanup: if (pviValue != &viValue) { DfsFree(pviValue); } return(Status); } //+---------------------------------------------------------------------------- // // Function: KRegpEnumSubKeys // // Synopsis: Retrieve the i'th subkey name of a key. // // Arguments: [hKey] -- Parent key // [iSubKey] -- index of the subkey // [wszSubKeyName] Buffer to hold name of subkey // [pcbMaxNameSize] On entry, size in bytes of wszSubKeyName // On exit, size of name. // // Returns: // //----------------------------------------------------------------------------- NTSTATUS KRegpEnumSubKeys( HKEY hKey, ULONG iSubKey, PWSTR wszSubKeyName, PULONG pcbMaxNameSize ) { KEY_SUBKEY_INFORMATION si, *psi; ULONG dwActualLength; NTSTATUS Status; psi = &si; Status = NtEnumerateKey( hKey, iSubKey, KeyNodeInformation, psi, KEY_SUBKEY_INFORMATION_LENGTH, &dwActualLength ); if (Status == STATUS_BUFFER_OVERFLOW) { // // Our default buffer of MAX_INFO_LENGTH size didn't quite cut it // Forced to allocate from heap and make call again. // psi = (KEY_SUBKEY_INFORMATION *) ExAllocatePoolWithTag( PagedPool, dwActualLength, ' sfD'); if (!psi) { return(STATUS_NO_MEMORY); } Status = NtEnumerateKey( hKey, iSubKey, KeyNodeInformation, psi, dwActualLength, &dwActualLength ); } if (NT_SUCCESS(Status)) { if (*pcbMaxNameSize < si.ni.NameLength) { Status = STATUS_BUFFER_TOO_SMALL; goto Cleanup; } RtlMoveMemory( (BYTE *) wszSubKeyName, (BYTE *) psi->ni.Name, psi->ni.NameLength ); wszSubKeyName[psi->ni.NameLength / sizeof(WCHAR)] = UNICODE_NULL; *pcbMaxNameSize = psi->ni.NameLength; } Cleanup: if (psi != &si) { DfsFree(psi); } return(Status); }