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

974 lines
38 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:
method.c
Abstract:
This module contains the helper functions for method sets, and
method set handler code. These allow a device object to present a
method set to a client, and allow the helper function to perform some
of the basic parameter validation and routing based on a method set
table.
--*/
#include "ksp.h"
#ifdef ALLOC_PRAGMA
const KSMETHOD_ITEM*
FASTCALL
FindMethodItem(
IN const KSMETHOD_SET* MethodSet,
IN ULONG MethodItemSize,
IN ULONG MethodId
);
const KSFASTMETHOD_ITEM*
FASTCALL
FindFastMethodItem(
IN const KSMETHOD_SET* MethodSet,
IN ULONG MethodId
);
#pragma alloc_text(PAGE, FindMethodItem)
#pragma alloc_text(PAGE, KsMethodHandler)
#pragma alloc_text(PAGE, KsMethodHandlerWithAllocator)
#pragma alloc_text(PAGE, KspMethodHandler)
#pragma alloc_text(PAGE, FindFastMethodItem)
#pragma alloc_text(PAGE, KsFastMethodHandler)
#endif // ALLOC_PRAGMA
const KSMETHOD_ITEM*
FASTCALL
FindMethodItem(
IN const KSMETHOD_SET* MethodSet,
IN ULONG MethodItemSize,
IN ULONG MethodId
)
/*++
Routine Description:
Given an method set structure locates the specified method item.
Arguments:
MethodSet -
Points to the method set to search.
MethodItemSize -
Contains the size of each Method item. This may be different
than the standard method item size, since the items could be
allocated on the fly, and contain context information.
MethodId -
Contains the method identifier to look for.
Return Value:
Returns a pointer to the method identifier structure, or NULL if it could
not be found.
--*/
{
const KSMETHOD_ITEM* MethodItem;
ULONG MethodsCount;
MethodItem = MethodSet->MethodItem;
for (MethodsCount = MethodSet->MethodsCount;
MethodsCount;
MethodsCount--, MethodItem = (const KSMETHOD_ITEM*)((PUCHAR)MethodItem + MethodItemSize)) {
if (MethodId == MethodItem->MethodId) {
return MethodItem;
}
}
return NULL;
}
KSDDKAPI
NTSTATUS
NTAPI
KsMethodHandler(
IN PIRP Irp,
IN ULONG MethodSetsCount,
IN const KSMETHOD_SET* MethodSet
)
/*++
Routine Description:
Handles method requests. Responds to all method identifiers defined
by the sets. The owner of the method set may then perform pre- or
post-filtering of the method handling. This function may only be
called at PASSIVE_LEVEL.
Arguments:
Irp -
Contains the IRP with the method request being handled.
MethodSetsCount -
Indicates the number of method set structures being passed.
MethodSet -
Contains the pointer to the list of method set information.
Return Value:
Returns STATUS_SUCCESS, else an error specific to the method being
handled. Always sets the IO_STATUS_BLOCK.Information field of the
PIRP.IoStatus element within the IRP, either through setting it to zero
because of an internal error, or through a method handler setting it.
It does not set the IO_STATUS_BLOCK.Status field, nor complete the IRP
however.
--*/
{
PAGED_CODE();
return KspMethodHandler(Irp, MethodSetsCount, MethodSet, NULL, 0, NULL, 0);
}
KSDDKAPI
NTSTATUS
NTAPI
KsMethodHandlerWithAllocator(
IN PIRP Irp,
IN ULONG MethodSetsCount,
IN const KSMETHOD_SET* MethodSet,
IN PFNKSALLOCATOR Allocator OPTIONAL,
IN ULONG MethodItemSize OPTIONAL
)
/*++
Routine Description:
Handles method requests. Responds to all method identifiers defined
by the sets. The owner of the method set may then perform pre- or
post-filtering of the method handling. This function may only be
called at PASSIVE_LEVEL.
Arguments:
Irp -
Contains the IRP with the method request being handled.
MethodSetsCount -
Indicates the number of method set structures being passed.
MethodSet -
Contains the pointer to the list of method set information.
Allocator -
Optionally contains the callback with which a mapped buffer
request will be made. If this is not provided, pool memory
will be used. If specified, this is used to allocate memory
for a method IRP using the callback. This can be used
to allocate specific memory for method requests, such as
mapped memory. Note that this assumes that method Irp's passed
to a filter have not been manipulated before being sent. It is
invalid to directly forward a method Irp.
MethodItemSize -
Optionally contains an alternate method item size to use when
incrementing the current method item counter. If this is a
non-zero value, it is assumed to contain the size of the increment,
and directs the function to pass a pointer to the method item
located in the DriverContext field accessed through the
KSMETHOD_ITEM_IRP_STORAGE macro.
Return Value:
Returns STATUS_SUCCESS, else an error specific to the method being
handled. Always sets the IO_STATUS_BLOCK.Information field of the
PIRP.IoStatus element within the IRP, either through setting it to zero
because of an internal error, or through a method handler setting it.
It does not set the IO_STATUS_BLOCK.Status field, nor complete the IRP
however.
--*/
{
PAGED_CODE();
return KspMethodHandler(Irp, MethodSetsCount, MethodSet, Allocator, MethodItemSize, NULL, 0);
}
NTSTATUS
KspMethodHandler(
IN PIRP Irp,
IN ULONG MethodSetsCount,
IN const KSMETHOD_SET* MethodSet,
IN PFNKSALLOCATOR Allocator OPTIONAL,
IN ULONG MethodItemSize OPTIONAL,
IN const KSAUTOMATION_TABLE*const* NodeAutomationTables OPTIONAL,
IN ULONG NodesCount
)
/*++
Routine Description:
Handles method requests. Responds to all method identifiers defined
by the sets. The owner of the method set may then perform pre- or
post-filtering of the method handling. This function may only be
called at PASSIVE_LEVEL.
Arguments:
Irp -
Contains the IRP with the method request being handled.
MethodSetsCount -
Indicates the number of method set structures being passed.
MethodSet -
Contains the pointer to the list of method set information.
Allocator -
Optionally contains the callback with which a mapped buffer
request will be made. If this is not provided, pool memory
will be used. If specified, this is used to allocate memory
for a method IRP using the callback. This can be used
to allocate specific memory for method requests, such as
mapped memory. Note that this assumes that method Irp's passed
to a filter have not been manipulated before being sent. It is
invalid to directly forward a method Irp.
MethodItemSize -
Optionally contains an alternate method item size to use when
incrementing the current method item counter. If this is a
non-zero value, it is assumed to contain the size of the increment,
and directs the function to pass a pointer to the method item
located in the DriverContext field accessed through the
KSMETHOD_ITEM_IRP_STORAGE macro.
NodeAutomationTables -
Optional table of automation tables for nodes.
NodesCount -
Count of nodes.
Return Value:
Returns STATUS_SUCCESS, else an error specific to the method being
handled. Always sets the IO_STATUS_BLOCK.Information field of the
PIRP.IoStatus element within the IRP, either through setting it to zero
because of an internal error, or through a method handler setting it.
It does not set the IO_STATUS_BLOCK.Status field, nor complete the IRP
however.
--*/
{
PIO_STACK_LOCATION IrpStack;
ULONG InputBufferLength;
ULONG OutputBufferLength;
ULONG AlignedBufferLength;
KSMETHOD LocalMethod;
PVOID UserBuffer;
PKSMETHOD Method;
ULONG NodeId;
ULONG LocalMethodItemSize;
ULONG RemainingSetsCount;
NTSTATUS Status;
ULONG Flags;
PAGED_CODE();
//
// Determine the offsets to both the Method and UserBuffer parameters based
// on the lengths of the DeviceIoControl parameters. A single allocation is
// used to buffer both parameters. The UserBuffer (or results on a support
// query) is stored first, and the Method is stored second, on
// FILE_QUAD_ALIGNMENT.
//
IrpStack = IoGetCurrentIrpStackLocation(Irp);
InputBufferLength = IrpStack->Parameters.DeviceIoControl.InputBufferLength;
OutputBufferLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;
AlignedBufferLength = (OutputBufferLength + FILE_QUAD_ALIGNMENT) & ~FILE_QUAD_ALIGNMENT;
//
// Determine if the parameters have already been buffered by a previous
// call to this function.
//
if (!Irp->AssociatedIrp.SystemBuffer) {
//
// Initially just check for the minimal method parameter length. The
// actual minimal length will be validated when the method item is found.
// Also ensure that the output and input buffer lengths are not set so
// large as to overflow when aligned or added.
//
if ((InputBufferLength < sizeof(*Method)) || (AlignedBufferLength < OutputBufferLength) || (AlignedBufferLength + InputBufferLength < AlignedBufferLength)) {
return STATUS_INVALID_BUFFER_SIZE;
}
//
// Retrieve a pointer to the method for use in searching for a handler.
//
if (Irp->RequestorMode != KernelMode) {
try {
//
// Validate the pointer if the client is not trusted.
//
ProbeForRead(IrpStack->Parameters.DeviceIoControl.Type3InputBuffer, InputBufferLength, sizeof(BYTE));
//
// Get the flags and the node ID.
//
Flags = ((PKSMETHOD)IrpStack->Parameters.DeviceIoControl.Type3InputBuffer)->Flags;
if ((Flags & KSMETHOD_TYPE_TOPOLOGY) && (InputBufferLength >= sizeof(KSM_NODE))) {
NodeId = ((PKSM_NODE)IrpStack->Parameters.DeviceIoControl.Type3InputBuffer)->NodeId;
}
//
// Validate the flags.
//
switch (Flags & ~KSMETHOD_TYPE_TOPOLOGY) {
//
// Allow old flags.
//
case KSMETHOD_TYPE_NONE:
case KSMETHOD_TYPE_READ: //KSMETHOD_TYPE_SEND
case KSMETHOD_TYPE_WRITE:
case KSMETHOD_TYPE_MODIFY:
case KSMETHOD_TYPE_NONE | KSMETHOD_TYPE_SOURCE:
case KSMETHOD_TYPE_READ | KSMETHOD_TYPE_SOURCE:
case KSMETHOD_TYPE_WRITE | KSMETHOD_TYPE_SOURCE:
case KSMETHOD_TYPE_MODIFY | KSMETHOD_TYPE_SOURCE:
//
// Just copy the method for now to use in lookup, as the
// buffer cannot be set up yet.
//
LocalMethod = *(PKSMETHOD)IrpStack->Parameters.DeviceIoControl.Type3InputBuffer;
//
// Remove old flags.
//
LocalMethod.Flags = KSMETHOD_TYPE_SEND | (Flags & KSMETHOD_TYPE_TOPOLOGY);
Method = &LocalMethod;
break;
case KSMETHOD_TYPE_SETSUPPORT:
case KSMETHOD_TYPE_BASICSUPPORT:
if (Irp->RequestorMode != KernelMode) {
ProbeForWrite(Irp->UserBuffer, OutputBufferLength, sizeof(BYTE));
}
//
// The allocator is not used for support calls.
//
Irp->AssociatedIrp.SystemBuffer = ExAllocatePoolWithQuotaTag(NonPagedPool, AlignedBufferLength + InputBufferLength, 'ppSK');
Irp->Flags |= (IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER);
if (OutputBufferLength) {
Irp->Flags |= IRP_INPUT_OPERATION;
}
RtlCopyMemory(
(PUCHAR)Irp->AssociatedIrp.SystemBuffer + AlignedBufferLength,
IrpStack->Parameters.DeviceIoControl.Type3InputBuffer,
InputBufferLength);
Method = (PKSMETHOD)((PUCHAR)Irp->AssociatedIrp.SystemBuffer + AlignedBufferLength);
//
// Overwrite with previously captured flags.
//
Method->Flags = Flags;
//
// Store the flags so that an asynchronous operation can
// determine what type of buffering was used in the method.
// KsDispatchSpecificMethod uses this to determine where
// the Method parameter is.
//
KSMETHOD_TYPE_IRP_STORAGE(Irp) = Method->Flags;
break;
default:
return STATUS_INVALID_PARAMETER;
}
} except (EXCEPTION_EXECUTE_HANDLER) {
return GetExceptionCode();
}
} else {
//
// This is a trusted client, so use the original pointer, since it
// is cheaper than determining where to point into the SystemBuffer.
//
Method = (PKSMETHOD)IrpStack->Parameters.DeviceIoControl.Type3InputBuffer;
}
} else if (KSMETHOD_TYPE_IRP_STORAGE(Irp) & KSMETHOD_TYPE_SOURCE) {
Method = (PKSMETHOD)Irp->AssociatedIrp.SystemBuffer;
} else {
Method = (PKSMETHOD)((PUCHAR)Irp->AssociatedIrp.SystemBuffer + AlignedBufferLength);
}
//
// Optionally call back if this is a node request.
//
Flags = Method->Flags;
if (Flags & KSMETHOD_TYPE_TOPOLOGY) {
//
// Input buffer must include node fields.
//
if (InputBufferLength < sizeof(KSM_NODE)) {
return STATUS_INVALID_BUFFER_SIZE;
}
if (NodeAutomationTables) {
const KSAUTOMATION_TABLE* automationTable;
//
// If the method was not captured in the local buffer, we have not
// extracted the node ID yet. When the local buffer is used, the
// node ID is extracted in the try/except above.
//
if (Method != &LocalMethod) {
NodeId = ((PKSM_NODE) Method)->NodeId;
}
if (NodeId >= NodesCount) {
return STATUS_INVALID_DEVICE_REQUEST;
}
automationTable = NodeAutomationTables[NodeId];
if ((! automationTable) || (automationTable->MethodSetsCount == 0)) {
return STATUS_NOT_FOUND;
}
MethodSetsCount = automationTable->MethodSetsCount;
MethodSet = automationTable->MethodSets;
MethodItemSize = automationTable->MethodItemSize;
}
Flags &= ~KSMETHOD_TYPE_TOPOLOGY;
}
//
// Allow the caller to indicate a size for each method item.
//
if (MethodItemSize) {
ASSERT(MethodItemSize >= sizeof(KSMETHOD_ITEM));
LocalMethodItemSize = MethodItemSize;
} else {
LocalMethodItemSize = sizeof(KSMETHOD_ITEM);
}
//
// Search for the specified Method set within the list of sets given. Don't modify
// the MethodSetsCount so that it can be used later in case this is a query for
// the list of sets supported. Don't do that comparison first (GUID_NULL),
// because it is rare.
//
for (RemainingSetsCount = MethodSetsCount; RemainingSetsCount; MethodSet++, RemainingSetsCount--) {
if (IsEqualGUIDAligned(&Method->Set, MethodSet->Set)) {
const KSMETHOD_ITEM* MethodItem;
if (Flags & KSIDENTIFIER_SUPPORTMASK) {
if (Flags == KSMETHOD_TYPE_SETSUPPORT) {
//
// Querying for support of this set in general.
//
return STATUS_SUCCESS;
}
//
// Else querying for basic support of this set. The data
// parameter must be long enough to contain the flags
// returned.
//
if (OutputBufferLength < sizeof(OutputBufferLength)) {
return STATUS_BUFFER_TOO_SMALL;
}
//
// Attempt to locate the method item within the set already found.
//
if (!(MethodItem = FindMethodItem(MethodSet, LocalMethodItemSize, Method->Id))) {
return STATUS_NOT_FOUND;
}
//
// Some filters want to do their own processing, so a pointer to
// the set is placed in any IRP forwarded.
//
KSMETHOD_SET_IRP_STORAGE(Irp) = MethodSet;
//
// Optionally provide method item context.
//
if (MethodItemSize) {
KSMETHOD_ITEM_IRP_STORAGE(Irp) = MethodItem;
}
//
// The output for the flags is either an allocated system address,
// or it is the original output buffer as passed by a trusted client,
// which must be a system address.
//
UserBuffer = (Irp->RequestorMode == KernelMode) ?
Irp->UserBuffer : Irp->AssociatedIrp.SystemBuffer;
//
// If the item contains an entry for a query support handler of its
// own, then call that handler. The return from that handler
// indicates that:
//
// 1. The item is supported, and the handler filled in the request.
// 2. The item is supported, but the handler did not fill anything in.
// 3. The item is supported, but the handler is waiting to modify
// what is filled in.
// 4. The item is not supported, and an error it to be returned.
// 5. A pending return.
//
if (MethodItem->SupportHandler &&
(!NT_SUCCESS(Status = MethodItem->SupportHandler(Irp, Method, UserBuffer)) ||
(Status != STATUS_SOME_NOT_MAPPED)) &&
(Status != STATUS_MORE_PROCESSING_REQUIRED)) {
//
// If 1) the item is not supported, 2) it is supported and the
// handler filled in the request, or 3) a pending return, then
// return the status. For the case of the item being
// supported, and the handler not filling in the requested
// information, STATUS_SOME_NOT_MAPPED or
// STATUS_MORE_PROCESSING_REQUIRED will continue on with
// default processing.
//
return Status;
} else {
Status = STATUS_SUCCESS;
}
//
// Just return the flags for the type of method this is.
//
*(PULONG)UserBuffer = MethodItem->Flags;
Irp->IoStatus.Information = sizeof(ULONG);
//
// If the handler wants to do some post-processing, then
// pass along the request again. The support handler knows
// that this is the post-processing query because
// Irp->IoStatus.Information is non-zero.
//
if (Status == STATUS_MORE_PROCESSING_REQUIRED) {
return MethodItem->SupportHandler(Irp, Method, UserBuffer);
}
return STATUS_SUCCESS;
}
//
// Attempt to locate the method item within the set already found.
//
if (!(MethodItem = FindMethodItem(MethodSet, LocalMethodItemSize, Method->Id))) {
break;
}
if (!Irp->AssociatedIrp.SystemBuffer) {
//
// Store the flags so that an asynchronous operation can
// determine what type of buffering was used in the method.
// KsDispatchSpecificMethod uses this to determine where
// the Method parameter is.
//
// The Allocator callback may also use this to determine
// flags to set.
//
KSMETHOD_TYPE_IRP_STORAGE(Irp) = MethodItem->Flags;
try {
ULONG AllocateLength;
if (Irp->RequestorMode != KernelMode) {
//
// A KSMETHOD_TYPE_NONE is not probed.
//
if (MethodItem->Flags & KSMETHOD_TYPE_WRITE) {
//
// This covers KSMETHOD_TYPE_MODIFY since there is no
// such thing as write-only memory.
//
ProbeForWrite(Irp->UserBuffer, OutputBufferLength, sizeof(BYTE));
} else if (MethodItem->Flags & KSMETHOD_TYPE_READ) {
ProbeForRead(Irp->UserBuffer, OutputBufferLength, sizeof(BYTE));
}
}
AllocateLength = ((MethodItem->Flags & KSMETHOD_TYPE_SOURCE) ? 0 : AlignedBufferLength) + InputBufferLength;
//
// Allocate space for one or both parameters, and set
// the cleanup flags so that normal Irp completion will
// take care of the buffer.
//
if (Allocator) {
//
// The allocator callback places the buffer into SystemBuffer.
// The flags must be updated by the allocation function if
// they apply.
//
Status = Allocator(Irp, AllocateLength, (BOOLEAN)(OutputBufferLength && (MethodItem->Flags & KSMETHOD_TYPE_WRITE) && !(MethodItem->Flags & KSMETHOD_TYPE_SOURCE)));
if (!NT_SUCCESS(Status)) {
return Status;
}
} else {
//
// No allocator was specified, so just use pool memory.
//
Irp->AssociatedIrp.SystemBuffer = ExAllocatePoolWithQuotaTag(NonPagedPool, AllocateLength, 'ppSK');
Irp->Flags |= (IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER);
}
if (InputBufferLength > sizeof(*Method)) {
//
// Copy the Method parameter.
//
RtlCopyMemory(
(PUCHAR)Irp->AssociatedIrp.SystemBuffer + AllocateLength - InputBufferLength,
IrpStack->Parameters.DeviceIoControl.Type3InputBuffer,
InputBufferLength);
}
//
// Overwrite with captured data.
//
*(PKSMETHOD)((PUCHAR)Irp->AssociatedIrp.SystemBuffer + AllocateLength - InputBufferLength) = *Method;
Method = (PKSMETHOD)((PUCHAR)Irp->AssociatedIrp.SystemBuffer + AllocateLength - InputBufferLength);
//
// Prepare the parameter buffer.
//
if (MethodItem->Flags & KSMETHOD_TYPE_SOURCE) {
//
// If something other than None was selected, allocate
// an MDL for the data parameter, and probe it for the
// type specified. The Modify flag covers both Read and
// Write.
//
if (MethodItem->Flags & KSMETHOD_TYPE_MODIFY) {
if (OutputBufferLength) {
if (!(Irp->MdlAddress = IoAllocateMdl(Irp->UserBuffer, OutputBufferLength, FALSE, TRUE, Irp))) {
ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
}
//
// Actually probe the data, assuming that the flags
// which are used in the method type are consistant
// with the probe flags.
//
// Probing and locking is the last thing which is
// done, because if it fails the failure path below
// assumes that the Mdl is not locked.
//
#if KSMETHOD_TYPE_READ - 1 != IoReadAccess
#error KSMETHOD_TYPE_READ - 1 != IoReadAccess
#endif // KSMETHOD_TYPE_READ - 1 != IoReadAccess
MmProbeAndLockPages(Irp->MdlAddress, Irp->RequestorMode, (LOCK_OPERATION)((MethodItem->Flags & KSMETHOD_TYPE_MODIFY) - 1));
}
//
// The system address is passed to the handler.
//
UserBuffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
} else {
//
// Else the actual source address is passed, which has
// not been verified in any manner.
//
UserBuffer = Irp->UserBuffer;
}
} else if (OutputBufferLength) {
switch (MethodItem->Flags) {
case KSMETHOD_TYPE_READ:
case KSMETHOD_TYPE_MODIFY:
RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer,
Irp->UserBuffer,
OutputBufferLength);
if (MethodItem->Flags == KSMETHOD_TYPE_READ) {
break;
}
// no break;
case KSMETHOD_TYPE_WRITE:
if (!Allocator) {
Irp->Flags |= IRP_INPUT_OPERATION;
}
}
UserBuffer = Irp->AssociatedIrp.SystemBuffer;
} else {
UserBuffer = NULL;
}
} except (EXCEPTION_EXECUTE_HANDLER) {
//
// If an Mdl was allocated, then the only thing which could
// have failed would be probing or locking the pages. If
// this happens, the Mdl must be freed before returning,
// since Irp completion assumes any Mdl associated with
// an Irp is locked. Note that it is assumed that the
// pages are not locked.
//
if (Irp->MdlAddress) {
IoFreeMdl(Irp->MdlAddress);
Irp->MdlAddress = NULL;
}
return GetExceptionCode();
}
} else if (KSMETHOD_TYPE_IRP_STORAGE(Irp) & KSMETHOD_TYPE_SOURCE) {
if (OutputBufferLength) {
if (KSMETHOD_TYPE_IRP_STORAGE(Irp) & ~(KSMETHOD_TYPE_SOURCE | KSMETHOD_TYPE_TOPOLOGY)) {
//
// The Read, Write, or Modify flag has been set, indicating that
// a system address probed as such should be used.
//
UserBuffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
} else {
//
// Else the actual source address is passed, which has not been
// verified in any manner.
//
UserBuffer = Irp->UserBuffer;
}
} else {
UserBuffer = NULL;
}
} else if (OutputBufferLength) {
UserBuffer = Irp->AssociatedIrp.SystemBuffer;
} else {
UserBuffer = NULL;
}
//
// Some filters want to do their own processing, so a pointer to
// the set is placed in any IRP forwarded.
//
KSMETHOD_SET_IRP_STORAGE(Irp) = MethodSet;
//
// Optionally provide method item context.
//
if (MethodItemSize) {
KSMETHOD_ITEM_IRP_STORAGE(Irp) = MethodItem;
}
if ((IrpStack->Parameters.DeviceIoControl.InputBufferLength <
MethodItem->MinMethod) ||
(OutputBufferLength < MethodItem->MinData)) {
return STATUS_BUFFER_TOO_SMALL;
}
return MethodItem->MethodHandler(Irp, Method, UserBuffer);
}
}
//
// The outer loop looking for method sets fell through with no match. This may
// indicate that this is a support query for the list of all method sets
// supported.
//
if (!RemainingSetsCount) {
//
// Specifying a GUID_NULL as the set means that this is a support query
// for all sets.
//
if (!IsEqualGUIDAligned(&Method->Set, &GUID_NULL)) {
return STATUS_PROPSET_NOT_FOUND;
}
//
// The support flag must have been used so that the IRP_INPUT_OPERATION
// is set. For future expansion, the identifier within the set is forced
// to be zero.
//
// WRM: Changed below from !Method->Id to Method->Id. Otherwise,
// we end up returning invalid parameter for valid set support
// queries.
//
if (Method->Id || (Flags != KSMETHOD_TYPE_SETSUPPORT)) {
return STATUS_INVALID_PARAMETER;
}
//
// The query can request the length of the needed buffer, or can
// specify a buffer which is at least long enough to contain the
// complete list of GUID's.
//
if (!OutputBufferLength) {
//
// Return the size of the buffer needed for all the GUID's.
//
Irp->IoStatus.Information = MethodSetsCount * sizeof(GUID);
return STATUS_BUFFER_OVERFLOW;
#ifdef SIZE_COMPATIBILITY
} else if (OutputBufferLength == sizeof(OutputBufferLength)) {
*(PULONG)Irp->AssociatedIrp.SystemBuffer = MethodSetsCount * sizeof(GUID);
Irp->IoStatus.Information = sizeof(OutputBufferLength);
return STATUS_SUCCESS;
#endif // SIZE_COMPATIBILITY
} else if (OutputBufferLength < MethodSetsCount * sizeof(GUID)) {
//
// The buffer was too short for all the GUID's.
//
return STATUS_BUFFER_TOO_SMALL;
} else {
GUID* Guid;
Irp->IoStatus.Information = MethodSetsCount * sizeof(*Guid);
MethodSet -= MethodSetsCount;
for (Guid = (GUID*)Irp->AssociatedIrp.SystemBuffer;
MethodSetsCount;
Guid++, MethodSet++, MethodSetsCount--) {
*Guid = *MethodSet->Set;
}
}
return STATUS_SUCCESS;
}
return STATUS_NOT_FOUND;
}
const KSFASTMETHOD_ITEM*
FASTCALL
FindFastMethodItem(
IN const KSMETHOD_SET* MethodSet,
IN ULONG MethodId
)
/*++
Routine Description:
Given an method set structure locates the specified fast method item.
Arguments:
MethodSet -
Points to the method set to search.
MethodId -
Contains the fast method identifier to look for.
Return Value:
Returns a pointer to the fast method identifier structure, or NULL if it
could not be found.
--*/
{
const KSFASTMETHOD_ITEM* FastMethodItem;
ULONG MethodsCount;
FastMethodItem = MethodSet->FastIoTable;
for (MethodsCount = MethodSet->FastIoCount;
MethodsCount; MethodsCount--,
FastMethodItem++) {
if (MethodId == FastMethodItem->MethodId) {
return FastMethodItem;
}
}
return NULL;
}
KSDDKAPI
BOOLEAN
NTAPI
KsFastMethodHandler(
IN PFILE_OBJECT FileObject,
IN PKSMETHOD Method,
IN ULONG MethodLength,
IN OUT PVOID Data,
IN ULONG DataLength,
OUT PIO_STATUS_BLOCK IoStatus,
IN ULONG MethodSetsCount,
IN const KSMETHOD_SET* MethodSet
)
/*++
Routine Description:
Handles methods requested through the fast I/O interface. Does not deal
with method information support, just the methods themselves. In the
former case, the function returns FALSE, which allows the caller to
generate an IRP to deal with the request. The function also does not deal
with extended method items. This function may only be called at
PASSIVE_LEVEL.
Arguments:
FileObject -
The file object on which the request is being made.
Method -
The method to query set. Must be LONG aligned.
MethodLength -
The length of the Method parameter.
Data -
The associated buffer for the query set, in which the data is
returned or placed.
DataLength -
The length of the Data parameter.
IoStatus -
Return status.
MethodSetsCount -
Indicates the number of method set structures being passed.
MethodSet -
Contains the pointer to the list of method set information.
Return Value:
Returns TRUE if the request was handled, else FALSE if an IRP must be
generated. Sets the Information and Status in IoStatus.
--*/
{
KPROCESSOR_MODE ProcessorMode;
KSMETHOD LocalMethod;
ULONG RemainingSetsCount;
PAGED_CODE();
//
// Initially just check for the minimal method parameter length. The
// actual minimal length will be validated when the method item is found.
//
if (MethodLength < sizeof(LocalMethod)) {
return FALSE;
}
ProcessorMode = ExGetPreviousMode();
//
// Validate the method if the client is not trusted, then capture it.
//
if (ProcessorMode != KernelMode) {
try {
ProbeForRead(Method, MethodLength, sizeof(ULONG));
LocalMethod = *Method;
} except (EXCEPTION_EXECUTE_HANDLER) {
return FALSE;
}
} else {
LocalMethod = *Method;
}
//
// Must use the normal method handler for support queries.
//
if (LocalMethod.Flags & KSIDENTIFIER_SUPPORTMASK) {
return FALSE;
}
for (RemainingSetsCount = MethodSetsCount; RemainingSetsCount; MethodSet++, RemainingSetsCount--) {
if (IsEqualGUIDAligned(&LocalMethod.Set, MethodSet->Set)) {
const KSFASTMETHOD_ITEM* FastMethodItem;
const KSMETHOD_ITEM* MethodItem;
//
// Once the method set is found, determine if there is fast
// I/O support for that method item.
//
if (!(FastMethodItem = FindFastMethodItem(MethodSet, LocalMethod.Id))) {
return FALSE;
}
//
// If there is fast I/O support, then the real method item needs to
// be located in order to validate the parameter sizes.
//
if (!(MethodItem = FindMethodItem(MethodSet, sizeof(*MethodItem), LocalMethod.Id))) {
return FALSE;
}
//
// Validate the data if the client is not trusted.
//
if (ProcessorMode != KernelMode) {
try {
if (MethodItem->Flags & KSMETHOD_TYPE_READ) {
//
// This covers KSMETHOD_TYPE_MODIFY since there is no
// such thing as write-only memory.
//
ProbeForRead(Data, DataLength, sizeof(BYTE));
} else if (MethodItem->Flags & KSMETHOD_TYPE_WRITE) {
ProbeForWrite(Data, DataLength, sizeof(BYTE));
}
} except (EXCEPTION_EXECUTE_HANDLER) {
return FALSE;
}
}
//
// The bytes returned is always assumed to be initialized by the handler.
//
IoStatus->Information = 0;
if (!FastMethodItem->MethodHandler) {
return FALSE;
}
if ((MethodLength < MethodItem->MinMethod) ||
(DataLength < MethodItem->MinData)) {
return FALSE;
}
return FastMethodItem->MethodHandler(
FileObject,
Method,
MethodLength,
Data,
DataLength,
IoStatus);
}
}
return FALSE;
}