Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

931 lines
24 KiB

/*++
Copyright (C) 2002 Microsoft Corporation
Module Name:
registry.c
Abstract:
This module contains routines that port drivers export to miniports to allow
them to read and write registry data. The Keys are relative to the miniport's
<ServiceName>\Parameters key.
Author:
Environment:
kernel mode only
Notes:
Revision History:
--*/
#include "precomp.h"
#define MAX_REGISTRY_BUFFER_SIZE 0x10000
#define PORT_TAG_REGISTRY_BUFFER 'BRlP'
#define PORT_REGISTRY_INFO_INITIALIZED 0x00000001
#define PORT_REGISTRY_BUFFER_ALLOCATED 0x00000002
NTSTATUS
PortMiniportRegistryInitialize(
IN OUT PPORT_REGISTRY_INFO PortContext
)
/*++
Routine Description:
This routine is called by the port driver to init the registry routine's internal
data.
Arguments:
PortContext - Used to identify the caller and instatiation of the caller.
Return Value:
STATUS_SUCCESS for now. If this is extended, it might require allocations, etc.
--*/
{
NTSTATUS status;
ASSERT(PortContext->Size == sizeof(PORT_REGISTRY_INFO));
//
// Initialize the spinlock and list entry.
//
KeInitializeSpinLock(&PortContext->SpinLock);
InitializeListHead(&PortContext->ListEntry);
//
// Ensure that buffer is NULL at this time. It's only valid after we allocate it.
//
PortContext->Buffer = NULL;
//
// Indiacate that this is ready to go.
//
PortContext->Flags = PORT_REGISTRY_INFO_INITIALIZED;
return STATUS_SUCCESS;
}
VOID
PortMiniportRegistryDestroy(
IN PPORT_REGISTRY_INFO PortContext
)
/*++
Routine Description:
This routine is called by the port driver when it unloads a miniport.
It will free the resources and cleanup whatever else.
Arguments:
PortContext - Used to identify the caller and instatiation of the caller.
Return Value:
NOTHING
--*/
{
KIRQL irql;
KeAcquireSpinLock(&PortContext->SpinLock, &irql);
//
// See whether there is still a buffer hanging around.
//
if (PortContext->Flags & PORT_REGISTRY_BUFFER_ALLOCATED) {
ASSERT(PortContext->Buffer);
//
// Free it.
//
ExFreePool(PortContext->Buffer);
} else {
//
// This should be NULL if it's not allocated.
//
ASSERT(PortContext->Buffer == NULL);
}
//
// Clean up.
//
PortContext->Flags = 0;
PortContext->Buffer = NULL;
KeReleaseSpinLock(&PortContext->SpinLock, irql);
return;
}
NTSTATUS
PortAllocateRegistryBuffer(
IN PPORT_REGISTRY_INFO PortContext
)
/*++
Routine Description:
This routine is called by the port driver to allocate a registry buffer of Length for the
miniport.
The caller should initialize Length before calling this routine.
Length is checked against a max. and alignment requirements and updated accordingly, if
necessary.
Arguments:
PortContext - Value used to identify the caller and instatiation of the caller.
Return Value:
The buffer field is updated or NULL if some error prevents the allocation.
Length is updated to reflect the actual size.
SUCCESS, INSUFFICIENT_RESOURCES, or BUSY if the miniport already has a buffer.
--*/
{
PUCHAR buffer = NULL;
ULONG length;
NTSTATUS status;
//
// The size must be correct.
//
ASSERT(PortContext->Size == sizeof(PORT_REGISTRY_INFO));
//
// Must be initialized via the init routine.
//
ASSERT(PortContext->Flags & PORT_REGISTRY_INFO_INITIALIZED );
//
// Can't be here twice.
//
ASSERT((PortContext->Flags & PORT_REGISTRY_BUFFER_ALLOCATED) == 0);
//
// Capture the length.
//
length = PortContext->LengthNeeded;
//
// See if the miniport is attempted a double allocate.
//
if (PortContext->Flags & PORT_REGISTRY_BUFFER_ALLOCATED) {
//
// This would say that there better be a buffer.
//
ASSERT(PortContext->Buffer);
//
// Buffer already outstanding, so don't allow this.
//
status = STATUS_DEVICE_BUSY;
} else {
//
// Check the upper bound.
//
if (length > MAX_REGISTRY_BUFFER_SIZE) {
//
// The request is too large, reset it. The port driver or miniport will have
// to deal with the change.
//
length = MAX_REGISTRY_BUFFER_SIZE;
}
//
// Allocate the buffer.
//
buffer = ExAllocatePoolWithTag(NonPagedPool,
length,
PORT_TAG_REGISTRY_BUFFER);
if (buffer == NULL) {
//
// Set the caller's length to 0 as the allocation failed.
//
PortContext->AllocatedLength = 0;
status = STATUS_INSUFFICIENT_RESOURCES;
} else {
//
// Indicate that the allocation of Length was successful, and
// that the miniport has a registry buffer.
//
PortContext->Flags |= PORT_REGISTRY_BUFFER_ALLOCATED;
PortContext->Buffer = buffer;
PortContext->AllocatedLength = length;
//
// Zero it for them to be nice.
//
RtlZeroMemory(buffer, length);
status = STATUS_SUCCESS;
}
}
return status;
}
NTSTATUS
PortFreeRegistryBuffer(
IN PPORT_REGISTRY_INFO PortContext
)
/*++
Routine Description:
Frees the buffer allocated via PortAllocateRegistryBuffer.
Arguments:
PortContext - Value used to identify the caller and instatiation of the caller.
Return Value:
INVALID_PARAMETER if the buffer isn't already allocated.
SUCCESS
--*/
{
//
// The size must be correct.
//
ASSERT(PortContext->Size == sizeof(PORT_REGISTRY_INFO));
//
// Must be initialized via the init routine.
//
ASSERT(PortContext->Flags & PORT_REGISTRY_INFO_INITIALIZED );
//
// Can't be here, unless a buffer has been allocated.
//
ASSERT(PortContext->Flags & PORT_REGISTRY_BUFFER_ALLOCATED);
//
// If it's not allocated, return an error.
//
if ((PortContext->Flags & PORT_REGISTRY_BUFFER_ALLOCATED) == 0) {
ASSERT(PortContext->Buffer == NULL);
return STATUS_INVALID_PARAMETER;
}
//
// Poison the buffer to catch poorly-written miniports.
//
RtlZeroMemory(PortContext->Buffer, PortContext->AllocatedLength);
//
// Free the buffer.
//
ExFreePool(PortContext->Buffer);
//
// Indicate that it's gone.
//
PortContext->Flags &= ~PORT_REGISTRY_BUFFER_ALLOCATED;
PortContext->AllocatedLength = 0;
PortContext->Buffer = NULL;
return STATUS_SUCCESS;
}
ULONG
WCharToAscii(
OUT PUCHAR Destination,
IN PWCHAR Source,
IN ULONG BufferSize
)
/*+++
Routine Description:
This routine is used to convert the Source buffer into ASCII.
NOTE: BufferSize should include the NULL-Terminator, while the return value does not.
Arguements:
Destination - ASCII buffer to place the converted values.
Source - WCHAR buffer containing the string to convert.
BufferSize - Size, in bytes, of Destination.
Return Value:
The converted buffer and the count of converted chars. The NULL-termination
isn't included.
--*/
{
ULONG convertedCount = 0;
ULONG i;
RtlZeroMemory(Destination, BufferSize);
if (Source) {
//
// Run through the Source buffer and convert the WCHAR to ASCII, placing
// the converted value in Destination. Ensure that the destination buffer
// isn't overflowed.
//
for (i = 0; (i < (BufferSize - 1)) && (*Source); i++, convertedCount++) {
*Destination = (UCHAR)*Source;
Destination++;
Source++;
}
}
return convertedCount;
}
ULONG
AsciiToWChar(
IN PWCHAR Destination,
IN PUCHAR Source,
IN ULONG BufferSize
)
/*+++
Routine Description:
This routine is used to convert the Source buffer into WCHAR.
NOTE: BufferSize should include the NULL-Terminator, while the return value does not.
Arguements:
Destination - WCHAR buffer to place the converted values.
Source - ASCII buffer containing the string to convert.
BufferSize - Size, in bytes, of Destination.
Return Value:
The converted buffer and the count of converted chars. The NULL-termination
isn't included.
--*/
{
ULONG convertedCount = 0;
ULONG i;
RtlZeroMemory(Destination, BufferSize);
if (Source) {
//
// Run through source buffer and convert the ascii values to WCHAR and put
// the convert into Destination. Ensure that neither source nor dest are
// overflowed.
//
for (i = 0; (i < (BufferSize - 1)) && (*Source); i++, convertedCount++) {
*Destination = (WCHAR)*Source;
Destination++;
Source++;
}
}
return convertedCount;
}
NTSTATUS
PortAsciiToUnicode(
IN PUCHAR AsciiString,
OUT PUNICODE_STRING UnicodeString
)
{
ANSI_STRING ansiString;
//
// Convert ValueName to Unicode.
//
RtlInitAnsiString(&ansiString, AsciiString);
return RtlAnsiStringToUnicodeString(UnicodeString, &ansiString, TRUE);
}
NTSTATUS
PortpBinaryReadCallBack(
IN PWSTR ValueName,
IN ULONG Type,
IN PVOID Buffer,
IN ULONG BufferLength,
IN PVOID Context,
IN PVOID EntryContext
)
/*++
Routine Description:
This routine is the callback function for handling REG_BINARY reads. It will determine
whether the buffer in the PORT_REGISTRY_INFO is of sufficient size to handle the data,
and copy it over, if necessary. Otherwise the data length needed is updated.
Arguments:
ValueName - The name of the data to be returned.
Type - The reg. data type for this request.
ValueLength - Size, in bytes, of ValueData
Context - Not used.
PortContext - Blob containing the miniports buffer and it's size.
Return Value:
SUCCESS or BUFFER_TOO_SMALL (which unfortunately gets updated by the RTL function to
SUCCESS. InternalStatus is updated to the real status and the length field within
the REGISTRY_INFO struct. is updated.
--*/
{
PPORT_REGISTRY_INFO portContext = EntryContext;
PUCHAR currentBuffer;
NTSTATUS status = STATUS_BUFFER_TOO_SMALL;
//
// Determine whether the supplied buffer is big enough to hold the data.
//
if (portContext->CurrentLength >= BufferLength) {
//
// Determine the correct offset into the buffer.
//
currentBuffer = portContext->Buffer + portContext->Offset;
//
// The Rtl routine will free it's buffer, so get the data now.
//
RtlCopyMemory(currentBuffer, Buffer, BufferLength);
//
// Update the status to show the data in the buffer is valid.
//
status = STATUS_SUCCESS;
}
//
// Update the length. This will either tell them how big the buffer
// should be, or how large the returned data actually is.
// The Read routine will handle the rest.
//
portContext->CurrentLength = BufferLength;
portContext->InternalStatus = status;
//
// Return the status to the Read routine.
//
return status;
}
NTSTATUS
PortRegistryRead(
IN PUNICODE_STRING RegistryKeyName,
IN PUNICODE_STRING ValueName,
IN ULONG Type,
IN PPORT_REGISTRY_INFO PortContext
)
/*++
Routine Description:
This routine is used by the portdriver to read the info. at ValueName from the regkey
This assumes that the data is a REG_SZ, REG_DWORD, or REG_BINARY only.
REG_SZ data is converted into a NULL-terminiated ASCII string from the UNICODE.
DWORD and BINARY data are directly copied into the caller's buffer if it's of
correct size.
Arguments:
RegistryKeyName - The absolute key name where ValueName lives.
ValueName - The name of the data to be returned.
Type - The reg. data type for this request.
PortContext - Blob containing the miniports buffer and it's size.
Return Value:
STATUS of the registry routines, INSUFFICIENT_RESOURCES, or BUFFER_TOO_SMALL.
If TOO_SMALL, LengthNeeded within the PortContext is updated to reflect the size needed.
--*/
{
RTL_QUERY_REGISTRY_TABLE queryTable[2];
WCHAR defaultData[] = { L"\0" };
ULONG defaultUlong = (ULONG)-1;
UNICODE_STRING unicodeString;
NTSTATUS status;
ULONG length;
PUCHAR currentBuffer;
RtlZeroMemory(queryTable, sizeof(queryTable));
//
// Calculate the actual buffer location where this data should go.
// This presupposes that the port-driver has done all the validation, otherwise
// this will be blindly copying into who-knows-where.
//
currentBuffer = PortContext->Buffer + PortContext->Offset;
//
// Looking for what lives at ValueName.
//
queryTable[0].Name = ValueName->Buffer;
//
// Indicate that there is no call-back routine and to return everything as one big
// blob.
//
queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_NOEXPAND;
//
// Handle setting up for the various types that are supported.
//
if (Type == REG_SZ) {
RtlZeroMemory(&unicodeString, sizeof(UNICODE_STRING));
//
// Local storage for the returned data. The routine will allocate the buffer
// and set Length.
//
queryTable[0].EntryContext = &unicodeString;
queryTable[0].DefaultData = defaultData;
queryTable[0].DefaultLength = sizeof(defaultData);
} else if (Type == REG_DWORD) {
//
// The data will be placed in the first ulong of the caller's
// buffer.
//
queryTable[0].EntryContext = currentBuffer;
queryTable[0].DefaultData = &defaultUlong;
queryTable[0].DefaultLength = sizeof(ULONG);
} else {
//
// Clear the flags because we need a callback to determine
// the real size of the binary data.
//
queryTable[0].Flags = 0;
queryTable[0].QueryRoutine = PortpBinaryReadCallBack;
queryTable[0].EntryContext = PortContext;
queryTable[0].DefaultData = &defaultUlong;
queryTable[0].DefaultLength = sizeof(ULONG);
}
//
// Set the type.
//
queryTable[0].DefaultType = Type;
//
// Call the query routine. This will either be direct, or result in the callback
// function getting invoked.
//
status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
RegistryKeyName->Buffer,
queryTable,
NULL,
NULL);
if (NT_SUCCESS(status)) {
if (Type == REG_SZ) {
//
// Have their data. Now figure out whether it fits. The Query function allocates
// the unicode string buffer.
//
if ((unicodeString.Length) &&
((unicodeString.Length / sizeof(WCHAR)) <= PortContext->CurrentLength)) {
//
// Magically convert.
//
length = WCharToAscii(currentBuffer,
unicodeString.Buffer,
PortContext->CurrentLength);
//
// Set the length of the buffer.
//
PortContext->CurrentLength = length;
} else {
ASSERT(unicodeString.Length);
//
// Update the Length to indicate how big it should actually be.
//
PortContext->LengthNeeded = (unicodeString.Length + 1) / sizeof(WCHAR);
PortContext->CurrentLength = 0;
status = STATUS_BUFFER_TOO_SMALL;
}
//
// Free our string, as the data has already been copied, or won't be copied
// into the caller's buffer.
//
ExFreePool(unicodeString.Buffer);
} else if (Type == REG_DWORD) {
//
// The data should already be there.
//
PortContext->CurrentLength = sizeof(ULONG);
} else {
//
// The Rtl routine has this annoying effect of fixing up BUFFER_TOO_SMALL
// into SUCCESS. Check for this case.
//
if (PortContext->InternalStatus == STATUS_BUFFER_TOO_SMALL) {
//
// Reset the status correctly for the caller.
//
status = PortContext->InternalStatus;
//
// Update length needed, so that the miniport can realloc.
//
PortContext->LengthNeeded = PortContext->CurrentLength;
PortContext->CurrentLength = 0;
} else {
//
// The callback did all the necessary work.
//
NOTHING;
}
}
} else {
//
// Indicate to the caller that the error is NOT due to a length mismatch or
// that the Buffer is too small. The difference is that if too small, the callback
// routine adjusted CurrentLength.
//
PortContext->LengthNeeded = PortContext->CurrentLength;
PortContext->CurrentLength = 0;
}
return status;
}
NTSTATUS
PortRegistryWrite(
IN PUNICODE_STRING RegistryKeyName,
IN PUNICODE_STRING ValueName,
IN ULONG Type,
IN PPORT_REGISTRY_INFO PortContext
)
/*++
Routine Description:
This routine is used by the port-driver to write the contents of Buffer to ValueName
which is located at the reg. key RegistryKeyName.
Buffer is first converted to UNICODE then the write takes place.
Arguments:
RegistryKeyName - The absolute path to the key name.
ValueName - The name of the data to be written.
Type - The reg. data type for this operation.
PortContext - Blob containing the miniports buffer and it's size.
Return Value:
STATUS from the registry routines, or INSUFFICIENT_RESOURCES
--*/
{
UNICODE_STRING unicodeString;
ULONG bufferLength;
PUCHAR currentBuffer;
LONG offset;
ULONG length;
NTSTATUS status;
//
// Determine whether the field exists.
//
status = RtlCheckRegistryKey(RTL_REGISTRY_ABSOLUTE,
RegistryKeyName->Buffer);
if (!NT_SUCCESS(status)) {
//
// The key doesn't exist. Create it.
//
status = RtlCreateRegistryKey(RTL_REGISTRY_ABSOLUTE,
RegistryKeyName->Buffer);
}
if (!NT_SUCCESS(status)) {
//
// Can't go on. Return the error to the port-driver and it can figure
// out what best to do.
//
return status;
}
//
// Calculate the actual buffer location where this data lives.
// This presupposes that the port-driver has done all the validation.
//
currentBuffer = PortContext->Buffer + PortContext->Offset;
if (Type == REG_SZ) {
//
// Determine the size needed for the WCHAR.
//
bufferLength = PortContext->CurrentLength * sizeof(WCHAR);
//
// Allocate a buffer to build the converted data in.
//
unicodeString.Buffer = ExAllocatePool(NonPagedPool, bufferLength + sizeof(UNICODE_NULL));
if (unicodeString.Buffer == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(unicodeString.Buffer, bufferLength + sizeof(UNICODE_NULL));
//
// Set the lengths.
//
unicodeString.MaximumLength = (USHORT)(bufferLength + sizeof(UNICODE_NULL));
unicodeString.Length = (USHORT)bufferLength;
//
// Convert it.
//
length = AsciiToWChar(unicodeString.Buffer,
currentBuffer,
unicodeString.Length);
//
// Length is now set for the call below. Get the buffer by resetting
// currentbuffer to that of the unicode string's.
//
currentBuffer = (PUCHAR)unicodeString.Buffer;
} else if (Type == REG_DWORD){
//
// always this size.
//
length = sizeof(ULONG);
} else {
//
// For BINARY use the passed in buffer (currentBuffer) and length.
//
length = PortContext->CurrentLength;
}
//
// Write the data to the specified key/Value
//
status = RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE,
RegistryKeyName->Buffer,
ValueName->Buffer,
Type,
currentBuffer,
length);
return status;
}
NTSTATUS
PortBuildRegKeyName(
IN PUNICODE_STRING RegistryPath,
IN OUT PUNICODE_STRING KeyName,
IN ULONG PortNumber,
IN ULONG Global
)
/*++
Routine Description:
This routine will build the registry keyname to the miniport's
Device(N) key based on RegistryPath and whether the key is for global miniport
data, or specific to one scsiN.
Arguments:
RegistryPath - The path to the miniport's service key.
KeyName - Storage for the whole path.
PortNumber - The adapter ordinal. Valid only if Global is FALSE.
Global - Indicates whether the Device or Device(N) path should be built.
Return Value:
SUCCESS - KeyName is valid.
INSUFFICIENT_RESOURCES
--*/
{
UNICODE_STRING unicodeValue;
UNICODE_STRING tempKeyName;
ANSI_STRING ansiKeyName;
ULONG maxLength;
NTSTATUS status;
UCHAR paramsBuffer[24];
//
// If this is global, it represents ALL adapters being controlled by
// the miniport. Otherwise, it's the scsiN key only.
//
if (Global) {
RtlInitAnsiString(&ansiKeyName, "\\Parameters\\Device");
RtlAnsiStringToUnicodeString(&tempKeyName, &ansiKeyName, TRUE);
} else {
//
// Get the scsiport'N'.
//
sprintf(paramsBuffer, "\\Parameters\\Device%d", PortNumber);
RtlInitAnsiString(&ansiKeyName, paramsBuffer);
RtlAnsiStringToUnicodeString(&tempKeyName, &ansiKeyName, TRUE);
}
//
// The total length will be the size of the path to <services> plus the parameters\device
// string. Add enough for a NULL at the end.
//
maxLength = RegistryPath->MaximumLength + tempKeyName.MaximumLength + 2;
KeyName->Buffer = ExAllocatePool(NonPagedPool, maxLength);
if (KeyName->Buffer == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(KeyName->Buffer, maxLength);
//
// Clone the Reg.Path.
//
KeyName->MaximumLength = (USHORT)maxLength;
RtlCopyUnicodeString(KeyName,
RegistryPath);
//
// Have a copy of the path to the services name. Add the rest of the keyname
// to it.
//
status = RtlAppendUnicodeStringToString(KeyName, &tempKeyName);
//
// Free the buffer allocated above.
//
RtlFreeUnicodeString(&tempKeyName);
return status;
}