Windows2003-3790/drivers/ksfilter/ks/api.c
2020-09-30 16:53:55 +02:00

1249 lines
41 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (C) Microsoft Corporation, 1996 - 1999
Module Name:
api.c
Abstract:
This module contains the general helper functions for dealing with
device objects, interrupts, strings, etc.
--*/
#include "ksp.h"
#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg("PAGECONST")
#endif // ALLOC_DATA_PRAGMA
static const WCHAR MediaCategories[] = L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\MediaCategories\\";
static const WCHAR NodeNameValue[] = L"Name";
static const WCHAR MediumCache[] = L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\MediumCache";
static const WCHAR DosDevicesU[] = L"\\DosDevices";
static const WCHAR WhackWhackDotU[] = L"\\\\.";
#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg()
#endif // ALLOC_DATA_PRAGMA
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, KsAcquireResetValue)
#pragma alloc_text(PAGE, QueryReferenceBusInterface)
#pragma alloc_text(PAGE, ReadNodeNameValue)
#pragma alloc_text(PAGE, KsTopologyPropertyHandler)
#pragma alloc_text(PAGE, KsCreateDefaultSecurity)
#pragma alloc_text(PAGE, KsForwardIrp)
#pragma alloc_text(PAGE, KsForwardAndCatchIrp)
#pragma alloc_text(PAGE, KsSynchronousIoControlDevice)
#pragma alloc_text(PAGE, KsUnserializeObjectPropertiesFromRegistry)
#pragma alloc_text(PAGE, KsCacheMedium)
#endif // ALLOC_PRAGMA
KSDDKAPI
NTSTATUS
NTAPI
KsAcquireResetValue(
IN PIRP Irp,
OUT KSRESET* ResetValue
)
/*++
Routine Description:
Returns the reset value type from an IOCTL_KS_RESET Ioctl.
Arguments:
Irp -
The IOCTL_KS_RESET irp with the value to retrieve.
ResetValue -
The place in which to return the reset value.
Return Value:
Returns STATUS_SUCCESS if the value was retrieved, else an error.
--*/
{
PIO_STACK_LOCATION IrpStack;
ULONG BufferLength;
IrpStack = IoGetCurrentIrpStackLocation(Irp);
BufferLength = IrpStack->Parameters.DeviceIoControl.InputBufferLength;
if (BufferLength < sizeof(*ResetValue)) {
return STATUS_INVALID_PARAMETER;
}
if (Irp->RequestorMode != KernelMode) {
try {
ProbeForRead(
IrpStack->Parameters.DeviceIoControl.Type3InputBuffer,
BufferLength,
sizeof(BYTE));
*ResetValue = *(KSRESET*)IrpStack->Parameters.DeviceIoControl.Type3InputBuffer;
} except (EXCEPTION_EXECUTE_HANDLER) {
return GetExceptionCode();
}
} else {
*ResetValue = *(KSRESET*)IrpStack->Parameters.DeviceIoControl.Type3InputBuffer;
}
if ((ULONG)*ResetValue > KSRESET_END) {
return STATUS_INVALID_PARAMETER;
}
return STATUS_SUCCESS;
}
NTSTATUS
QueryReferenceBusInterface(
IN PDEVICE_OBJECT PnpDeviceObject,
OUT PBUS_INTERFACE_REFERENCE BusInterface
)
/*++
Routine Description:
Queries the bus for the standard information interface.
Arguments:
PnpDeviceObject -
Contains the next device object on the Pnp stack.
PhysicalDeviceObject -
Contains the physical device object which was passed to the FDO during
the Add Device.
BusInterface -
The place in which to return the Reference interface.
Return Value:
Returns STATUS_SUCCESS if the interface was retrieved, else an error.
--*/
{
NTSTATUS Status;
KEVENT Event;
IO_STATUS_BLOCK IoStatusBlock;
PIRP Irp;
PIO_STACK_LOCATION IrpStackNext;
PAGED_CODE();
//
// There is no file object associated with this Irp, so the event may be located
// on the stack as a non-object manager object.
//
KeInitializeEvent(&Event, NotificationEvent, FALSE);
Irp = IoBuildSynchronousFsdRequest(
IRP_MJ_PNP,
PnpDeviceObject,
NULL,
0,
NULL,
&Event,
&IoStatusBlock);
if (Irp) {
Irp->RequestorMode = KernelMode;
Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
IrpStackNext = IoGetNextIrpStackLocation(Irp);
//
// Create an interface query out of the Irp.
//
IrpStackNext->MinorFunction = IRP_MN_QUERY_INTERFACE;
IrpStackNext->Parameters.QueryInterface.InterfaceType = (GUID*)&REFERENCE_BUS_INTERFACE;
IrpStackNext->Parameters.QueryInterface.Size = sizeof(*BusInterface);
IrpStackNext->Parameters.QueryInterface.Version = BUS_INTERFACE_REFERENCE_VERSION;
IrpStackNext->Parameters.QueryInterface.Interface = (PINTERFACE)BusInterface;
IrpStackNext->Parameters.QueryInterface.InterfaceSpecificData = NULL;
Status = IoCallDriver(PnpDeviceObject, Irp);
if (Status == STATUS_PENDING) {
//
// This waits using KernelMode, so that the stack, and therefore the
// event on that stack, is not paged out.
//
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
Status = IoStatusBlock.Status;
}
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
return Status;
}
NTSTATUS
ReadNodeNameValue(
IN PIRP Irp,
IN const GUID* Category,
OUT PVOID NameBuffer
)
/*++
Routine Description:
Queries the "Name" key from the specified category GUID. This is used
by the topology handler to query for the value from the name GUID or
topology GUID. If the buffer length is sizeof(ULONG), then the size of
the buffer needed is returned, else the buffer is filled with the name.
Arguments:
Irp -
Contains the IRP with the property request being handled.
Category -
The GUID to locate the name value for.
NameBuffer -
The place in which to put the value.
Return Value:
Returns STATUS_SUCCESS, else a buffer size or memory error. Always fills
in the IO_STATUS_BLOCK.Information field of the PIRP.IoStatus element
within the IRP. It does not set the IO_STATUS_BLOCK.Status field, nor
complete the IRP however.
--*/
{
OBJECT_ATTRIBUTES ObjectAttributes;
NTSTATUS Status;
HANDLE CategoryKey;
KEY_VALUE_PARTIAL_INFORMATION PartialInfoHeader;
WCHAR RegistryPath[sizeof(MediaCategories) + 39];
UNICODE_STRING RegistryString;
UNICODE_STRING ValueName;
ULONG BytesReturned;
//
// Build the registry key path to the specified category GUID.
//
Status = RtlStringFromGUID(Category, &RegistryString);
if (!NT_SUCCESS(Status)) {
return Status;
}
wcscpy(RegistryPath, MediaCategories);
wcscat(RegistryPath, RegistryString.Buffer);
RtlFreeUnicodeString(&RegistryString);
RtlInitUnicodeString(&RegistryString, RegistryPath);
InitializeObjectAttributes(&ObjectAttributes, &RegistryString, OBJ_CASE_INSENSITIVE, NULL, NULL);
if (!NT_SUCCESS(Status = ZwOpenKey(&CategoryKey, KEY_READ, &ObjectAttributes))) {
return Status;
}
//
// Read the "Name" value beneath this category key.
//
RtlInitUnicodeString(&ValueName, NodeNameValue);
Status = ZwQueryValueKey(
CategoryKey,
&ValueName,
KeyValuePartialInformation,
&PartialInfoHeader,
sizeof(PartialInfoHeader),
&BytesReturned);
//
// Even if the read did not cause an overflow, just take the same
// code path, as such a thing would not normally happen.
//
if ((Status == STATUS_BUFFER_OVERFLOW) || NT_SUCCESS(Status)) {
ULONG BufferLength;
BufferLength = IoGetCurrentIrpStackLocation(Irp)->Parameters.DeviceIoControl.OutputBufferLength;
//
// Determine if this is just a query for the length of the
// buffer needed, or a query for the actual data.
//
if (!BufferLength) {
//
// Return just the size of the string needed.
//
Irp->IoStatus.Information = BytesReturned - FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
Status = STATUS_BUFFER_OVERFLOW;
} else if (BufferLength < BytesReturned - FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data)) {
Status = STATUS_BUFFER_TOO_SMALL;
} else {
PKEY_VALUE_PARTIAL_INFORMATION PartialInfoBuffer;
//
// Allocate a buffer for the actual size of data needed.
//
PartialInfoBuffer = ExAllocatePoolWithTag(
PagedPool,
BytesReturned,
'vnSK');
if (PartialInfoBuffer) {
//
// Retrieve the actual name.
//
Status = ZwQueryValueKey(
CategoryKey,
&ValueName,
KeyValuePartialInformation,
PartialInfoBuffer,
BytesReturned,
&BytesReturned);
if (NT_SUCCESS(Status)) {
//
// Make sure that there is always a value.
//
if (!PartialInfoBuffer->DataLength || (PartialInfoBuffer->Type != REG_SZ)) {
Status = STATUS_UNSUCCESSFUL;
} else {
RtlCopyMemory(
NameBuffer,
PartialInfoBuffer->Data,
PartialInfoBuffer->DataLength);
Irp->IoStatus.Information = PartialInfoBuffer->DataLength;
}
}
ExFreePool(PartialInfoBuffer);
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
}
}
ZwClose(CategoryKey);
return Status;
}
KSDDKAPI
NTSTATUS
NTAPI
KsTopologyPropertyHandler(
IN PIRP Irp,
IN PKSPROPERTY Property,
IN OUT PVOID Data,
IN const KSTOPOLOGY* Topology
)
/*++
Routine Description:
Performs standard handling of the static members of the
KSPROPSETID_Topology property set.
Arguments:
Irp -
Contains the IRP with the property request being handled.
Property -
Contains the specific property being queried.
Data -
Contains the topology property specific data.
Topology -
Points to a structure containing the topology information
for this object.
Return Value:
Returns STATUS_SUCCESS, else an error specific to the property being
handled. Always fills in the IO_STATUS_BLOCK.Information field of the
PIRP.IoStatus element within the IRP. It does not set the
IO_STATUS_BLOCK.Status field, nor complete the IRP however.
--*/
{
PAGED_CODE();
switch (Property->Id) {
case KSPROPERTY_TOPOLOGY_CATEGORIES:
//
// Return the Category list for this object.
//
return KsHandleSizedListQuery(Irp, Topology->CategoriesCount, sizeof(*Topology->Categories), Topology->Categories);
case KSPROPERTY_TOPOLOGY_NODES:
//
// Return the Node list for this object.
//
return KsHandleSizedListQuery(Irp, Topology->TopologyNodesCount, sizeof(*Topology->TopologyNodes), Topology->TopologyNodes);
case KSPROPERTY_TOPOLOGY_CONNECTIONS:
//
// Return the Connection list for this object.
//
return KsHandleSizedListQuery(Irp, Topology->TopologyConnectionsCount, sizeof(*Topology->TopologyConnections), Topology->TopologyConnections);
case KSPROPERTY_TOPOLOGY_NAME:
{
ULONG NodeId;
//
// Return the name of the requested node.
//
NodeId = *(PULONG)(Property + 1);
if (NodeId >= Topology->TopologyNodesCount) {
return STATUS_INVALID_PARAMETER;
}
//
// First attempt to retrieve the name based on a specific name GUID.
// This name list is optional, and the specific entry is also optional.
//
if (Topology->TopologyNodesNames &&
!IsEqualGUIDAligned(&Topology->TopologyNodesNames[NodeId], &GUID_NULL)) {
//
// The entry must be in the registry if the device specifies
// a name.
//
return ReadNodeNameValue(Irp, &Topology->TopologyNodesNames[NodeId], Data);
}
//
// Default to using the GUID of the topology node type.
//
return ReadNodeNameValue(Irp, &Topology->TopologyNodes[NodeId], Data);
}
}
return STATUS_NOT_FOUND;
}
KSDDKAPI
NTSTATUS
NTAPI
KsCreateDefaultSecurity(
IN PSECURITY_DESCRIPTOR ParentSecurity OPTIONAL,
OUT PSECURITY_DESCRIPTOR* DefaultSecurity
)
/*++
Routine Description:
Creates a security descriptor with default security, optionally inheriting
parameters from a parent security descriptor. This is used when initializing
sub-objects which do not have any stored security.
Arguments:
ParentSecurity -
Optionally contains the parent object's security which describes inherited
security parameters.
DefaultSecurity -
Points to the place in which to put the returned default security descriptor.
Return Value:
Returns STATUS_SUCCESS, else a resource or assignment error.
--*/
{
#ifndef WIN9X_KS
NTSTATUS Status;
SECURITY_SUBJECT_CONTEXT SubjectContext;
PAGED_CODE();
SeCaptureSubjectContext(&SubjectContext);
Status = SeAssignSecurity(ParentSecurity,
NULL,
DefaultSecurity,
FALSE,
&SubjectContext,
IoGetFileObjectGenericMapping(),
NonPagedPool);
SeReleaseSubjectContext(&SubjectContext);
return Status;
#else
if (!(*DefaultSecurity = ExAllocatePoolWithTag(PagedPool, sizeof(ParentSecurity), 'snSK'))) {
return STATUS_INSUFFICIENT_RESOURCES;
}
return STATUS_SUCCESS;
#endif
}
KSDDKAPI
NTSTATUS
NTAPI
KsForwardIrp(
IN PIRP Irp,
IN PFILE_OBJECT FileObject,
IN BOOLEAN ReuseStackLocation
)
/*++
Routine Description:
This function is used with non-stacked devices which communicate via file
object.
Forwards an IRP to the specified driver after initializing the next stack
location if needed, and setting the file object. This is useful when the
parameters of the forwarded IRP do not change, as it optionally copies the
current stack parameters to the next stack location, other than the
FileObject. If a new stack location is used, it verifies that there is such
a new stack location to copy into before attempting to do so. If there is
not, the Irp is completed with STATUS_INVALID_DEVICE_REQUEST.
Arguments:
Irp -
Contains the IRP which is being forwarded to the specified driver.
FileObject -
Contains the file object to initialize the next stack with.
ReuseStackLocation -
If this is set to TRUE, the current stack location is reused when the
Irp is forwarded, else the parameters are copied to the next stack
location.
Return Value:
Returns the result of IoCallDriver, or an invalid status if no more stack
depth is available.
--*/
{
PAGED_CODE();
if (ReuseStackLocation) {
//
// No new stack location will be used. Set the new File Object.
//
IoGetCurrentIrpStackLocation(Irp)->FileObject = FileObject;
IoSkipCurrentIrpStackLocation(Irp);
return IoCallDriver(IoGetRelatedDeviceObject(FileObject), Irp);
} else {
//
// Ensure that there is another stack location before copying parameters.
//
ASSERT(Irp->CurrentLocation > 1);
if (Irp->CurrentLocation > 1) {
//
// Copy everything, then rewrite the file object.
//
IoCopyCurrentIrpStackLocationToNext(Irp);
IoGetNextIrpStackLocation(Irp)->FileObject = FileObject;
return IoCallDriver(IoGetRelatedDeviceObject(FileObject), Irp);
}
}
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_INVALID_DEVICE_REQUEST;
}
NTSTATUS
KsiCompletionRoutine(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PKEVENT Event
)
/*++
Routine Description:
This function is used to stop further processing on an Irp which has been
passed to KsForwardAndCatchIrp. It signals a event which has been passed
in the context parameter to indicate that the Irp processing is complete.
It then returns STATUS_MORE_PROCESSING_REQUIRED in order to stop processing
on this Irp.
Arguments:
DeviceObject -
Contains the device which set up this completion routine.
Irp -
Contains the Irp which is being stopped.
Event -
Contains the event which is used to signal that this Irp has been
completed.
Return Value:
Returns STATUS_MORE_PROCESSING_REQUIRED in order to stop processing on the
Irp.
--*/
{
//
// This will allow the KsForwardAndCatchIrp call to continue on its way.
//
KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
//
// This will ensure that nothing else touches the Irp, since the original
// caller has now continued, and the Irp may not exist anymore.
//
return STATUS_MORE_PROCESSING_REQUIRED;
}
KSDDKAPI
NTSTATUS
NTAPI
KsForwardAndCatchIrp(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PFILE_OBJECT FileObject,
IN KSSTACK_USE StackUse
)
/*++
Routine Description:
This function is used with devices which may be stacked, and may not use
file objects to communicate.
Forwards an IRP to the specified driver after initializing the next
stack location, and regains control of the Irp on completion from that
driver. If a file object is being used, the caller must place initialize
the current stack location with that file object before calling. Verifies
that there is a new stack location to copy into before attempting to do
so. If there is not, the function returns STATUS_INVALID_DEVICE_REQUEST.
In either case the Irp will not have been completed.
Arguments:
DeviceObject -
Contains the device to forward the Irp to.
Irp -
Contains the Irp which is being forwarded to the specified driver.
FileObject -
Contains a File Object value to copy to the next stack location.
This may be NULL in order to set no File Object, but is always
copied to the next stack location. If the current File Object is to
be preserved, it must be passed in this parameter.
StackUse -
If the value is KsStackCopyToNewLocation, the parameters are copied
to the next stack location. If the value is KsStackReuseCurrentLocation,
the current stack location is reused when the Irp is forwarded, and the
stack location is returned to the current location. If the value is
KsStackUseNewLocation, the new stack location is used as is.
Return Value:
Returns the result of IoCallDriver, or an invalid status if no more stack
depth is available.
--*/
{
NTSTATUS Status;
KEVENT Event;
UCHAR Control;
PIO_COMPLETION_ROUTINE CompletionRoutine;
PVOID Context;
PAGED_CODE();
if (StackUse == KsStackReuseCurrentLocation) {
PIO_STACK_LOCATION IrpStack;
//
// No new stack location will be used. The completion routine and
// associated parameters must be saved.
//
IrpStack = IoGetCurrentIrpStackLocation(Irp);
Control = IrpStack->Control;
CompletionRoutine = IrpStack->CompletionRoutine;
Context = IrpStack->Context;
IrpStack->FileObject = FileObject;
IoSkipCurrentIrpStackLocation(Irp);
} else {
//
// Ensure that there is another stack location before copying parameters.
//
ASSERT(Irp->CurrentLocation > 1);
if (Irp->CurrentLocation > 1) {
if (StackUse == KsStackCopyToNewLocation) {
//
// Just copy the current stack. The new File Object is set below.
//
IoCopyCurrentIrpStackLocationToNext(Irp);
}
} else {
return STATUS_INVALID_DEVICE_REQUEST;
}
}
//
// Set up a completion routine so that the Irp is not actually
// completed. Thus the caller can get control of the Irp back after
// this next driver is done with it.
//
KeInitializeEvent(&Event, NotificationEvent, FALSE);
IoGetNextIrpStackLocation(Irp)->FileObject = FileObject;
IoSetCompletionRoutine(Irp, KsiCompletionRoutine, &Event, TRUE, TRUE, TRUE);
Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING) {
//
// Wait for completion which will occur when the CompletionRoutine
// signals this event. After that point nothing else will be
// touching the Irp. Wait in KernelMode so that the current stack
// is not paged out, since there is an event object on this stack.
//
KeWaitForSingleObject(
&Event,
Suspended,
KernelMode,
FALSE,
NULL);
Status = Irp->IoStatus.Status;
}
if (StackUse == KsStackReuseCurrentLocation) {
PIO_STACK_LOCATION IrpStack;
//
// Set the stack location back to what it was.
//
Irp->CurrentLocation--;
Irp->Tail.Overlay.CurrentStackLocation--;
//
// Put the completion routine and associated parameters back.
//
IrpStack = IoGetCurrentIrpStackLocation(Irp);
IrpStack->Control = Control;
IrpStack->CompletionRoutine = CompletionRoutine;
IrpStack->Context = Context;
}
return Status;
}
KSDDKAPI
NTSTATUS
NTAPI
KsSynchronousIoControlDevice(
IN PFILE_OBJECT FileObject,
IN KPROCESSOR_MODE RequestorMode,
IN ULONG IoControl,
IN PVOID InBuffer,
IN ULONG InSize,
OUT PVOID OutBuffer,
IN ULONG OutSize,
OUT PULONG BytesReturned
)
/*++
Routine Description:
Performs a synchronous device I/O control on the target device object.
Waits in a non-alertable state until the I/O completes. This function
may only be called at PASSIVE_LEVEL.
Arguments:
FileObject -
The file object to fill in the first stack location with.
RequestorMode -
Indicates the processor mode to place in the IRP if one is needs to
be generated. This allows a caller to force a KernelMode request no
matter what Previous Mode currently is.
IoControl -
Contains the I/O control to send.
InBuffer -
Points to the device input buffer. This buffer is assumed to be valid.
InSize -
Contains the size in bytes of the device input buffer.
OutBuffer -
Points to the device output buffer. This buffer is assumed to be valid.
OutSize -
Contains the size in bytes of the device output buffer.
BytesReturned -
Points to the place in which to put the number of bytes returned.
Return Value:
Returns the result of the device I/O control.
--*/
{
PDEVICE_OBJECT DeviceObject;
NTSTATUS Status;
KEVENT Event;
IO_STATUS_BLOCK IoStatusBlock;
PIRP Irp;
ASSERT( KeGetCurrentIrql() == PASSIVE_LEVEL );
DeviceObject = IoGetRelatedDeviceObject(FileObject);
//
// Since there is no way for the recipient to determine the requestor mode other
// than looking at PreviousMode, then if the requestor mode is not KernelMode,
// and it does not match PreviousMode, Fast I/O cannot be used.
//
if ((RequestorMode != KernelMode) || (ExGetPreviousMode() == KernelMode)) {
//
// Check to see if there is even a Fast I/O dispatch table, and a Device
// Control entry in it.
//
if (DeviceObject->DriverObject->FastIoDispatch &&
DeviceObject->DriverObject->FastIoDispatch->FastIoDeviceControl) {
//
// Either the request was handled (by succeeding or failing), or it
// could not be done synchronously, or by the Fast I/O handler.
//
if (DeviceObject->DriverObject->FastIoDispatch->FastIoDeviceControl(
FileObject,
TRUE,
InBuffer,
InSize,
OutBuffer,
OutSize,
IoControl,
&IoStatusBlock,
DeviceObject)) {
*BytesReturned = (ULONG)IoStatusBlock.Information;
return IoStatusBlock.Status;
}
}
}
//
// Fast I/O did not work, so use and Irp.
//
KeInitializeEvent(&Event, NotificationEvent, FALSE);
Irp = IoBuildDeviceIoControlRequest(
IoControl,
DeviceObject,
InBuffer,
InSize,
OutBuffer,
OutSize,
FALSE,
&Event,
&IoStatusBlock);
if (Irp) {
//
// Set the mode selected rather than using Previous Mode.
//
Irp->RequestorMode = RequestorMode;
//
// This is dereferenced by the completion routine.
//
Irp->Tail.Overlay.OriginalFileObject = FileObject;
ObReferenceObject(FileObject);
//
// Allows use of an event which has not been allocated by the object
// manager, while still allowing multiple outstanding Irp's to the
// file object. Also makes the assumption that the status block is
// located in a safe address.
//
Irp->Flags |= IRP_SYNCHRONOUS_API;
IoGetNextIrpStackLocation(Irp)->FileObject = FileObject;
Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING) {
//
// This waits using KernelMode, so that the stack, and therefore the
// event on that stack, is not paged out.
//
KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
Status = IoStatusBlock.Status;
}
*BytesReturned = (ULONG)IoStatusBlock.Information;
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
return Status;
}
KSDDKAPI
NTSTATUS
NTAPI
KsUnserializeObjectPropertiesFromRegistry(
IN PFILE_OBJECT FileObject,
IN HANDLE ParentKey OPTIONAL,
IN PUNICODE_STRING RegistryPath OPTIONAL
)
/*++
Routine Description:
Given a destination object and a registry path, enumerate the values and
apply them as serialized data to the specified property sets listed in the
serialized data. An Irp is generated when sending the serialized data, so
no assumption is made on use of KS property structures to internally define
the property sets. The function does not look at or care about the names
of the values.
The Property parameter to the unserialize request sent to the object is
assumed to contain just an identifier, and no context information.
Arguments:
FileObject -
The file object whose properties are being set.
ParentKey -
Optionally contains a handle to the parent of the path, else NULL.
The Parent Key and/or the RegistryPath must be passed.
RegistryPath -
Optionally contains the path to the key whose subkeys will be
enumerated as property sets, else NULL. The Parent Key and/or the
RegistryPath must be passed.
Return Value:
Returns STATUS_SUCCESS if the property sets were unserialized, else an
error if the registry path was invalid, one of the subkeys was invalid,
setting a property failed, the serialized format was invalid, or a
property set was not supported on the object.
--*/
{
OBJECT_ATTRIBUTES ObjectAttributes;
NTSTATUS Status;
HANDLE RootKey;
ULONG ValueIndex;
KEY_VALUE_PARTIAL_INFORMATION PartialInfoHeader;
PAGED_CODE();
//
// This is the key whose subkeys will be enumerated.
//
if (RegistryPath) {
InitializeObjectAttributes(&ObjectAttributes, RegistryPath, OBJ_CASE_INSENSITIVE, ParentKey, NULL);
if (!NT_SUCCESS(Status = ZwOpenKey(&RootKey, KEY_READ, &ObjectAttributes))) {
return Status;
}
} else if (!ParentKey) {
return STATUS_INVALID_PARAMETER_MIX;
} else {
RootKey = ParentKey;
}
//
// Loop through all the values until either no more entries exist, or an
// error occurs.
//
for (ValueIndex = 0;; ValueIndex++) {
ULONG BytesReturned;
PKEY_VALUE_PARTIAL_INFORMATION PartialInfoBuffer;
//
// Retrieve the value size.
//
Status = ZwEnumerateValueKey(
RootKey,
ValueIndex,
KeyValuePartialInformation,
&PartialInfoHeader,
sizeof(PartialInfoHeader),
&BytesReturned);
if ((Status != STATUS_BUFFER_OVERFLOW) && !NT_SUCCESS(Status)) {
//
// Either an error occured, or there are no more entries.
//
if (Status == STATUS_NO_MORE_ENTRIES) {
Status = STATUS_SUCCESS;
}
//
// Exit the loop with a failure or success.
//
break;
}
//
// Allocate a buffer for the actual size of data needed.
//
PartialInfoBuffer = ExAllocatePoolWithTag(
PagedPool,
BytesReturned,
'psSK');
if (PartialInfoBuffer) {
//
// Retrieve the actual serialized data.
//
Status = ZwEnumerateValueKey(
RootKey,
ValueIndex,
KeyValuePartialInformation,
PartialInfoBuffer,
BytesReturned,
&BytesReturned);
if (NT_SUCCESS(Status)) {
if (BytesReturned < FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) + sizeof(KSPROPERTY_SERIALHDR)) {
//
// The data value is not long enough to even hold the
// KSPROPERTY_SERIALHDR, so it must be invalid.
//
Status = STATUS_INVALID_BUFFER_SIZE;
} else {
KSPROPERTY Property;
//
// Unserialize the buffer which was retrieved.
//
Property.Set = ((PKSPROPERTY_SERIALHDR)&PartialInfoBuffer->Data)->PropertySet;
Property.Id = 0;
Property.Flags = KSPROPERTY_TYPE_UNSERIALIZESET;
Status = KsSynchronousIoControlDevice(
FileObject,
KernelMode,
IOCTL_KS_PROPERTY,
&Property,
sizeof(Property),
&PartialInfoBuffer->Data,
PartialInfoBuffer->DataLength,
&BytesReturned);
}
} else if (Status == STATUS_NO_MORE_ENTRIES) {
Status = STATUS_SUCCESS;
}
ExFreePool(PartialInfoBuffer);
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
//
// Any sort of failure just exits the loop.
//
if (!NT_SUCCESS(Status)) {
break;
}
}
//
// A subkey is opened only if a path is passed in, else the ParentKey
// is used.
//
if (RegistryPath) {
ZwClose(RootKey);
}
return Status;
}
KSDDKAPI
NTSTATUS
NTAPI
KsCacheMedium(
IN PUNICODE_STRING SymbolicLink,
IN PKSPIN_MEDIUM Medium,
IN DWORD PinDirection
)
/*++
Routine Description:
To improve performance of graph building of pins which use Mediums to define
connectivity, create a registry key at:
\System\CurrentControlSet\Control\MediumCache\{GUID}\DWord\DWord
This enables fast lookup of connected filters in TvTuner and other complex graphs.
The GUID part is the Medium of the connnection, and the DWords
are used to denote the device instance. The value name is the SymbolicLink for the driver,
and the ActualValue is the pin direction.
Arguments:
SymbolicLink -
The symbolic link used to open the device interface.
Medium -
Points to the Medium to cache.
PinDirection -
Contains the direction of the Pin. 1 is output, 0 is input.
Return Value:
Returns STATUS_SUCCESS on success.
--*/
{
NTSTATUS Status;
HANDLE KeyHandle;
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING GuidUnicode;
UNICODE_STRING KeyNameUnicode;
UNICODE_STRING TempUnicode;
UNICODE_STRING SymbolicLinkLocalU;
WCHAR * SymbolicLinkLocalUBuf;
WCHAR * KeyNameBuf;
WCHAR TempBuf[16];
ULONG Disposition;
int nCount;
PAGED_CODE();
#define MAX_FILENAME_LENGTH_BYTES (MAXIMUM_FILENAME_LENGTH * sizeof (WCHAR))
if (Medium == NULL ||
IsEqualGUID(&Medium->Set, &KSMEDIUMSETID_Standard) ||
IsEqualGUID(&Medium->Set, &GUID_NULL)) {
// Skip pins with standard or NULL Mediums
return STATUS_SUCCESS;
}
// Make a local copy of the SymbolicLink
if (SymbolicLinkLocalUBuf = ExAllocatePoolWithTag(PagedPool,
MAX_FILENAME_LENGTH_BYTES,
'cmSK')) {
SymbolicLinkLocalU.Length = 0;
SymbolicLinkLocalU.MaximumLength = MAX_FILENAME_LENGTH_BYTES;
SymbolicLinkLocalU.Buffer = SymbolicLinkLocalUBuf;
RtlCopyUnicodeString(&SymbolicLinkLocalU, SymbolicLink);
}
else {
return STATUS_INSUFFICIENT_RESOURCES;
}
// KeyNameBuf is where the DeviceName + Medium goes
if (KeyNameBuf = ExAllocatePoolWithTag(PagedPool,
MAX_FILENAME_LENGTH_BYTES,
'cmSK')) {
KeyNameUnicode.Length = 0;
KeyNameUnicode.Buffer = KeyNameBuf;
KeyNameUnicode.MaximumLength = MAX_FILENAME_LENGTH_BYTES;
RtlZeroMemory (KeyNameBuf, MAX_FILENAME_LENGTH_BYTES);
}
else {
ExFreePool(SymbolicLinkLocalUBuf);
return STATUS_INSUFFICIENT_RESOURCES;
}
TempUnicode.Length = 0;
TempUnicode.Buffer = TempBuf;
TempUnicode.MaximumLength = sizeof (TempBuf);
GuidUnicode.Length = 0;
GuidUnicode.Buffer = NULL;
GuidUnicode.MaximumLength = 0;
Status = RtlAppendUnicodeToString (&KeyNameUnicode, MediumCache);
// Win2K won't make subkeys if the parent doesn't exist, so ensure we can open the above string
InitializeObjectAttributes(&ObjectAttributes,
&KeyNameUnicode,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
// Open the key
Status = ZwCreateKey(&KeyHandle,
KEY_ALL_ACCESS, // IN ACCESS_MASK DesiredAccess,
&ObjectAttributes, // IN POBJECT_ATTRIBUTES ObjectAttributes,
0, // IN ULONG TitleIndex,
NULL, // IN PUNICODE_STRING Class OPTIONAL,
REG_OPTION_NON_VOLATILE, // IN ULONG CreateOptions,
&Disposition // OUT PULONG Disposition OPTIONAL
);
if (!NT_SUCCESS(Status)) {
ExFreePool(SymbolicLinkLocalUBuf);
ExFreePool(KeyNameBuf);
return Status;
}
ZwClose(KeyHandle);
Status = RtlAppendUnicodeToString(&KeyNameUnicode, L"\\");
Status = RtlStringFromGUID(&Medium->Set, &GuidUnicode); // allocates string
Status = RtlAppendUnicodeStringToString(&KeyNameUnicode, &GuidUnicode);
RtlFreeUnicodeString (&GuidUnicode); // free it
Status = RtlAppendUnicodeToString(&KeyNameUnicode, L"-");
Status = RtlIntegerToUnicodeString(Medium->Id, 16, &TempUnicode);
Status = RtlAppendUnicodeStringToString(&KeyNameUnicode, &TempUnicode);
Status = RtlAppendUnicodeToString(&KeyNameUnicode, L"-");
Status = RtlIntegerToUnicodeString(Medium->Flags, 16, &TempUnicode);
Status = RtlAppendUnicodeStringToString(&KeyNameUnicode, &TempUnicode);
// At this point, KeyNameUnicode looks like:
//
// \System\CurrentControlSet\Control\MediumCache\
//
// | GUID | | Id | | Flags|
// {00000000-0000-0000-0000-000000000000}-00000000-00000000
//
// At this key, add an entry containing the symbolic link, with
// the DWORD value indicating pin direction.
InitializeObjectAttributes(&ObjectAttributes,
&KeyNameUnicode,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
// Open the key
Status = ZwCreateKey(&KeyHandle,
KEY_ALL_ACCESS, // IN ACCESS_MASK DesiredAccess,
&ObjectAttributes, // IN POBJECT_ATTRIBUTES ObjectAttributes,
0, // IN ULONG TitleIndex,
NULL, // IN PUNICODE_STRING Class OPTIONAL,
REG_OPTION_NON_VOLATILE, // IN ULONG CreateOptions,
&Disposition // OUT PULONG Disposition OPTIONAL
);
#if DBG
if (!NT_SUCCESS(Status)) {
_DbgPrintF(DEBUGLVL_VERBOSE,
("KsCacheMedium: Key = %S, Status = %x\n",
KeyNameUnicode.Buffer, Status));
}
#endif
if (NT_SUCCESS(Status)) {
// On Win9x, the SymbolicLinkListU here starts with "\DosDevices\#000..."
// format but NTCreateFile requires the "\\.\#0000..." format. So convert the
// string if necessary.
// On Win2K the rules are (of course) different. Here, translate from
// "\??\..." to "\\?\..."
nCount = (int)RtlCompareMemory (SymbolicLinkLocalU.Buffer,
DosDevicesU,
sizeof (DosDevicesU) - 2); // Don't compare the NULL
if (nCount == sizeof (DosDevicesU) - 2) {
// W98: Replace \DosDevices with \\.\ and copy the rest of the string down
PWCHAR pSrcU = SymbolicLinkLocalU.Buffer + SIZEOF_ARRAY (DosDevicesU) - 1;
PWCHAR pDstU = SymbolicLinkLocalU.Buffer + SIZEOF_ARRAY (WhackWhackDotU) - 1;
RtlCopyMemory (SymbolicLinkLocalU.Buffer, WhackWhackDotU, sizeof (WhackWhackDotU));
while (*pDstU++ = *pSrcU++);
}
else if (SymbolicLinkLocalU.Buffer[1] == '?' && SymbolicLinkLocalU.Buffer[2] == '?') {
// Win2K: translate from "\??\..." to "\\?\..."
SymbolicLinkLocalU.Buffer[1] = '\\';
}
// Write the key
Status = ZwSetValueKey(KeyHandle,
&SymbolicLinkLocalU, // IN PUNICODE_STRING ValueName,
0, // IN ULONG TitleIndex OPTIONAL,
REG_DWORD, // IN ULONG Type,
(PVOID)&PinDirection, // IN PVOID Data,
sizeof (PinDirection) // IN ULONG DataSize
);
_DbgPrintF(DEBUGLVL_VERBOSE, ("MediumCache: Status = %d\n",
Status));
_DbgPrintF(DEBUGLVL_VERBOSE, ("MediumCache: KeyNameUnicode = %S, length=%d\n",
KeyNameUnicode.Buffer, KeyNameUnicode.Length));
_DbgPrintF(DEBUGLVL_VERBOSE, ("MediumCache: SymbolicLink = %S, length=%d\n",
SymbolicLinkLocalU.Buffer, SymbolicLinkLocalU.Length));
ZwClose (KeyHandle);
}
ExFreePool(SymbolicLinkLocalUBuf);
ExFreePool(KeyNameBuf);
return Status;
}