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.
 
 
 
 
 
 

4921 lines
167 KiB

/*++
Copyright (c) 1997-1999 Microsoft Corporation
Module Name:
consumer.c
Abstract:
Data Consumer apis
Author:
AlanWar
Environment:
Kernel mode
Revision History:
--*/
#include "wmikmp.h"
#include <evntrace.h>
#include <ntcsrmsg.h>
#define NTOSKRNL_WMI
#include <basemsg.h>
#define STRSAFE_NO_DEPRECATE
#include <strsafe.h>
void WmipCompleteGuidIrpWithError(
PWMIGUIDOBJECT GuidObject
);
NTSTATUS WmipCreatePumpThread(
PWMIGUIDOBJECT Object
);
void WmipClearThreadObjectList(
PWMIGUIDOBJECT MainObject
);
void
WmipGetGuidPropertiesFromGuidEntry(
PWMIGUIDPROPERTIES GuidInfo,
PGUIDENTRY GuidEntry);
BOOLEAN WmipIsQuerySetGuid(
PBGUIDENTRY GuidEntry
);
NTSTATUS WmipAddProviderIdToPIList(
PBINSTANCESET **PIPtrPtr,
PULONG PICountPtr,
PULONG PIMaxPtr,
PBINSTANCESET *StaticPIPtr,
PBINSTANCESET InstanceSet
);
NTSTATUS WmipPrepareForWnodeAD(
IN PWMIGUIDOBJECT GuidObject,
OUT LPGUID Guid,
IN OUT ULONG *ProviderIdCount,
OUT PBINSTANCESET **ProviderIdList,
OUT BOOLEAN *InternalGuid
);
ULONG WmipStaticInstanceNameSize(
PBINSTANCESET InstanceSet
);
void WmipInsertStaticNames(
PWNODE_ALL_DATA Wnode,
ULONG MaxWnodeSize,
PBINSTANCESET InstanceSet
);
NTSTATUS WmipQueryGuidInfo(
IN OUT PWMIQUERYGUIDINFO QueryGuidInfo
);
void WmipCopyFromEventQueues(
IN POBJECT_EVENT_INFO ObjectArray,
IN ULONG HandleCount,
OUT PUCHAR OutBuffer,
OUT ULONG *OutBufferSizeUsed,
OUT PWNODE_HEADER *LastWnode,
IN BOOLEAN IsHiPriority
);
void WmipClearIrpObjectList(
PIRP Irp
);
NTSTATUS WmipReceiveNotifications(
PWMIRECEIVENOTIFICATION ReceiveNotification,
PULONG OutBufferSize,
PIRP Irp
);
NTSTATUS WmipQueueNotification(
PWMIGUIDOBJECT Object,
PWMIEVENTQUEUE EventQueue,
PWNODE_HEADER Wnode
);
PWNODE_HEADER WmipDereferenceEvent(
PWNODE_HEADER Wnode
);
PWNODE_HEADER WmipIncludeStaticNames(
PWNODE_HEADER Wnode
);
NTSTATUS WmipWriteWnodeToObject(
PWMIGUIDOBJECT Object,
PWNODE_HEADER Wnode,
BOOLEAN IsHighPriority
);
NTSTATUS WmipProcessEvent(
PWNODE_HEADER InWnode,
BOOLEAN IsHighPriority,
BOOLEAN FreeBuffer
);
NTSTATUS WmipRegisterUMGuids(
IN POBJECT_ATTRIBUTES ObjectAttributes,
IN ULONG Cookie,
IN PWMIREGINFO RegInfo,
IN ULONG RegInfoSize,
OUT HANDLE *RequestHandle,
OUT ULONG64 *LoggerContext
);
NTSTATUS WmipUnregisterGuids(
PWMIUNREGGUIDS UnregGuids
);
NTSTATUS WmipWriteMBToObject(
IN PWMIGUIDOBJECT RequestObject,
IN PWMIGUIDOBJECT ReplyObject,
IN PUCHAR Message,
IN ULONG MessageSize
);
NTSTATUS WmipWriteMessageToGuid(
IN PBGUIDENTRY GuidEntry,
IN PWMIGUIDOBJECT ReplyObject,
IN PUCHAR Message,
IN ULONG MessageSize,
OUT PULONG WrittenCount
);
NTSTATUS WmipCreateUMLogger(
IN OUT PWMICREATEUMLOGGER CreateInfo
);
NTSTATUS WmipMBReply(
IN HANDLE RequestHandle,
IN ULONG ReplyIndex,
IN PUCHAR Message,
IN ULONG MessageSize
);
NTSTATUS WmipPrepareWnodeSI(
IN PWMIGUIDOBJECT GuidObject,
IN OUT PWNODE_SINGLE_INSTANCE WnodeSI,
IN OUT ULONG *ProviderIdCount,
OUT PBINSTANCESET **ProviderIdList,
OUT BOOLEAN *IsDynamic,
OUT BOOLEAN *InternalGuid
);
void WmipCreatePumpThreadRoutine(
PVOID Context
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE,WmipIsQuerySetGuid)
#pragma alloc_text(PAGE,WmipOpenBlock)
#pragma alloc_text(PAGE,WmipAddProviderIdToPIList)
#pragma alloc_text(PAGE,WmipPrepareForWnodeAD)
#pragma alloc_text(PAGE,WmipStaticInstanceNameSize)
#pragma alloc_text(PAGE,WmipInsertStaticNames)
#pragma alloc_text(PAGE,WmipQueryAllData)
#pragma alloc_text(PAGE,WmipQueryAllDataMultiple)
#pragma alloc_text(PAGE,WmipPrepareWnodeSI)
#pragma alloc_text(PAGE,WmipQuerySetExecuteSI)
#pragma alloc_text(PAGE,WmipQuerySingleMultiple)
#pragma alloc_text(PAGE,WmipEnumerateGuids)
#pragma alloc_text(PAGE,WmipQueryGuidInfo)
#pragma alloc_text(PAGE,WmipClearIrpObjectList)
#pragma alloc_text(PAGE,WmipReceiveNotifications)
#pragma alloc_text(PAGE,WmipQueueNotification)
#pragma alloc_text(PAGE,WmipDereferenceEvent)
#pragma alloc_text(PAGE,WmipIncludeStaticNames)
#pragma alloc_text(PAGE,WmipWriteWnodeToObject)
#pragma alloc_text(PAGE,WmipProcessEvent)
#pragma alloc_text(PAGE,WmipUMProviderCallback)
#pragma alloc_text(PAGE,WmipRegisterUMGuids)
#pragma alloc_text(PAGE,WmipUnregisterGuids)
#pragma alloc_text(PAGE,WmipWriteMBToObject)
#pragma alloc_text(PAGE,WmipWriteMessageToGuid)
#pragma alloc_text(PAGE,WmipCreateUMLogger)
#pragma alloc_text(PAGE,WmipMBReply)
#pragma alloc_text(PAGE,WmipGetGuidPropertiesFromGuidEntry)
#pragma alloc_text(PAGE,WmipClearThreadObjectList)
#pragma alloc_text(PAGE,WmipClearObjectFromThreadList)
#pragma alloc_text(PAGE,WmipCreatePumpThread)
#pragma alloc_text(PAGE,WmipCopyFromEventQueues)
#pragma alloc_text(PAGE,WmipCreatePumpThreadRoutine)
#pragma alloc_text(PAGE,WmipMarkHandleAsClosed)
#pragma alloc_text(PAGE,WmipCompleteGuidIrpWithError)
#endif
BOOLEAN WmipIsQuerySetGuid(
PBGUIDENTRY GuidEntry
)
{
PLIST_ENTRY InstanceSetList;
PBINSTANCESET InstanceSet;
PAGED_CODE();
WmipAssert(GuidEntry != NULL);
WmipEnterSMCritSection();
InstanceSetList = GuidEntry->ISHead.Flink;
while (InstanceSetList != &GuidEntry->ISHead)
{
InstanceSet = CONTAINING_RECORD(InstanceSetList,
INSTANCESET,
GuidISList);
if ( (InstanceSet->Flags &
(IS_TRACED | IS_CONTROL_GUID | IS_EVENT_ONLY)) == 0 )
{
//
// If there is at least one IS that isn't traced and isn't
// an event only then it is a queryset guid
//
WmipLeaveSMCritSection();
return (TRUE);
}
InstanceSetList = InstanceSetList->Flink;
}
WmipLeaveSMCritSection();
return (FALSE);
}
NTSTATUS WmipOpenBlock(
IN ULONG Ioctl,
IN KPROCESSOR_MODE AccessMode,
IN POBJECT_ATTRIBUTES CapturedObjectAttributes,
IN ULONG DesiredAccess,
OUT PHANDLE Handle
)
{
PBGUIDENTRY GuidEntry;
PWMIGUIDOBJECT Object;
NTSTATUS Status;
PAGED_CODE();
//
// Creates a guid handle with the desired access
//
Status = WmipOpenGuidObject(CapturedObjectAttributes,
DesiredAccess,
AccessMode,
Handle,
&Object);
if (NT_SUCCESS(Status))
{
Object->Type = Ioctl;
if (Ioctl != IOCTL_WMI_OPEN_GUID)
{
GuidEntry = WmipFindGEByGuid(&Object->Guid, FALSE);
//
// Establish our object on the guidentry list
//
WmipEnterSMCritSection();
if (GuidEntry != NULL)
{
InsertTailList(&GuidEntry->ObjectHead,
&Object->GEObjectList);
}
Object->GuidEntry = GuidEntry;
WmipLeaveSMCritSection();
switch (Ioctl)
{
case IOCTL_WMI_OPEN_GUID_FOR_QUERYSET:
{
//
// Guid is being opened for query/set/method operations so
// we need to insure that there is a guid entry and that
// the guid entry has InstanceSets attached and it is
// has at least one instance set that is not a traced
// guid and is not an event only guid
//
if ((GuidEntry == NULL) ||
(GuidEntry->ISCount == 0) ||
(! WmipIsQuerySetGuid(GuidEntry)))
{
//
// Either we could not find a guidentry or there
// is no instance sets attached. We close the
// original handle and fail the IOCTL
//
ZwClose(*Handle);
Status = STATUS_WMI_GUID_NOT_FOUND;
break;
}
//
// Fall through
//
}
case IOCTL_WMI_OPEN_GUID_FOR_EVENTS:
{
//
// Since we can register to receive events before
// the event provider has been registered we'll need
// to create the guid entry if one does not exist
//
if (AccessMode == KernelMode)
{
Object->Flags |= WMIGUID_FLAG_KERNEL_NOTIFICATION;
}
if (GuidEntry == NULL)
{
WmipAssert(Ioctl == IOCTL_WMI_OPEN_GUID_FOR_EVENTS);
WmipEnterSMCritSection();
GuidEntry = WmipAllocGuidEntry();
if (GuidEntry != NULL)
{
//
// Initialize the new GuidEntry and place it
// on the master GuidEntry list.
//
memcpy(&GuidEntry->Guid,
&Object->Guid,
sizeof(GUID));
InsertHeadList(WmipGEHeadPtr, &GuidEntry->MainGEList);
InsertTailList(&GuidEntry->ObjectHead,
&Object->GEObjectList);
Object->GuidEntry = GuidEntry;
WmipLeaveSMCritSection();
} else {
WmipLeaveSMCritSection();
ZwClose(*Handle);
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
}
//
// Now we need to see if we have to enable collection
// or events
//
Status = WmipEnableCollectOrEvent(GuidEntry,
Ioctl,
&Object->EnableRequestSent,
0);
if (! NT_SUCCESS(Status))
{
//
// For some reason enabling failed so just return
// the error
//
ZwClose(*Handle);
}
//
// Don't unref the guid entry as that ref count is
// taken by the object just placed on the list
//
break;
}
default:
{
//
// We should never get here.....
//
WmipAssert(FALSE);
ZwClose(*Handle);
Status = STATUS_ILLEGAL_FUNCTION;
break;
}
}
} else {
//
// Mark this as a security object
//
Object->Flags |= WMIGUID_FLAG_SECURITY_OBJECT;
}
//
// remove the ref taken when the object was created
//
ObDereferenceObject(Object);
}
return(Status);
}
NTSTATUS WmipAddProviderIdToPIList(
PBINSTANCESET **PIPtrPtr,
PULONG PICountPtr,
PULONG PIMaxPtr,
PBINSTANCESET *StaticPIPtr,
PBINSTANCESET InstanceSet
)
{
ULONG PICount;
ULONG PIMax, NewPIMax;
PBINSTANCESET *PIPtr, *OldPIPtr, *NewPIPtr;
NTSTATUS Status;
ULONG i;
PAGED_CODE();
Status = STATUS_SUCCESS;
PICount = *PICountPtr;
PIMax = *PIMaxPtr;
PIPtr = *PIPtrPtr;
//
// Remember dynamic providerid
//
if (PICount == PIMax)
{
//
// We have overflowed the PI List so we need to
// reallocate a bigger buffer
//
NewPIMax = PIMax * 2;
NewPIPtr = (PBINSTANCESET *)WmipAlloc(NewPIMax *
sizeof(PBINSTANCESET));
OldPIPtr = PIPtr;
if (NewPIPtr != NULL)
{
//
// Copy provider ids from old to new buffer
//
memcpy(NewPIPtr, OldPIPtr, PIMax*sizeof(PBINSTANCESET));
PIPtr = NewPIPtr;
*PIPtrPtr = NewPIPtr;
PIMax = NewPIMax;
*PIMaxPtr = PIMax;
} else {
//
// Bad break, we could not allocate more space
// unref any instance sets and return an error
//
for (i = 0; i < PIMax; i++)
{
WmipUnreferenceIS(PIPtr[i]);
}
WmipUnreferenceIS(InstanceSet);
*PIPtrPtr = NULL;
Status = STATUS_INSUFFICIENT_RESOURCES;
}
//
// if previous buffer was not static then free it
//
if (OldPIPtr != StaticPIPtr)
{
WmipFree(OldPIPtr);
}
}
if (NT_SUCCESS(Status))
{
//
// Remember instance set
//
PIPtr[PICount++] = InstanceSet;
*PICountPtr = PICount;
}
return(Status);
}
NTSTATUS WmipPrepareForWnodeAD(
IN PWMIGUIDOBJECT GuidObject,
OUT LPGUID Guid,
IN OUT ULONG *ProviderIdCount,
OUT PBINSTANCESET **ProviderIdList,
OUT BOOLEAN *InternalGuid
)
{
PBINSTANCESET *PIPtr, *StaticPIPtr;
ULONG PICount, PIMax;
NTSTATUS Status;
PBGUIDENTRY GuidEntry;
PBINSTANCESET InstanceSet;
PLIST_ENTRY InstanceSetList;
PAGED_CODE();
GuidEntry = GuidObject->GuidEntry;
if ((GuidEntry != NULL) && (GuidEntry->ISCount > 0))
{
//
// We were passed a valid guid handle, get out the guid
//
*Guid = GuidEntry->Guid;
Status = STATUS_SUCCESS;
if (GuidEntry->Flags & GE_FLAG_INTERNAL)
{
*InternalGuid = TRUE;
} else {
//
// Build list of provider ids to whom the QAD will be targetted
//
*InternalGuid = FALSE;
StaticPIPtr = *ProviderIdList;
PIPtr = StaticPIPtr;
PIMax = *ProviderIdCount;
PICount = 0;
WmipEnterSMCritSection();
InstanceSetList = GuidEntry->ISHead.Flink;
while ((InstanceSetList != &GuidEntry->ISHead) &&
NT_SUCCESS(Status))
{
InstanceSet = CONTAINING_RECORD(InstanceSetList,
INSTANCESET,
GuidISList);
//
// Take a refcount on the instance set so that it won't
// go away until after we are done with our query
// The refcount gets removed by the caller when it is
// done with the list or in WmipAddProviderIdTOLlist if it
// returns an error
//
if ((InstanceSet->Flags & (IS_TRACED | IS_CONTROL_GUID | IS_EVENT_ONLY)) == 0)
{
//
// Only take those IS that are not traced or control
// guids and are not event only guids
//
WmipReferenceIS(InstanceSet);
Status = WmipAddProviderIdToPIList(&PIPtr,
&PICount,
&PIMax,
StaticPIPtr,
InstanceSet);
}
InstanceSetList = InstanceSetList->Flink;
}
WmipLeaveSMCritSection();
if (PICount == 0)
{
Status = STATUS_WMI_GUID_DISCONNECTED;
} else {
*ProviderIdCount = PICount;
*ProviderIdList = PIPtr;
}
}
} else {
Status = STATUS_WMI_GUID_DISCONNECTED;
}
return(Status);
}
ULONG WmipStaticInstanceNameSize(
PBINSTANCESET InstanceSet
)
/*+++
Routine Description:
This routine will calculate the size needed to place instance names in
a WNODE_ALL_DATA
Arguments:
WmiInstanceInfo describes to instance set whose instance name size
is to be calculated
Return Value:
Size needed to place instance names in a WNODE_ALL_DATA plus 3. The
extra 3 bytes are added in case the OffsetInstanceNameOffsets need to be
padded since they must be on a 4 byte boundary.
---*/
{
SIZE_T NameSize;
ULONG i;
PAGED_CODE();
//
// If we already computed this then just return the results
if (InstanceSet->WADInstanceNameSize != 0)
{
return(InstanceSet->WADInstanceNameSize);
}
//
// Start with a name size of 3 in case the OffsetInstanceNameOffset will
// need to be padded so that it starts on a 4 byte boundary.
NameSize = 3;
if (InstanceSet->Flags & IS_INSTANCE_BASENAME)
{
//
// For static base names we assume that there will never be more than
// MAXBASENAMESUFFIXVALUE instances of a guid. So the size of each instance name
// would be the size of the base name plus the size of the suffix
// plus a USHORT for the count (for counted string) plus a ULONG
// to hold the offset to the instance name
//
WmipAssert((InstanceSet->IsBaseName->BaseIndex + InstanceSet->Count) < MAXBASENAMESUFFIXVALUE);
NameSize += ((wcslen(InstanceSet->IsBaseName->BaseName) * sizeof(WCHAR)) +
MAXBASENAMESUFFIXSIZE * sizeof(WCHAR) +
sizeof(USHORT) +
sizeof(ULONG)) * InstanceSet->Count;
} else if (InstanceSet->Flags & IS_INSTANCE_STATICNAMES)
{
//
// For a static name list we count up each size of
// the static instance names in the list and add a ULONG and a USHORT
// for the offset and count (for counted string)
for (i = 0; i < InstanceSet->Count; i++)
{
NameSize += (wcslen(InstanceSet->IsStaticNames->StaticNamePtr[i]) + 2) * sizeof(WCHAR) + sizeof(ULONG);
}
}
InstanceSet->WADInstanceNameSize = (ULONG)NameSize;
return(ULONG)(NameSize);
}
void WmipInsertStaticNames(
PWNODE_ALL_DATA Wnode,
ULONG MaxWnodeSize,
PBINSTANCESET InstanceSet
)
/*+++
Routine Description:
This routine will copy into the WNODE_ALL_DATA instance names for a
static instance name set. If the Wnode_All_data is too small then it
is converted to a WNODE_TOO_SMALL
Arguments:
Wnode points at the WNODE_ALL_DATA
MaxWnodeSize is the maximum size of the Wnode
WmiInstanceInfo is the Instance Info
Return Value:
---*/
{
PWCHAR NamePtr;
PULONG NameOffsetPtr;
ULONG InstanceCount;
ULONG i;
WCHAR Index[MAXBASENAMESUFFIXSIZE+1];
PWCHAR StaticName;
ULONG SizeNeeded;
SIZE_T NameLen;
USHORT Len;
ULONG PaddedBufferSize;
size_t Size;
HRESULT hr;
PAGED_CODE();
if ((InstanceSet->Flags &
(IS_INSTANCE_BASENAME | IS_INSTANCE_STATICNAMES)) == 0)
{
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_ERROR_LEVEL,"WMI: Try to setup static names for dynamic guid\n"));
return;
}
InstanceCount = InstanceSet->Count;
//
// Pad out the size of the incoming wnode to a 4 byte boundary since the
// OffsetInstanceNameOffsets is being appended to the end of the
// wnode and it must be on a 4 byte boundary
//
PaddedBufferSize = (Wnode->WnodeHeader.BufferSize + 3) & ~3;
//
// Compute the complete size needed to rewrite the WNODE to include
// the instance names.
//
// Include the size that is needed to fill out
Size = WmipStaticInstanceNameSize(InstanceSet);
// Include the space needed for the array of offsets to the
// instance names plus the size of the names plus the padded
// size of the wnode
SizeNeeded = (InstanceCount * sizeof(ULONG)) +
(ULONG)Size +
PaddedBufferSize;
if (SizeNeeded > MaxWnodeSize)
{
//
// If not enough space left in the buffer passed then build a
// WNODE_TOO_SMALL as the result to indicate how much buffer
// space is needed.
//
Wnode->WnodeHeader.BufferSize = sizeof(WNODE_TOO_SMALL);
Wnode->WnodeHeader.Flags = WNODE_FLAG_TOO_SMALL;
((PWNODE_TOO_SMALL)Wnode)->SizeNeeded = SizeNeeded;
return;
}
//
// Allocate space for the array of offsets to instance names
//
NameOffsetPtr = (PULONG)((PUCHAR)Wnode + PaddedBufferSize);
Wnode->OffsetInstanceNameOffsets = (ULONG)((PUCHAR)NameOffsetPtr - (PUCHAR)Wnode);
//
// Point at the beginning of the area to write the instance names
//
NamePtr = (PWCHAR)(NameOffsetPtr + InstanceCount);
if (InstanceSet->Flags & IS_INSTANCE_BASENAME)
{
//
// The instance name is based upon a basename with a trailing
// index number to provide uniqueness
//
if (InstanceSet->Flags & IS_PDO_INSTANCENAME)
{
Wnode->WnodeHeader.Flags |= WNODE_FLAG_PDO_INSTANCE_NAMES;
}
for (i = 0; i < InstanceCount; i++)
{
//
// Account for space used by length of string that follows
//
Size -= sizeof(USHORT);
*NameOffsetPtr++ = (ULONG)((PUCHAR)NamePtr - (PUCHAR)Wnode);
//
// Copy over basename while accounting length used by it
//
hr = StringCbCopy(NamePtr+1,
Size,
InstanceSet->IsBaseName->BaseName);
WmipAssert(hr == S_OK);
//
// Format unique index number
//
hr = StringCbPrintf(Index,
sizeof(Index),
BASENAMEFORMATSTRING,
InstanceSet->IsBaseName->BaseIndex+i);
WmipAssert(hr == S_OK);
//
// Append unique index number to instance name
//
hr = StringCbCat(NamePtr+1,
Size,
Index);
WmipAssert(hr == S_OK);
NameLen = wcslen(NamePtr+1) + 1;
*NamePtr = (USHORT)NameLen * sizeof(WCHAR);
NamePtr += NameLen + 1;
Size -= NameLen * sizeof(WCHAR);
}
} else if (InstanceSet->Flags & IS_INSTANCE_STATICNAMES) {
//
// Instance names are from a list of static names
//
for (i = 0; i < InstanceCount; i++)
{
*NameOffsetPtr++ = (ULONG)((PUCHAR)NamePtr - (PUCHAR)Wnode);
StaticName = InstanceSet->IsStaticNames->StaticNamePtr[i];
Len = (USHORT)((wcslen(StaticName)+1) * sizeof(WCHAR));
*NamePtr++ = Len;
//
// Account for space used by length of string that follows
//
Size -= sizeof(USHORT);
//
// Copy over and account for static name
//
hr = StringCbCopyEx(NamePtr,
Size,
StaticName,
NULL,
&Size,
0);
WmipAssert(hr == S_OK);
NamePtr += Len / sizeof(WCHAR);
}
}
Wnode->WnodeHeader.BufferSize = SizeNeeded;
}
//
// This defines how many provider ids will fit within the static block. If
// we need more than this, then we'll have to allocate memory for it
//
#if DBG
#define MANYPROVIDERIDS 1
#else
#define MANYPROVIDERIDS 16
#endif
NTSTATUS WmipQueryAllData(
IN PWMIGUIDOBJECT GuidObject,
IN PIRP Irp,
IN KPROCESSOR_MODE AccessMode,
IN PWNODE_ALL_DATA Wnode,
IN ULONG OutBufferLen,
OUT PULONG RetSize
)
{
NTSTATUS Status;
PBINSTANCESET StaticPIList[MANYPROVIDERIDS];
PBINSTANCESET *PIList;
PBINSTANCESET InstanceSet;
WNODE_ALL_DATA WnodeAllData;
BOOLEAN IsBufferTooSmall;
PWNODE_HEADER WnodeHeader;
LOGICAL UsesStaticNames;
PWNODE_TOO_SMALL WnodeTooSmall = (PWNODE_TOO_SMALL)&WnodeAllData;
PWNODE_ALL_DATA WnodeAD;
ULONG BufferLeft;
ULONG SizeNeeded;
ULONG PICount;
ULONG WnodeFlags, WnodeSize;
PWNODE_HEADER WnodeLast;
ULONG Linkage;
ULONG i;
GUID Guid;
PUCHAR Buffer;
ULONG BufferUsed;
HANDLE KernelHandle;
BOOLEAN InternalGuid;
IO_STATUS_BLOCK Iosb;
PAGED_CODE();
//
// Check Security
//
if (GuidObject != NULL)
{
Status = ObReferenceObjectByPointer(GuidObject,
WMIGUID_QUERY,
WmipGuidObjectType,
AccessMode);
} else {
KernelHandle = Wnode->WnodeHeader.KernelHandle;
Status = ObReferenceObjectByHandle(KernelHandle,
WMIGUID_QUERY,
WmipGuidObjectType,
AccessMode,
&GuidObject,
NULL);
}
if (NT_SUCCESS(Status))
{
//
// Get the provider id list for the guid
//
PIList = StaticPIList;
PICount = MANYPROVIDERIDS;
Status = WmipPrepareForWnodeAD(GuidObject,
&Guid,
&PICount,
&PIList,
&InternalGuid);
if (NT_SUCCESS(Status))
{
if (InternalGuid)
{
//
// This is an internal guid so we fill out the WNODE_ALL_DATA
// and mark it to be completed by the user mode code
//
Wnode->WnodeHeader.Guid = Guid;
Wnode->WnodeHeader.Flags |= WNODE_FLAG_INTERNAL;
Wnode->WnodeHeader.Linkage = 0;
*RetSize = sizeof(WNODE_HEADER);
Status = STATUS_SUCCESS;
} else {
//
// Get all of the information from the WNODE_HEADER so we can
// rebuild it
//
WnodeFlags = Wnode->WnodeHeader.Flags;
WnodeSize = Wnode->WnodeHeader.BufferSize;
//
// Loop over all provider ids and send each a WAD query
//
Buffer = (PUCHAR)Wnode;
BufferLeft = OutBufferLen;
IsBufferTooSmall = FALSE;
SizeNeeded = 0;
WnodeLast = NULL;
for (i = 0; i < PICount; i++)
{
InstanceSet = PIList[i];
if ((IsBufferTooSmall) || (BufferLeft < sizeof(WNODE_ALL_DATA)))
{
//
// If we have already determined that the buffer is
// too small then we use the static WNODE_ALL_DATA
// just to get the size needed
//
WnodeAD = &WnodeAllData;
BufferLeft = sizeof(WNODE_ALL_DATA);
IsBufferTooSmall = TRUE;
} else {
//
// Otherwise we will append to the end of the buffer
//
WnodeAD = (PWNODE_ALL_DATA)Buffer;
}
//
// Build the WNODE and send it off to the driver
//
WnodeHeader = (PWNODE_HEADER)WnodeAD;
WnodeHeader->BufferSize = sizeof(WNODE_HEADER);
UsesStaticNames =((InstanceSet->Flags & IS_INSTANCE_BASENAME) ||
(InstanceSet->Flags & IS_INSTANCE_STATICNAMES));
WnodeHeader->Flags = WnodeFlags | (UsesStaticNames ?
WNODE_FLAG_STATIC_INSTANCE_NAMES :
0);
WnodeHeader->Guid = Guid;
WnodeHeader->ProviderId = PIList[i]->ProviderId;
WnodeHeader->Linkage = 0;
if (Irp != NULL)
{
Status = WmipForwardWmiIrp(Irp,
IRP_MN_QUERY_ALL_DATA,
WnodeHeader->ProviderId,
&WnodeHeader->Guid,
BufferLeft,
WnodeAD);
} else {
Status = WmipSendWmiIrp(
IRP_MN_QUERY_ALL_DATA,
WnodeHeader->ProviderId,
&WnodeHeader->Guid,
BufferLeft,
WnodeAD,
&Iosb);
}
if (NT_SUCCESS(Status))
{
if (WnodeHeader->Flags & WNODE_FLAG_TOO_SMALL)
{
//
// There was not enough space to write the WNODE
// so we keep track of how much was needed and then
// switch to the mode where we just query for size needed
//
WnodeTooSmall = (PWNODE_TOO_SMALL)WnodeAD;
SizeNeeded += WnodeTooSmall->SizeNeeded;
if (UsesStaticNames)
{
SizeNeeded = (SizeNeeded + 3) &~3;
SizeNeeded += WmipStaticInstanceNameSize(InstanceSet)+
(InstanceSet->Count *sizeof(ULONG));
}
SizeNeeded = (SizeNeeded +7) & ~7;
IsBufferTooSmall = TRUE;
} else if (IsBufferTooSmall) {
//
// We passed a minimum sized buffer, but it is large
// enough for the driver. Since we are just trying
// to get the size needed we get the size he needs
// and throw away his data
//
SizeNeeded += WnodeAD->WnodeHeader.BufferSize +
WmipStaticInstanceNameSize(InstanceSet) +
(InstanceSet->Count *sizeof(ULONG));
SizeNeeded = (SizeNeeded +7) & ~7;
} else {
//
// The driver returned a completed WNODE_ALL_DATA
// so we need to link the previous WNODE_ALL_DATA to
// this one, fill out any static instance names, and
// then update the buffer pointer and size
//
if (WnodeLast != NULL)
{
Linkage = (ULONG) ((PCHAR)WnodeAD - (PCHAR)WnodeLast);
WnodeLast->Linkage = Linkage;
}
WnodeLast = (PWNODE_HEADER)WnodeAD;
if (UsesStaticNames)
{
//
// We need to insert the static names
//
WmipInsertStaticNames(WnodeAD,
BufferLeft,
InstanceSet);
if (WnodeAD->WnodeHeader.Flags & WNODE_FLAG_TOO_SMALL)
{
//
// The static names caused us to run out of
// buffer so we switch to mode where we
// query for the sizes
//
WnodeTooSmall = (PWNODE_TOO_SMALL)WnodeAD;
IsBufferTooSmall = TRUE;
BufferUsed = WnodeTooSmall->SizeNeeded;
} else {
//
// Static names fit so just pull out the updated
// wnode size
//
BufferUsed = WnodeAD->WnodeHeader.BufferSize;
}
} else {
//
// Wnode has dynamic names so just add size returned
// by driver
//
BufferUsed = WnodeAD->WnodeHeader.BufferSize;
}
//
// Update size needed and advance to free space in
// output buffer
//
BufferUsed = (BufferUsed + 7) & ~7;
SizeNeeded += BufferUsed;
//
// Make sure that by adding in pad we don't run out of
// room in buffer
//
if ((! IsBufferTooSmall) && (BufferLeft >= BufferUsed))
{
BufferLeft -= BufferUsed;
Buffer += BufferUsed;
} else {
IsBufferTooSmall = TRUE;
}
}
} else {
//
// The driver failed the request, but that is no biggie
// as we just ignore it for now
//
}
//
// We are done with the instance set so remove our ref
// on it so it can go away if need be
//
WmipUnreferenceIS(InstanceSet);
}
if (SizeNeeded == 0)
{
//
// No devices responded to the WMI Query All Data so we
// return an error
//
Status = STATUS_WMI_GUID_NOT_FOUND;
} else if ((IsBufferTooSmall) &&
(SizeNeeded > OutBufferLen)) {
//
// Our buffer passed was too small so return a WNODE_TOO_SMALL
//
WnodeTooSmall = (PWNODE_TOO_SMALL)Wnode;
WnodeTooSmall->WnodeHeader.BufferSize = sizeof(WNODE_TOO_SMALL);
WnodeTooSmall->WnodeHeader.Flags = WNODE_FLAG_TOO_SMALL;
WnodeTooSmall->SizeNeeded = SizeNeeded;
*RetSize = sizeof(WNODE_TOO_SMALL);
Status = STATUS_SUCCESS;
} else {
*RetSize = SizeNeeded;
Status = STATUS_SUCCESS;
}
//
// Make sure any memory allocated for the PI list is freed
//
if ((PIList != StaticPIList) && (PIList != NULL))
{
WmipFree(PIList);
}
}
}
//
// And remove ref on guid object
//
ObDereferenceObject(GuidObject);
}
return(Status);
}
NTSTATUS WmipQueryAllDataMultiple(
IN ULONG ObjectCount,
IN PWMIGUIDOBJECT *ObjectList,
IN PIRP Irp,
IN KPROCESSOR_MODE AccessMode,
IN OUT PUCHAR BufferPtr,
IN ULONG BufferSize,
IN PWMIQADMULTIPLE QadMultiple,
OUT ULONG *ReturnSize
)
{
ULONG i;
HANDLE *Handles;
ULONG Count;
WNODE_ALL_DATA WnodeAD;
BOOLEAN BufferOverFlow;
ULONG SkipSize, RetSize, SizeNeeded;
ULONG WnodeSize;
NTSTATUS Status, Status2;
ULONG Linkage = 0;
PWNODE_TOO_SMALL WnodeTooSmall;
PWNODE_HEADER WnodePrev;
PUCHAR Buffer;
PWNODE_ALL_DATA Wnode;
PWMIGUIDOBJECT Object = NULL;
PAGED_CODE();
Status = STATUS_SUCCESS;
if (ObjectList == NULL)
{
//
// Copy the handle list out of the system buffer since it will
// be overwritten by the first query all data
//
Count = QadMultiple->HandleCount;
Handles = (HANDLE *)WmipAlloc(Count * sizeof(HANDLE));
if (Handles != NULL)
{
for (i = 0; i < Count; i++)
{
Handles[i] = QadMultiple->Handles[i].Handle;
}
} else {
return STATUS_INSUFFICIENT_RESOURCES;
}
} else {
Count = ObjectCount;
Handles = NULL;
}
SizeNeeded = 0;
Buffer = BufferPtr;
BufferOverFlow = FALSE;
WnodePrev = NULL;
Wnode = (PWNODE_ALL_DATA)Buffer;
WnodeSize = BufferSize;
for (i = 0; i < Count; i++)
{
if ((Wnode == &WnodeAD) || (WnodeSize < sizeof(WNODE_ALL_DATA)))
{
//
// If there is no more room, we are just querying for the
// size that will be needed.
//
Wnode = &WnodeAD;
WnodeSize = sizeof(WNODE_ALL_DATA);
WnodePrev = NULL;
} else {
Wnode = (PWNODE_ALL_DATA)Buffer;
WnodeSize = BufferSize;
}
//
// Build WNODE_ALL_DATA in order to do the query
//
RtlZeroMemory(Wnode, sizeof(WNODE_ALL_DATA));
Wnode->WnodeHeader.Flags = WNODE_FLAG_ALL_DATA;
Wnode->WnodeHeader.BufferSize = sizeof(WNODE_HEADER);
if (ObjectList == NULL)
{
Wnode->WnodeHeader.KernelHandle = Handles[i];
} else {
Object = ObjectList[i];
}
Status2 = WmipQueryAllData(Object,
Irp,
AccessMode,
Wnode,
WnodeSize,
&RetSize);
if (NT_SUCCESS(Status2))
{
if (Wnode->WnodeHeader.Flags & WNODE_FLAG_INTERNAL)
{
//
// Skip any internal guid quesries
//
} else if (Wnode->WnodeHeader.Flags & WNODE_FLAG_TOO_SMALL) {
//
// There is no enough room so just tally up
// the size that will be needed.
//
WnodeTooSmall = (PWNODE_TOO_SMALL)Wnode;
SizeNeeded += (WnodeTooSmall->SizeNeeded+7) & ~7;
Wnode = &WnodeAD;
BufferOverFlow = TRUE;
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_API_INFO_LEVEL,"WMI: %x Too Small %x needed, total %x\n",
ObjectList ? ObjectList[i] : Handles[i],
WnodeTooSmall->SizeNeeded, SizeNeeded));
} else if (Wnode == &WnodeAD) {
//
// Even though this succeeded, we still aren't going
// to be able to return any data so just count up
// how much size we need
//
SizeNeeded += (RetSize+7) & ~7;
BufferOverFlow = TRUE;
WmipDebugPrintEx((DPFLTR_WMICORE_ID,
DPFLTR_API_INFO_LEVEL,"WMI: %x Large Enough but full %x needed, total %x\n",
ObjectList ? ObjectList[i] : Handles[i],
RetSize, SizeNeeded));
} else {
//
// We successfully got data. Link the previous wnode
// to this one
//
if (WnodePrev != NULL)
{
WnodePrev->Linkage = Linkage;
}
WnodePrev = (PWNODE_HEADER)Wnode;
while (WnodePrev->Linkage != 0)
{
WnodePrev = (PWNODE_HEADER)OffsetToPtr(WnodePrev,
WnodePrev->Linkage);
}
SkipSize = (RetSize+7) &~7;
SizeNeeded += SkipSize;
BufferSize -= SkipSize;
Buffer += SkipSize;
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_API_INFO_LEVEL,"WMI: %x Large Enough %x needed, total %x\n",
ObjectList ? ObjectList[i] : Handles[i],
RetSize, SizeNeeded));
Linkage = (ULONG) ((PCHAR)Buffer - (PCHAR)WnodePrev);
}
} else {
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_API_INFO_LEVEL,"WMI: %x Failed %x, total %x\n",
ObjectList ? ObjectList[i] : Handles[i],
Status2,
SizeNeeded));
}
}
if (Handles != NULL)
{
WmipFree(Handles);
}
if (BufferOverFlow)
{
WnodeTooSmall = (PWNODE_TOO_SMALL)BufferPtr;
WnodeTooSmall->WnodeHeader.BufferSize = sizeof(WNODE_TOO_SMALL);
WnodeTooSmall->WnodeHeader.Flags = WNODE_FLAG_TOO_SMALL;
WnodeTooSmall->SizeNeeded = SizeNeeded;
*ReturnSize = sizeof(WNODE_TOO_SMALL);
} else {
*ReturnSize = SizeNeeded;
}
return(Status);
}
NTSTATUS WmipPrepareWnodeSI(
IN PWMIGUIDOBJECT GuidObject,
IN OUT PWNODE_SINGLE_INSTANCE WnodeSI,
IN OUT ULONG *ProviderIdCount,
OUT PBINSTANCESET **ProviderIdList,
OUT BOOLEAN *IsDynamic,
OUT BOOLEAN *InternalGuid
)
{
NTSTATUS Status;
PBGUIDENTRY GuidEntry;
ULONG i;
PWNODE_HEADER Wnode;
PWCHAR CInstanceName;
PWCHAR InstanceName;
PLIST_ENTRY InstanceSetList;
PBINSTANCESET InstanceSet;
PBINSTANCESET *PIPtr = NULL;
PBINSTANCESET *StaticPIPtr = NULL;
ULONG PICount = 0, PIMax;
BOOLEAN Done;
PAGED_CODE();
*IsDynamic = TRUE;
GuidEntry = GuidObject->GuidEntry;
Wnode = (PWNODE_HEADER)WnodeSI;
if ((GuidEntry != NULL) && (GuidEntry->ISCount > 0))
{
//
// We were passed a valid guid handle, fill out the guid
// in WNODE_HEADER
//
Status = STATUS_SUCCESS;
Wnode->Guid = GuidEntry->Guid;
if (GuidEntry->Flags & GE_FLAG_INTERNAL)
{
*InternalGuid = TRUE;
} else {
*InternalGuid = FALSE;
//
// Obtain instance name from WNODE
//
CInstanceName = (PWCHAR)OffsetToPtr(WnodeSI,
WnodeSI->OffsetInstanceName);
InstanceName = WmipCountedToSz(CInstanceName);
if (InstanceName != NULL)
{
//
// Remember the static provider id list and assume that the
// request is going to be dynamic
//
StaticPIPtr = *ProviderIdList;
PIPtr = StaticPIPtr;
PIMax = *ProviderIdCount;
PICount = 0;
//
// March down instance set list to see if we have a
// static name and build the list of dynamic provider ids
//
Done = FALSE;
WmipEnterSMCritSection();
if (GuidEntry->ISCount > 0)
{
InstanceSetList = GuidEntry->ISHead.Flink;
while ((InstanceSetList != &GuidEntry->ISHead) && ! Done)
{
InstanceSet = CONTAINING_RECORD(InstanceSetList,
INSTANCESET,
GuidISList);
if ((InstanceSet->Flags & (IS_TRACED | IS_CONTROL_GUID | IS_EVENT_ONLY)) == 0)
{
//
// Only take those IS that are not traced or control
// guids and are not event only guids
//
if (InstanceSet->Flags & IS_INSTANCE_BASENAME)
{
PBISBASENAME IsBaseName;
ULONG BaseIndex;
PWCHAR BaseName;
SIZE_T BaseNameLen;
PWCHAR SuffixPtr;
ULONG Suffix;
WCHAR SuffixText[MAXBASENAMESUFFIXSIZE+1];
//
// See if the instance name is from this base name
//
IsBaseName = InstanceSet->IsBaseName;
BaseIndex = IsBaseName->BaseIndex;
BaseName = IsBaseName->BaseName;
BaseNameLen = wcslen(BaseName);
if ((wcslen(InstanceName) > BaseNameLen) &&
(_wcsnicmp(InstanceName, BaseName, BaseNameLen) == 0))
{
//
// The suffix matches the beginning of our instance
// name and our instance name is longer than the
// suffix.
//
SuffixPtr = &InstanceName[BaseNameLen];
Suffix = _wtoi(SuffixPtr);
if ((WmipIsNumber(SuffixPtr) &&
(Suffix >= BaseIndex) &&
(Suffix < (BaseIndex + InstanceSet->Count))))
{
//
// Our suffix is a number within the range for
// this instance set
//
if (Suffix < MAXBASENAMESUFFIXVALUE)
{
StringCbPrintf(SuffixText,
sizeof(SuffixText),
BASENAMEFORMATSTRING,
Suffix);
if (_wcsicmp(SuffixText, SuffixPtr) == 0)
{
//
// Our instance name is part of the
// instance set so note the provider id
// and instance index
//
Wnode->Flags |= WNODE_FLAG_STATIC_INSTANCE_NAMES;
Wnode->ProviderId = InstanceSet->ProviderId;
WnodeSI->InstanceIndex = Suffix - BaseIndex;
*IsDynamic = FALSE;
Done = TRUE;
}
}
}
}
} else if (InstanceSet->Flags & IS_INSTANCE_STATICNAMES) {
//
// See if the passed instance name matches any of the
// static names for this instnace set
//
PWCHAR *StaticNames;
StaticNames = InstanceSet->IsStaticNames->StaticNamePtr;
for (i =0; i < InstanceSet->Count; i++)
{
if (_wcsicmp(StaticNames[i], InstanceName) == 0)
{
//
// We matched our instance name with a static
// instance name. Remember provider id and
// instance index.
//
Wnode->Flags |= WNODE_FLAG_STATIC_INSTANCE_NAMES;
Wnode->ProviderId = InstanceSet->ProviderId;
WnodeSI->InstanceIndex = i;
*IsDynamic = FALSE;
Done = TRUE;
break;
}
}
} else {
//
// Remember dynamic providerid
//
WmipReferenceIS(InstanceSet);
Status = WmipAddProviderIdToPIList(&PIPtr,
&PICount,
&PIMax,
StaticPIPtr,
InstanceSet);
if (! NT_SUCCESS(Status))
{
Done = TRUE;
}
}
}
InstanceSetList = InstanceSetList->Flink;
}
} else {
//
// There are no instance sets registered for this guid
//
Status = STATUS_WMI_GUID_DISCONNECTED;
}
WmipFree(InstanceName);
WmipLeaveSMCritSection();
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
if (*IsDynamic)
{
//
// Dynamic instance name so return list of dynamic providers
//
*ProviderIdCount = PICount;
*ProviderIdList = PIPtr;
} else {
//
// Static instance name so unref an dynamic instance sets
//
if (PIPtr != NULL)
{
for (i = 0; i < PICount; i++)
{
WmipUnreferenceIS(PIPtr[i]);
}
if (PIPtr != StaticPIPtr)
{
WmipFree(PIPtr);
}
}
}
}
} else {
Status = STATUS_WMI_GUID_DISCONNECTED;
}
return(Status);
}
#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg("PAGECONST")
#endif
const ACCESS_MASK DesiredAccessForFunction[] =
{
WMIGUID_QUERY, // IRP_MN_QUERY_ALL_DATA
WMIGUID_QUERY, // IRP_MN_QUERY_SINGLE_INSTANCE
WMIGUID_SET, // IRP_MN_CHANGE_SINGLE_INSTANCE
WMIGUID_SET, // IRP_MN_CHANGE_SINGLE_ITEM
0, // IRP_MN_ENABLE_EVENTS
0, // IRP_MN_DISABLE_EVENTS
0, // IRP_MN_ENABLE_COLLECTION
0, // IRP_MN_DISABLE_COLLECTION
0, // IRP_MN_REGINFO
WMIGUID_EXECUTE, // IRP_MN_EXECUTE_METHOD
0, // IRP_MN_TRACE_EVENT or IRP_MN_SET_TRACE_NOTIFY
0 // IRP_MN_REGINFO_EX
};
NTSTATUS WmipQuerySetExecuteSI(
IN PWMIGUIDOBJECT GuidObject,
IN PIRP Irp,
IN KPROCESSOR_MODE AccessMode,
IN UCHAR MinorFunction,
IN OUT PWNODE_HEADER Wnode,
IN ULONG OutBufferSize,
OUT PULONG RetSize
)
{
NTSTATUS Status, ReturnStatus;
PBINSTANCESET StaticPIList[MANYPROVIDERIDS];
PBINSTANCESET *PIList;
HANDLE KernelHandle;
ULONG PICount;
BOOLEAN IsDynamic;
ULONG i;
BOOLEAN InternalGuid;
IO_STATUS_BLOCK Iosb;
#if DBG
BOOLEAN InstanceClaimed;
#endif
PAGED_CODE();
WmipAssert(((MinorFunction >= IRP_MN_QUERY_ALL_DATA) &&
(MinorFunction <= IRP_MN_CHANGE_SINGLE_ITEM)) ||
(MinorFunction == IRP_MN_EXECUTE_METHOD));
//
// Check Security
//
if (GuidObject != NULL)
{
Status = ObReferenceObjectByPointer(GuidObject,
DesiredAccessForFunction[MinorFunction],
WmipGuidObjectType,
AccessMode);
} else {
KernelHandle = Wnode->KernelHandle;
Status = ObReferenceObjectByHandle(KernelHandle,
DesiredAccessForFunction[MinorFunction],
WmipGuidObjectType,
AccessMode,
&GuidObject,
NULL);
}
if (NT_SUCCESS(Status))
{
PIList = StaticPIList;
PICount = MANYPROVIDERIDS;
Status = WmipPrepareWnodeSI(GuidObject,
(PWNODE_SINGLE_INSTANCE)Wnode,
&PICount,
&PIList,
&IsDynamic,
&InternalGuid);
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_API_INFO_LEVEL,
"WMI: QSI Prepare [%s - %s] %x with %x PI at %p\n",
IsDynamic ? "Dynamic" : "Static",
InternalGuid ? "Internal" : "External",
Wnode->KernelHandle, PICount, PIList));
if (NT_SUCCESS(Status))
{
if (InternalGuid)
{
//
// Internal guid query
//
Wnode->Flags |= WNODE_FLAG_INTERNAL;
Wnode->BufferSize = sizeof(WNODE_HEADER);
Irp->IoStatus.Information = sizeof(WNODE_HEADER);
} else {
if (IsDynamic)
{
//
// We need to loop over all dynamic instance names until
// one of them responds successfully and then we assume
// that they own the instance
//
#if DBG
InstanceClaimed = FALSE;
#endif
if ((MinorFunction == IRP_MN_CHANGE_SINGLE_ITEM) ||
(MinorFunction == IRP_MN_EXECUTE_METHOD))
{
Status = STATUS_WMI_ITEMID_NOT_FOUND;
} else {
Status = STATUS_WMI_INSTANCE_NOT_FOUND;
}
for (i = 0; i < PICount; i++)
{
Wnode->ProviderId = PIList[i]->ProviderId;
if (Irp != NULL)
{
ReturnStatus = WmipForwardWmiIrp(Irp,
MinorFunction,
Wnode->ProviderId,
&Wnode->Guid,
OutBufferSize,
Wnode);
if (NT_SUCCESS(ReturnStatus))
{
*RetSize = (ULONG)Irp->IoStatus.Information;
}
} else {
ReturnStatus = WmipSendWmiIrp(
MinorFunction,
Wnode->ProviderId,
&Wnode->Guid,
OutBufferSize,
Wnode,
&Iosb);
if (NT_SUCCESS(ReturnStatus))
{
*RetSize = (ULONG)Iosb.Information;
}
}
//
// One of these status codes imply that the device does
// positively claim the instance name and so we break out
// and return the results
//
if ((NT_SUCCESS(ReturnStatus)) ||
(ReturnStatus == STATUS_WMI_SET_FAILURE) ||
(ReturnStatus == STATUS_WMI_ITEMID_NOT_FOUND) ||
(ReturnStatus == STATUS_WMI_READ_ONLY))
{
Status = ReturnStatus;
break;
}
//
// If the device does not own the instance it can
// only return STATUS_WMI_INSTANCE_NOT_FOUND or
// STATUS_WMI_GUID_NOT_FOUND. Any other return code
// implies that the device owns the instance, but
// encountered an error.
//
if ( (ReturnStatus != STATUS_WMI_INSTANCE_NOT_FOUND) &&
(ReturnStatus != STATUS_WMI_GUID_NOT_FOUND))
{
WmipAssert(! InstanceClaimed);
#if DBG
InstanceClaimed = TRUE;
#endif
Status = ReturnStatus;
}
WmipUnreferenceIS(PIList[i]);
}
if ((PIList != StaticPIList) && (PIList != NULL))
{
WmipFree(PIList);
}
} else {
//
// Since we have a static instance name we can target directly
// at the device that has our instance name
//
if (Irp != NULL)
{
Status = WmipForwardWmiIrp(Irp,
MinorFunction,
Wnode->ProviderId,
&Wnode->Guid,
OutBufferSize,
Wnode);
*RetSize = (ULONG)Irp->IoStatus.Information;
} else {
Status = WmipSendWmiIrp(
MinorFunction,
Wnode->ProviderId,
&Wnode->Guid,
OutBufferSize,
Wnode,
&Iosb);
*RetSize = (ULONG)Iosb.Information;
}
}
}
}
//
// And remove ref on guid object
//
ObDereferenceObject(GuidObject);
}
return(Status);
}
NTSTATUS WmipQuerySingleMultiple(
IN PIRP Irp,
IN KPROCESSOR_MODE AccessMode,
IN OUT PUCHAR BufferPtr,
IN ULONG BufferSize,
IN PWMIQSIMULTIPLE QsiMultiple,
IN ULONG QueryCount,
IN PWMIGUIDOBJECT *ObjectList,
IN PUNICODE_STRING InstanceNames,
OUT ULONG *ReturnSize
)
{
PWMIQSIINFO QsiInfo;
UCHAR WnodeQSIStatic[sizeof(WNODE_SINGLE_INSTANCE) +
256*sizeof(WCHAR) +
sizeof(ULONG)];
PWNODE_SINGLE_INSTANCE WnodeQSI;
ULONG WnodeQSISize;
ULONG WnodeSizeNeeded;
NTSTATUS Status, Status2;
ULONG SizeNeeded;
BOOLEAN BufferFull, BufferOverFlow;
PWNODE_HEADER WnodePrev;
PUCHAR Buffer;
ULONG i;
ULONG WnodeSize;
PWNODE_SINGLE_INSTANCE Wnode;
PWCHAR InstanceName;
ULONG RetSize;
PWNODE_TOO_SMALL WnodeTooSmall;
ULONG Linkage = 0;
ULONG SkipSize;
PWMIGUIDOBJECT Object = NULL;
UNICODE_STRING UString;
HANDLE KernelHandle;
PAGED_CODE();
//
// We are called by kernel mode and passed an object list and InstanceNames
// or we are called by user mode and passed a QsiMultiple instead
//
WmipAssert( ((AccessMode == KernelMode) &&
(QsiMultiple == NULL) &&
(ObjectList != NULL) &&
(InstanceNames != NULL)) ||
((AccessMode == UserMode) &&
(QsiMultiple != NULL) &&
(ObjectList == NULL) &&
(InstanceNames == NULL)) );
Status = STATUS_SUCCESS;
if (ObjectList == NULL)
{
//
// if this is a user call then we need to copy out the
// QSIMULTIPLE information since it is in the system buffer and
// will get overwritten on the first query
//
QsiInfo = (PWMIQSIINFO)WmipAlloc(QueryCount * sizeof(WMIQSIINFO));
if (QsiInfo != NULL)
{
RtlCopyMemory(QsiInfo,
&QsiMultiple->QsiInfo,
QueryCount * sizeof(WMIQSIINFO));
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
Object = NULL;
} else {
QsiInfo = NULL;
}
if (NT_SUCCESS(Status))
{
SizeNeeded = 0;
BufferFull = FALSE;
BufferOverFlow = FALSE;
WnodePrev = NULL;
Buffer = BufferPtr;
WnodeQSI = (PWNODE_SINGLE_INSTANCE)&WnodeQSIStatic;
WnodeQSISize = sizeof(WnodeQSIStatic);
for (i = 0; i < QueryCount; i++)
{
if (ObjectList == NULL)
{
UString.Length = QsiInfo[i].InstanceName.Length;
UString.MaximumLength = QsiInfo[i].InstanceName.MaximumLength;
UString.Buffer = QsiInfo[i].InstanceName.Buffer;
KernelHandle = QsiInfo[i].Handle.Handle;
} else {
UString = InstanceNames[i];
Object = ObjectList[i];
KernelHandle = NULL;
}
WnodeSizeNeeded = (FIELD_OFFSET(WNODE_SINGLE_INSTANCE,
VariableData) +
UString.Length +
sizeof(USHORT) + 7) & ~7;
if ((BufferFull) || (BufferSize < WnodeSizeNeeded))
{
//
// If there is no more room, we are just querying for the
// size that will be needed.
//
if (WnodeSizeNeeded > WnodeQSISize)
{
//
// Our temporary buffer is too small so lets alloc a
// larger one
//
if (WnodeQSI != (PWNODE_SINGLE_INSTANCE)WnodeQSIStatic)
{
WmipFree(WnodeQSI);
}
WnodeQSI = (PWNODE_SINGLE_INSTANCE)WmipAllocNP(WnodeSizeNeeded);
if (WnodeQSI == NULL)
{
//
// We couldn't allocate a larger temporary buffer
// so we abort this call and try to exit gracefully
//
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
WnodeQSISize = WnodeSizeNeeded;
}
Wnode = WnodeQSI;
WnodeSize = WnodeSizeNeeded;
WnodePrev = NULL;
BufferFull = TRUE;
} else {
//
// Plenty of room so build wnode directly into the output
// buffer
//
Wnode = (PWNODE_SINGLE_INSTANCE)Buffer;
WnodeSize = BufferSize;
}
//
// Build WNODE_SINGLE_INSTANCE in order to do the query
//
RtlZeroMemory(Wnode, sizeof(WNODE_SINGLE_INSTANCE));
Wnode->WnodeHeader.Flags = WNODE_FLAG_SINGLE_INSTANCE;
Wnode->WnodeHeader.BufferSize = WnodeSizeNeeded;
Wnode->WnodeHeader.KernelHandle = KernelHandle;
Wnode->OffsetInstanceName = FIELD_OFFSET(WNODE_SINGLE_INSTANCE,
VariableData);
Wnode->DataBlockOffset = WnodeSizeNeeded;
InstanceName = (PWCHAR)OffsetToPtr(Wnode,
Wnode->OffsetInstanceName);
*InstanceName++ = UString.Length;
try
{
if (AccessMode == UserMode)
{
ProbeForRead(UString.Buffer,
UString.Length,
sizeof(WCHAR));
}
RtlCopyMemory(InstanceName,
UString.Buffer,
UString.Length);
} except(EXCEPTION_EXECUTE_HANDLER) {
//
// If an error occured probing then we fail the entire call
//
Status = GetExceptionCode();
break;
}
Status2 = WmipQuerySetExecuteSI(Object,
Irp,
AccessMode,
IRP_MN_QUERY_SINGLE_INSTANCE,
(PWNODE_HEADER)Wnode,
WnodeSize,
&RetSize);
if (NT_SUCCESS(Status2))
{
if (Wnode->WnodeHeader.Flags & WNODE_FLAG_INTERNAL)
{
//
// Skip any internal guid quesries
//
} else if (Wnode->WnodeHeader.Flags & WNODE_FLAG_TOO_SMALL) {
//
// There is no enough room so just tally up
// the size that will be needed.
//
WnodeTooSmall = (PWNODE_TOO_SMALL)Wnode;
SizeNeeded += (WnodeTooSmall->SizeNeeded+7) & ~7;
BufferFull = TRUE;
BufferOverFlow = TRUE;
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_API_INFO_LEVEL,
"WMI: QSIM %ws too small %x SizeNeeded %x\n",
UString.Buffer,
(WnodeTooSmall->SizeNeeded+7) & ~7,
SizeNeeded));
} else if (BufferFull) {
//
// There was enough room, but the buffer was already
// filled so we just tally up the size needed
//
SizeNeeded += (RetSize+7) & ~7;
BufferOverFlow = TRUE;
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_API_INFO_LEVEL,
"WMI: QSIM %ws big enough but full %x SizeNeeded %x\n",
UString.Buffer,
(RetSize+7) & ~7,
SizeNeeded));
} else {
//
// We successfully got data. Link the previous wnode
// to this one
//
if (WnodePrev != NULL)
{
WnodePrev->Linkage = Linkage;
}
WnodePrev = (PWNODE_HEADER)Wnode;
while (WnodePrev->Linkage != 0)
{
WnodePrev = (PWNODE_HEADER)OffsetToPtr(WnodePrev,
WnodePrev->Linkage);
}
SkipSize = (RetSize+7) &~7;
SizeNeeded += SkipSize;
BufferSize -= SkipSize;
Buffer += SkipSize;
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_API_INFO_LEVEL,
"WMI: QSIM %ws big enough %x SizeNeeded %x\n",
UString.Buffer,
SkipSize,
SizeNeeded));
Linkage = (ULONG) ((PCHAR)Buffer - (PCHAR)WnodePrev);
}
} else {
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_API_INFO_LEVEL,
"WMI: QSIM %ws Failed SizeNeeded %x\n",
UString.Buffer,
SizeNeeded));
}
}
if (WnodeQSI != (PWNODE_SINGLE_INSTANCE)WnodeQSIStatic)
{
WmipFree(WnodeQSI);
}
if (NT_SUCCESS(Status) && (BufferFull))
{
WnodeTooSmall = (PWNODE_TOO_SMALL)BufferPtr;
WnodeTooSmall->WnodeHeader.BufferSize = sizeof(WNODE_TOO_SMALL);
WnodeTooSmall->WnodeHeader.Flags = WNODE_FLAG_TOO_SMALL;
WnodeTooSmall->SizeNeeded = SizeNeeded;
*ReturnSize = sizeof(WNODE_TOO_SMALL);
} else {
*ReturnSize = SizeNeeded;
}
if (QsiInfo != NULL)
{
WmipFree(QsiInfo);
}
}
return(Status);
}
void
WmipGetGuidPropertiesFromGuidEntry(
PWMIGUIDPROPERTIES GuidInfo,
PGUIDENTRY GuidEntry)
/*++
Routine Description:
This routine fills GuidInfo with the properties for the Guid
represented by the GuidEntry. Note that this call is made holding
the SMCritSection.
Arguments:
Return Value:
--*/
{
PLIST_ENTRY InstanceSetList;
PBINSTANCESET InstanceSet;
PAGED_CODE();
GuidInfo->GuidType = WMI_GUIDTYPE_DATA;
GuidInfo->IsEnabled = FALSE;
GuidInfo->LoggerId = 0;
GuidInfo->EnableLevel = 0;
GuidInfo->EnableFlags = 0;
InstanceSetList = GuidEntry->ISHead.Flink;
while (InstanceSetList != &GuidEntry->ISHead)
{
InstanceSet = CONTAINING_RECORD(InstanceSetList,
INSTANCESET,
GuidISList);
if (InstanceSet->Flags & IS_EVENT_ONLY)
{
GuidInfo->GuidType = WMI_GUIDTYPE_EVENT;
}
if (((InstanceSet->Flags & IS_ENABLE_EVENT) ||
(InstanceSet->Flags & IS_ENABLE_COLLECTION)) ||
(InstanceSet->Flags & IS_COLLECTING))
{
GuidInfo->IsEnabled = TRUE;
}
if ( (InstanceSet->Flags & IS_TRACED) &&
(InstanceSet->Flags & IS_CONTROL_GUID) )
{
GuidInfo->GuidType = WMI_GUIDTYPE_TRACECONTROL;
break;
}
InstanceSetList = InstanceSetList->Flink;
}
if (GuidEntry->Flags & GE_NOTIFICATION_TRACE_FLAG)
{
if (GuidInfo->GuidType == WMI_GUIDTYPE_TRACECONTROL) {
//
// If a NotificationEntry is found for a TraceControlGuid
// it means that it is enabled.
//
ULONG64 LoggerContext = GuidEntry->LoggerContext;
GuidInfo->IsEnabled = TRUE;
GuidInfo->LoggerId = WmiGetLoggerId(LoggerContext);
GuidInfo->EnableLevel = WmiGetLoggerEnableLevel(LoggerContext);
GuidInfo->EnableFlags = WmiGetLoggerEnableFlags(LoggerContext);
}
}
}
NTSTATUS WmipEnumerateGuids(
ULONG Ioctl,
PWMIGUIDLISTINFO GuidList,
ULONG MaxBufferSize,
ULONG *OutBufferSize
)
{
ULONG TotalGuidCount;
ULONG WrittenGuidCount;
ULONG AllowedGuidCount;
PWMIGUIDPROPERTIES GuidPtr;
PBGUIDENTRY GuidEntry;
PLIST_ENTRY GuidEntryList;
PAGED_CODE();
TotalGuidCount = 0;
WrittenGuidCount = 0;
AllowedGuidCount = (MaxBufferSize - FIELD_OFFSET(WMIGUIDLISTINFO, GuidList)) / sizeof(WMIGUIDPROPERTIES);
GuidPtr = &GuidList->GuidList[0];
WmipEnterSMCritSection();
//
// Fill up structure with list of guids
//
GuidEntryList = WmipGEHeadPtr->Flink;
while (GuidEntryList != WmipGEHeadPtr)
{
GuidEntry = CONTAINING_RECORD(GuidEntryList,
GUIDENTRY,
MainGEList);
TotalGuidCount++;
if (WrittenGuidCount < AllowedGuidCount)
{
GuidPtr[WrittenGuidCount].Guid = GuidEntry->Guid;
WrittenGuidCount++;
}
GuidEntryList = GuidEntryList->Flink;
}
if (Ioctl == IOCTL_WMI_ENUMERATE_GUIDS_AND_PROPERTIES)
{
//
// If needed fill struct with guid properties
//
TotalGuidCount = 0;
WrittenGuidCount = 0;
GuidEntryList = WmipGEHeadPtr->Flink;
while (GuidEntryList != WmipGEHeadPtr)
{
GuidEntry = CONTAINING_RECORD(GuidEntryList,
GUIDENTRY,
MainGEList);
TotalGuidCount++;
if (WrittenGuidCount < AllowedGuidCount)
{
WmipGetGuidPropertiesFromGuidEntry(&GuidPtr[WrittenGuidCount],
GuidEntry);
WrittenGuidCount++;
}
GuidEntryList = GuidEntryList->Flink;
}
}
WmipLeaveSMCritSection();
GuidList->TotalGuidCount = TotalGuidCount;
GuidList->ReturnedGuidCount = WrittenGuidCount;
*OutBufferSize = FIELD_OFFSET(WMIGUIDLISTINFO, GuidList) +
WrittenGuidCount * sizeof(WMIGUIDPROPERTIES);
return(STATUS_SUCCESS);
}
NTSTATUS WmipQueryGuidInfo(
IN OUT PWMIQUERYGUIDINFO QueryGuidInfo
)
{
HANDLE Handle;
NTSTATUS Status;
PLIST_ENTRY InstanceSetList;
PBINSTANCESET InstanceSet;
PBGUIDENTRY GuidEntry;
PWMIGUIDOBJECT GuidObject;
PAGED_CODE();
Handle = QueryGuidInfo->KernelHandle.Handle;
Status = ObReferenceObjectByHandle(Handle,
WMIGUID_QUERY,
WmipGuidObjectType,
UserMode,
&GuidObject,
NULL);
if (NT_SUCCESS(Status))
{
GuidEntry = GuidObject->GuidEntry;
if (GuidEntry != NULL)
{
//
// Assume that the guid is not expensive and then loop over
// all instances to see if one of them is expensive.
//
QueryGuidInfo->IsExpensive = FALSE;
WmipEnterSMCritSection();
InstanceSetList = GuidEntry->ISHead.Flink;
while (InstanceSetList != &GuidEntry->ISHead)
{
InstanceSet = CONTAINING_RECORD(InstanceSetList,
INSTANCESET,
GuidISList);
if (InstanceSet->Flags & IS_EXPENSIVE)
{
//
// The guid is expensive so remember that and break
// out of loop
//
QueryGuidInfo->IsExpensive = TRUE;
break;
}
InstanceSetList = InstanceSetList->Flink;
}
WmipLeaveSMCritSection();
} else {
//
// The guid object exists, but there is not a corresponding
// guidentry which is an error.
//
Status = STATUS_WMI_GUID_DISCONNECTED;
}
//
// And remove ref on guid object
//
ObDereferenceObject(GuidObject);
}
return(Status);
}
//
// The head of the list that contains the guid objects associated with
// an irp is in the DriverContext part of the irp
//
#define IRP_OBJECT_LIST_HEAD(Irp) (PLIST_ENTRY)((Irp)->Tail.Overlay.DriverContext)
void WmipClearIrpObjectList(
PIRP Irp
)
{
PLIST_ENTRY ObjectListHead;
PLIST_ENTRY ObjectList, ObjectListNext;
PWMIGUIDOBJECT Object;
PAGED_CODE();
//
// This routine assumes that the SMCritSection is being held
//
ObjectListHead = IRP_OBJECT_LIST_HEAD(Irp);
ObjectList = ObjectListHead->Flink;
//
// Loop over all objects associated with this irp and reset the
// value for its associated irp since this irp is now going away
//
while (ObjectList != ObjectListHead)
{
Object = CONTAINING_RECORD(ObjectList,
WMIGUIDOBJECT,
IrpObjectList);
WmipAssert(Object->Irp == Irp);
WmipAssert(Object->EventQueueAction == RECEIVE_ACTION_NONE);
Object->Irp = NULL;
RemoveEntryList(ObjectList);
ObjectListNext = ObjectList->Flink;
ObjectList = ObjectListNext;
}
}
void WmipClearObjectFromThreadList(
PWMIGUIDOBJECT Object
)
{
PLIST_ENTRY ThreadList;
PAGED_CODE();
ThreadList = &Object->ThreadObjectList;
if (IsListEmpty(ThreadList))
{
//
// if this is the last object on the thread list then we need
// to close the handle (in the system handle table) to the user
// mode process
//
ZwClose(Object->UserModeProcess);
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_API_INFO_LEVEL,
"WMI: Closed UserModeProcessHandle %x\n", Object->UserModeProcess));
}
Object->UserModeProcess = NULL;
Object->UserModeCallback = NULL;
Object->EventQueueAction = RECEIVE_ACTION_NONE;
RemoveEntryList(ThreadList);
InitializeListHead(ThreadList);
}
void WmipClearThreadObjectList(
PWMIGUIDOBJECT MainObject
)
{
PWMIGUIDOBJECT Object;
PLIST_ENTRY ObjectList;
#if DBG
HANDLE MyUserModeProcess;
PUSER_THREAD_START_ROUTINE MyUserModeCallback;
#endif
PAGED_CODE();
//
// This routine assumes the SMCrit Section is held
//
#if DBG
MyUserModeProcess = MainObject->UserModeProcess;
MyUserModeCallback = MainObject->UserModeCallback;
#endif
ObjectList = &MainObject->ThreadObjectList;
do
{
Object = CONTAINING_RECORD(ObjectList,
WMIGUIDOBJECT,
ThreadObjectList);
WmipAssert(Object->UserModeProcess == MyUserModeProcess);
WmipAssert(Object->UserModeCallback == MyUserModeCallback);
WmipAssert(Object->EventQueueAction == RECEIVE_ACTION_CREATE_THREAD);
ObjectList = ObjectList->Flink;
WmipClearObjectFromThreadList(Object);
} while (! IsListEmpty(ObjectList));
}
void WmipNotificationIrpCancel(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Cancel routine for a pending read notification irp.
Arguments:
DeviceObject is the device object of the WMI service device
Irp is the pending Irp to be cancelled
Return Value:
--*/
{
UNREFERENCED_PARAMETER (DeviceObject);
IoReleaseCancelSpinLock(Irp->CancelIrql);
WmipEnterSMCritSection();
WmipClearIrpObjectList(Irp);
WmipLeaveSMCritSection();
Irp->IoStatus.Status = STATUS_CANCELLED;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT );
}
#define WmipHaveHiPriorityEvent(Object) \
(((Object)->HiPriority.Buffer != NULL) && \
((Object)->HiPriority.NextOffset != 0))
#define WmipHaveLoPriorityEvent(Object) \
(((Object)->LoPriority.Buffer != NULL) && \
((Object)->LoPriority.NextOffset != 0))
#define WmipSetHighWord(a, b) \
(a) &= ~(0xffff0000); \
(a) |= ( (USHORT)(b) << 16)
void WmipCopyFromEventQueues(
IN POBJECT_EVENT_INFO ObjectArray,
IN ULONG HandleCount,
OUT PUCHAR OutBuffer,
OUT ULONG *OutBufferSizeUsed,
OUT PWNODE_HEADER *LastWnode,
IN BOOLEAN IsHiPriority
)
{
PWMIGUIDOBJECT GuidObject;
ULONG i, Earliest;
ULONG SizeUsed, Size;
PWNODE_HEADER InWnode, OutWnode;
LARGE_INTEGER Timestamp, LastTimestamp;
PWMIEVENTQUEUE EventQueue;
//
// Consider adding extra code for perf
// 1. If only 1 object is passed
// 2. Once we find the earliest event we look ahead in that same
// event queue buffer assuming that it will be earlier than
// events in other buffers. This makes sense when only one queue
// has events left in it.
//
PAGED_CODE();
//
// This routine assumes that the output buffer has been checked and
// that it is large enough to accomodate all of the events. This
// implies that this function is called while holding the critical
// section.
//
//
// See which guid objects have events to be processed
//
for (i = 0; i < HandleCount; i++)
{
GuidObject = ObjectArray[i].GuidObject;
if (IsHiPriority)
{
if ((GuidObject->HiPriority.Buffer != NULL) &&
(GuidObject->HiPriority.NextOffset != 0))
{
ObjectArray[i].NextWnode = (PWNODE_HEADER)GuidObject->HiPriority.Buffer;
WmipSetHighWord(ObjectArray[i].NextWnode->Version,
GuidObject->HiPriority.EventsLost);
GuidObject->HiPriority.EventsLost = 0;
WmipAssert(ObjectArray[i].NextWnode != NULL);
} else {
ObjectArray[i].NextWnode = NULL;
}
} else {
if ((GuidObject->LoPriority.Buffer != 0) &&
(GuidObject->LoPriority.NextOffset != 0))
{
ObjectArray[i].NextWnode = (PWNODE_HEADER)GuidObject->LoPriority.Buffer;
WmipSetHighWord(ObjectArray[i].NextWnode->Version,
GuidObject->LoPriority.EventsLost);
GuidObject->LoPriority.EventsLost = 0;
WmipAssert(ObjectArray[i].NextWnode != NULL);
} else {
ObjectArray[i].NextWnode = NULL;
}
}
}
//
// loop until all events in all guid objects have been
// processed
//
SizeUsed = 0;
Earliest = 0;
OutWnode = NULL;
while (Earliest != 0xffffffff)
{
Timestamp.QuadPart = 0x7fffffffffffffff;
Earliest = 0xffffffff;
for (i = 0; i < HandleCount; i++)
{
InWnode = (PWNODE_HEADER)ObjectArray[i].NextWnode;
if ((InWnode != NULL) &&
(InWnode->TimeStamp.QuadPart < Timestamp.QuadPart))
{
//
// We found an event that is earlier than any previous
// one so we remember the new candidate for earliest
// event and also the previous early event
//
LastTimestamp = Timestamp;
Timestamp = InWnode->TimeStamp;
Earliest = i;
}
}
if (Earliest != 0xffffffff)
{
//
// We found the earliest event so copy it into the output
// buffer
//
InWnode = (PWNODE_HEADER)ObjectArray[Earliest].NextWnode;
Size = (InWnode->BufferSize + 7) & ~7;
OutWnode = (PWNODE_HEADER)OutBuffer;
RtlCopyMemory(OutWnode, InWnode, InWnode->BufferSize);
OutWnode->Linkage = Size;
OutBuffer += Size;
SizeUsed += Size;
if (InWnode->Linkage != 0)
{
InWnode = (PWNODE_HEADER)((PUCHAR)InWnode + InWnode->Linkage);
} else {
InWnode = NULL;
}
ObjectArray[Earliest].NextWnode = InWnode;
}
}
*LastWnode = OutWnode;
*OutBufferSizeUsed = SizeUsed;
//
// clean up event queue resources and reset the object
//
for (i = 0; i < HandleCount; i++)
{
GuidObject = ObjectArray[i].GuidObject;
if (IsHiPriority)
{
EventQueue = &GuidObject->HiPriority;
} else {
EventQueue = &GuidObject->LoPriority;
}
if (EventQueue->Buffer != NULL)
{
WmipFree(EventQueue->Buffer);
EventQueue->Buffer = NULL;
EventQueue->NextOffset = 0;
EventQueue->LastWnode = NULL;
}
KeClearEvent(&GuidObject->Event);
}
}
void WmipCompleteGuidIrpWithError(
PWMIGUIDOBJECT GuidObject
)
{
PIRP OldIrp;
PAGED_CODE();
//
// This routine assumes that the SM Critical Section is held
//
//
// If this object is already being waited on by a different
// irp then we need to fail the original irp since we only
// allow a single irp to wait on a specific object
//
WmipAssert(GuidObject->IrpObjectList.Flink != NULL);
WmipAssert(GuidObject->IrpObjectList.Blink != NULL);
OldIrp = GuidObject->Irp;
if (IoSetCancelRoutine(OldIrp, NULL))
{
//
// If there was a cancel routine then this means that
// the irp is still pending so we can go and complete it
//
WmipClearIrpObjectList(OldIrp);
WmipAssert(GuidObject->Irp == NULL);
OldIrp->IoStatus.Status = STATUS_INVALID_HANDLE;
IoCompleteRequest(OldIrp, IO_NO_INCREMENT);
}
}
NTSTATUS WmipMarkHandleAsClosed(
HANDLE Handle
)
{
NTSTATUS Status;
PWMIGUIDOBJECT GuidObject;
PAGED_CODE();
Status = ObReferenceObjectByHandle(Handle,
WMIGUID_NOTIFICATION,
WmipGuidObjectType,
UserMode,
&GuidObject,
NULL);
if (NT_SUCCESS(Status))
{
//
// Mark object as no longer able to receive events
//
WmipEnterSMCritSection();
GuidObject->Flags |= WMIGUID_FLAG_RECEIVE_NO_EVENTS;
if (GuidObject->Irp != NULL)
{
//
// If this object was is waiting in a pending irp then we
// need to complete the irp to keep the pump moving
//
WmipCompleteGuidIrpWithError(GuidObject);
}
WmipLeaveSMCritSection();
ObDereferenceObject(GuidObject);
}
return(Status);
}
NTSTATUS WmipReceiveNotifications(
PWMIRECEIVENOTIFICATION ReceiveNotification,
PULONG OutBufferSize,
PIRP Irp
)
{
#define MANY_NOTIFICATION_OBJECTS 16
ULONG i;
PWMIGUIDOBJECT GuidObject;
ULONG HandleCount;
PHANDLE3264 HandleArray;
OBJECT_EVENT_INFO *ObjectArray;
OBJECT_EVENT_INFO StaticObjects[MANY_NOTIFICATION_OBJECTS];
PUCHAR OutBuffer;
UCHAR IsLoPriorityEvent, IsHighPriorityEvent, ReplacingIrp;
NTSTATUS Status = STATUS_INVALID_PARAMETER;
PWNODE_HEADER LastWnode;
PLIST_ENTRY IrpListHead, ThreadListHead;
ULONG MaxBufferSize, SizeUsed;
PVOID UserProcessObject;
HANDLE UserModeProcess;
ULONG SizeLeft, SizeNeeded, HiTotalSizeNeeded, LoTotalSizeNeeded;
PWNODE_TOO_SMALL WnodeTooSmall;
ULONG j, ObjectCount;
BOOLEAN DuplicateObject;
PIMAGE_NT_HEADERS NtHeaders;
SIZE_T StackSize, StackCommit;
#if defined(_WIN64)
PVOID Wow64Process;
PIMAGE_NT_HEADERS32 NtHeaders32;
#endif
PAGED_CODE();
MaxBufferSize = *OutBufferSize;
HandleCount = ReceiveNotification->HandleCount;
HandleArray = ReceiveNotification->Handles;
//
// Create space to store the object pointers so we can work with them
//
if (HandleCount > MANY_NOTIFICATION_OBJECTS)
{
ObjectArray = WmipAlloc(HandleCount * sizeof(OBJECT_EVENT_INFO));
if (ObjectArray == NULL)
{
return(STATUS_INSUFFICIENT_RESOURCES);
}
} else {
ObjectArray = StaticObjects;
}
#if DBG
RtlZeroMemory(ObjectArray, HandleCount * sizeof(OBJECT_EVENT_INFO));
#endif
//
// First check that we all handles are entitled to receive notifications
// and that the object is not already associated with an irp.
// Also check if there are any hi or lo priority events
//
WmipEnterSMCritSection();
IsLoPriorityEvent = 0;
IsHighPriorityEvent = 0;
ReplacingIrp = 0;
HiTotalSizeNeeded = 0;
LoTotalSizeNeeded = 0;
ObjectCount = 0;
for (i = 0; (i < HandleCount); i++)
{
Status = ObReferenceObjectByHandle(HandleArray[i].Handle,
WMIGUID_NOTIFICATION,
WmipGuidObjectType,
UserMode,
&GuidObject,
NULL);
if (! NT_SUCCESS(Status))
{
//
// If one handle is bad then it spoils the whole request
//
//
// Now try with Trace flags and if succeeds,
// We need to make sure the object is a trace request object.
//
Status = ObReferenceObjectByHandle(HandleArray[i].Handle,
TRACELOG_REGISTER_GUIDS,
WmipGuidObjectType,
UserMode,
&GuidObject,
NULL);
if (! NT_SUCCESS(Status))
{
goto Cleanup;
}
if (! (GuidObject->Flags & WMIGUID_FLAG_REQUEST_OBJECT) )
{
ObDereferenceObject(GuidObject);
Status = STATUS_ACCESS_DENIED;
goto Cleanup;
}
}
//
// Check that we do not have a duplicate object in the list
//
DuplicateObject = FALSE;
for (j = 0; j < ObjectCount; j++)
{
if (GuidObject == ObjectArray[j].GuidObject)
{
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_ERROR_LEVEL,
"WMI: Duplicate object %p passed to WmiReceiveNotifciations\n",
GuidObject));
ObDereferenceObject(GuidObject);
DuplicateObject = TRUE;
break;
}
}
if (! DuplicateObject)
{
//
// See if there was an irp attached to the guid object
// already. We'll need to cancel it if all guid objects
// are valid
//
if (GuidObject->Irp != NULL)
{
ReplacingIrp = 1;
}
//
// We note if there are any lo and hi priority events
//
ObjectArray[ObjectCount++].GuidObject = GuidObject;
if (WmipHaveHiPriorityEvent(GuidObject))
{
IsHighPriorityEvent = 1;
}
if (WmipHaveLoPriorityEvent(GuidObject))
{
IsLoPriorityEvent = 1;
}
//
// Clean up object in case it was part of a thread list
//
if (GuidObject->EventQueueAction == RECEIVE_ACTION_CREATE_THREAD)
{
WmipAssert(ReplacingIrp == 0);
WmipClearObjectFromThreadList(GuidObject);
}
//
// Calculate size needed to return data for this guid
//
HiTotalSizeNeeded += ((GuidObject->HiPriority.NextOffset + 7) & ~7);
LoTotalSizeNeeded += ((GuidObject->LoPriority.NextOffset + 7) & ~7);
}
}
//
// This is the total size needed to return all events
//
SizeNeeded = HiTotalSizeNeeded + LoTotalSizeNeeded;
//
// If any of the guid objects already had an irp attached then
// we need to complete that irp with an error and move on
//
if (ReplacingIrp == 1)
{
for (i = 0; i < ObjectCount; i++)
{
GuidObject = ObjectArray[i].GuidObject;
if (GuidObject->Irp != NULL)
{
WmipCompleteGuidIrpWithError(GuidObject);
}
}
}
if ( (IsHighPriorityEvent | IsLoPriorityEvent) != 0 )
{
if (SizeNeeded <= MaxBufferSize)
{
//
// There are events waiting to be recieved so pull them all
// out, high priority ones first then low priority ones. // events will show up first.
//
OutBuffer = (PUCHAR)ReceiveNotification;
LastWnode = NULL;
SizeLeft = MaxBufferSize;
SizeUsed = 0;
if (IsHighPriorityEvent != 0)
{
WmipCopyFromEventQueues(ObjectArray,
ObjectCount,
OutBuffer,
&SizeUsed,
&LastWnode,
TRUE);
WmipAssert(SizeUsed <= SizeLeft);
WmipAssert(SizeUsed = HiTotalSizeNeeded);
OutBuffer += SizeUsed;
SizeLeft -= SizeUsed;
}
if (IsLoPriorityEvent != 0)
{
WmipAssert(SizeLeft >= LoTotalSizeNeeded);
WmipCopyFromEventQueues(ObjectArray,
ObjectCount,
OutBuffer,
&SizeUsed,
&LastWnode,
FALSE);
WmipAssert(SizeUsed <= SizeLeft);
WmipAssert(SizeUsed == LoTotalSizeNeeded);
SizeLeft -= SizeUsed;
}
//
// We need to set the linkage field for the last wnode in
// the list to 0 so it can mark the end of the list
// correctly
//
if (LastWnode != NULL)
{
LastWnode->Linkage = 0;
}
//
// Compute the number of bytes used to fill the output
// buffer by subtracting the size left from the size passed
// in
//
*OutBufferSize = MaxBufferSize - SizeLeft;
} else {
//
// Not enough room to return all of the event data so we return
// a WNODE_TOO_SMALL to indicate the size needed
//
WnodeTooSmall = (PWNODE_TOO_SMALL)ReceiveNotification;
WnodeTooSmall->WnodeHeader.BufferSize = sizeof(WNODE_TOO_SMALL);
WnodeTooSmall->WnodeHeader.Flags = WNODE_FLAG_TOO_SMALL;
WnodeTooSmall->SizeNeeded = SizeNeeded;
*OutBufferSize = sizeof(WNODE_TOO_SMALL);
}
} else {
//
// There are no events waiting to be returned so we need to
// create our wait structures, pend the irp and return pending
//
if (ReceiveNotification->Action == RECEIVE_ACTION_NONE)
{
IrpListHead = IRP_OBJECT_LIST_HEAD(Irp);
InitializeListHead(IrpListHead);
for (i = 0; i < ObjectCount; i++)
{
GuidObject = ObjectArray[i].GuidObject;
GuidObject->Irp = Irp;
GuidObject->EventQueueAction = RECEIVE_ACTION_NONE;
InsertTailList(IrpListHead, &GuidObject->IrpObjectList);
}
IoSetCancelRoutine(Irp, WmipNotificationIrpCancel);
if (Irp->Cancel && IoSetCancelRoutine(Irp, NULL))
{
Status = STATUS_CANCELLED;
} else {
IoMarkIrpPending(Irp);
Status = STATUS_PENDING;
}
} else if (ReceiveNotification->Action == RECEIVE_ACTION_CREATE_THREAD) {
//
// Pump has called us to tell us that it is shutting down so we
// need to establish a list linking the guid objects and
// stashing away the callback address
//
#if defined(_WIN64)
//
// For native Win64 processes, ensure that the thread start
// address is aligned properly
//
Wow64Process = _PsGetCurrentProcess()->Wow64Process;
if ((Wow64Process == NULL) &&
(((ULONG_PTR)ReceiveNotification->UserModeCallback.Handle64 & 0x7) != 0))
{
Status = STATUS_INVALID_PARAMETER;
goto Cleanup;
}
#endif
//
// Make sure that the process handle we get is valid and has
// enough permissions to create the thread
//
Status = ObReferenceObjectByHandle(ReceiveNotification->UserModeProcess.Handle,
PROCESS_CREATE_THREAD |
PROCESS_QUERY_INFORMATION |
PROCESS_VM_OPERATION |
PROCESS_VM_WRITE |
PROCESS_VM_READ ,
NULL,
UserMode,
&UserProcessObject,
NULL);
if (NT_SUCCESS(Status))
{
//
// Create a handle for the process that lives in the system
// handle table so that it will be available in any thread
// context. Note that one handle is created for each thread
// object list and the handle is closed when the last
// object is removed from the list
//
Status = ObOpenObjectByPointer(UserProcessObject,
OBJ_KERNEL_HANDLE,
NULL,
THREAD_ALL_ACCESS,
NULL,
KernelMode,
&UserModeProcess);
if (NT_SUCCESS(Status))
{
//
// Get the default stack size and commit for this
// process and store it away in the guid object so
// that the pump threads created from kernel will
// have appropriatly sized stacks
//
try {
NtHeaders = RtlImageNtHeader(_PsGetCurrentProcess()->SectionBaseAddress);
if (NtHeaders != NULL)
{
#if defined(_WIN64)
if (Wow64Process != NULL) {
NtHeaders32 = (PIMAGE_NT_HEADERS32) NtHeaders;
StackSize = NtHeaders32->OptionalHeader.SizeOfStackReserve;
StackCommit = NtHeaders32->OptionalHeader.SizeOfStackCommit;
} else {
#endif
StackSize = NtHeaders->OptionalHeader.SizeOfStackReserve;
StackCommit = NtHeaders->OptionalHeader.SizeOfStackCommit;
#if defined(_WIN64)
}
#endif
} else {
StackSize = 0;
StackCommit = 0;
}
} except (EXCEPTION_EXECUTE_HANDLER) {
StackSize = 0;
StackCommit = 0;
}
GuidObject = ObjectArray[0].GuidObject;
GuidObject->UserModeCallback = (PUSER_THREAD_START_ROUTINE)(ULONG_PTR)ReceiveNotification->UserModeCallback.Handle;
GuidObject->EventQueueAction = RECEIVE_ACTION_CREATE_THREAD;
GuidObject->UserModeProcess = UserModeProcess;
GuidObject->StackSize = StackSize;
GuidObject->StackCommit = StackCommit;
ThreadListHead = &GuidObject->ThreadObjectList;
InitializeListHead(ThreadListHead);
for (i = 1; i < ObjectCount; i++)
{
GuidObject = ObjectArray[i].GuidObject;
GuidObject->UserModeCallback = (PUSER_THREAD_START_ROUTINE)(ULONG_PTR)ReceiveNotification->UserModeCallback.Handle;
GuidObject->EventQueueAction = RECEIVE_ACTION_CREATE_THREAD;
GuidObject->UserModeProcess = UserModeProcess;
GuidObject->StackSize = StackSize;
GuidObject->StackCommit = StackCommit;
InsertTailList(ThreadListHead, &GuidObject->ThreadObjectList);
}
}
ObDereferenceObject(UserProcessObject);
}
*OutBufferSize = 0;
}
}
Cleanup:
//
// Remove any object references that we took and free memory for
// the object array
//
WmipLeaveSMCritSection();
for (i = 0; i < ObjectCount; i++)
{
ObDereferenceObject(ObjectArray[i].GuidObject);
}
if (ObjectArray != StaticObjects)
{
WmipFree(ObjectArray);
}
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_EVENT_INFO_LEVEL,
"WMI: RCV Notification call -> 0x%x\n", Status));
return(Status);
}
NTSTATUS
WmipCsrClientMessageServer(
IN PVOID CsrPort,
IN OUT PCSR_API_MSG m,
IN CSR_API_NUMBER ApiNumber,
IN ULONG ArgLength
)
/*++
Routine Description:
This function sends an API datagram to the Windows Emulation Subsystem
Server.
Arguments:
CsrPort - pointer to LPC port object that is connected to CSR on
behalf of this process
m - Pointer to the API request message to send.
ApiNumber - Small integer that is the number of the API being called.
ArgLength - Length, in bytes, of the argument portion located at the
end of the request message. Used to calculate the length of the
request message.
Return Value:
Status Code from either client or server
--*/
{
NTSTATUS Status;
//
// Initialize the header of the message.
//
if ((LONG)ArgLength < 0)
{
ArgLength = (ULONG)(-(LONG)ArgLength);
m->h.u2.s2.Type = 0;
} else {
m->h.u2.ZeroInit = 0;
}
ArgLength |= (ArgLength << 16);
ArgLength += ((sizeof( CSR_API_MSG ) - sizeof( m->u )) << 16) |
(FIELD_OFFSET( CSR_API_MSG, u ) - sizeof( m->h ));
m->h.u1.Length = ArgLength;
m->CaptureBuffer = NULL;
m->ApiNumber = ApiNumber;
Status = LpcRequestPort( CsrPort,
(PPORT_MESSAGE)m);
//
// Check for failed status and do something.
//
if (! NT_SUCCESS( Status ))
{
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_ERROR_LEVEL,
"WMI: %p.%p LpcRequestPort failed %x\n",
NtCurrentTeb()->ClientId.UniqueProcess,
NtCurrentTeb()->ClientId.UniqueThread,
Status));
WmipAssert(FALSE);
m->ReturnValue = Status;
}
//
// The value of this function is whatever the server function returned.
//
return( m->ReturnValue );
}
VOID WmipPumpThreadApc(
IN PKAPC Apc,
IN PKNORMAL_ROUTINE *NormalRoutine,
IN PVOID *NormalContext,
IN PVOID *SystemArgument1,
IN PVOID *SystemArgument2
)
/*++
Routine Description:
Kernel mode APC that will register the current thread with CSR
Arguments:
Return Value:
--*/
{
BASE_API_MSG m;
PBASE_CREATETHREAD_MSG a = &m.u.CreateThread;
PEPROCESS Process;
UNREFERENCED_PARAMETER (NormalRoutine);
UNREFERENCED_PARAMETER (NormalContext);
UNREFERENCED_PARAMETER (SystemArgument1);
UNREFERENCED_PARAMETER (SystemArgument2);
//
// Free memory used by APC
//
ExFreePool(Apc);
//
// Get the ExceptionPort from the process object. In a Win32
// process this port is set by CSR to allow it to be notified when
// an exception occurs. This code will also use it to register this
// thread with CSR. Note that if the exception port is NULL then
// the process is not a Win32 process and it doesn't matter if the
// thread doesn't get registered.
//
Process = PsGetCurrentProcess();
if (Process->ExceptionPort != NULL)
{
a->ThreadHandle = NULL;
a->ClientId = NtCurrentTeb()->ClientId;
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,
"WMI: Sending message To CSR for %p.%p\n",
NtCurrentTeb()->ClientId.UniqueProcess,
NtCurrentTeb()->ClientId.UniqueThread));
WmipCsrClientMessageServer( Process->ExceptionPort,
(PCSR_API_MSG)&m,
CSR_MAKE_API_NUMBER( BASESRV_SERVERDLL_INDEX,
BasepRegisterThread
),
sizeof( *a )
);
} else {
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_ERROR_LEVEL,
"WMI: %p.%p Process %p has no exception port\n",
NtCurrentTeb()->ClientId.UniqueProcess,
NtCurrentTeb()->ClientId.UniqueThread,
Process));
WmipAssert(FALSE);
}
}
NTSTATUS WmipCreatePumpThread(
PWMIGUIDOBJECT Object
)
{
NTSTATUS Status = STATUS_SUCCESS;
HANDLE ThreadHandle;
PKAPC Apc;
PKTHREAD ThreadObj;
PAGED_CODE();
//
// First off we need to create the pump thread suspended so we'll
// have a chance to queue a kernel apc before the thread starts
// running
//
WmipEnterSMCritSection();
if (Object->UserModeProcess != NULL)
{
Status = RtlCreateUserThread(Object->UserModeProcess,
NULL,
TRUE,
0,
Object->StackSize,
Object->StackCommit,
Object->UserModeCallback,
(PVOID)0x1f1f1f1f,
&ThreadHandle,
NULL);
if (NT_SUCCESS(Status))
{
//
// Queue a kernel mode apc that will call into CSR to register
// this newly created thread. Note that if the APC cannot be
// run it is not fatal as we can allow the thread to run
// without being registered with CSR. The APC is freed at the
// end of the APC routine
//
Status = ObReferenceObjectByHandle(ThreadHandle,
0,
NULL,
KernelMode,
&ThreadObj,
NULL);
if (NT_SUCCESS(Status))
{
Apc = WmipAllocNP(sizeof(KAPC));
if (Apc != NULL)
{
KeInitializeApc(Apc,
ThreadObj,
OriginalApcEnvironment,
WmipPumpThreadApc,
NULL,
NULL,
KernelMode,
NULL);
if (! KeInsertQueueApc(Apc,
NULL,
NULL,
0))
{
ExFreePool(Apc);
WmipAssert(FALSE);
}
}
ObDereferenceObject(ThreadObj);
} else {
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_ERROR_LEVEL,
"WMI: ObRef(ThreadObj) failed %x\n",
Status));
WmipAssert(FALSE);
//
// Status is still successful since the pump thread was
// created, just not registered with CSR
//
Status = STATUS_SUCCESS;
}
//
// If we successfully created the pump thread then mark all of
// the related objects as not needing any thread creation
// anymore
//
WmipClearThreadObjectList(Object);
WmipLeaveSMCritSection();
ZwResumeThread(ThreadHandle,
NULL);
ZwClose(ThreadHandle);
} else {
WmipLeaveSMCritSection();
}
} else {
WmipLeaveSMCritSection();
}
return(Status);
}
void WmipCreatePumpThreadRoutine(
PVOID Context
)
/*+++
Routine Description:
This routine is a worker routine that will create a user mode pump
thread so that events can be delivered.
Arguments:
Context is a pointer to a CREATETHREADWORKITEM struct. It is freed
in this routine
Return Value:
---*/
{
PCREATETHREADWORKITEM WorkItem = (PCREATETHREADWORKITEM)Context;
NTSTATUS Status;
PAGED_CODE();
if (ObReferenceObjectSafe(WorkItem->Object))
{
//
// Only continue if the object is not being deleted
//
Status = WmipCreatePumpThread(WorkItem->Object);
if (! NT_SUCCESS(Status))
{
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_ERROR_LEVEL,
"WMI: Delayed pump thread creation failed %x\n",
Status));
}
ObDereferenceObject(WorkItem->Object);
}
//
// Release reference to object taken when work item was queued
//
ObDereferenceObject(WorkItem->Object);
ExFreePool(WorkItem);
}
#define WmipQueueEventToObject(Object, Wnode, IsHighPriority) \
WmipQueueNotification(Object, IsHighPriority ? &Object->HiPriority : \
&Object->LoPriority, \
Wnode);
NTSTATUS WmipQueueNotification(
PWMIGUIDOBJECT Object,
PWMIEVENTQUEUE EventQueue,
PWNODE_HEADER Wnode
)
{
//
// This routine assumes that the SMCritSection is held
//
PUCHAR Buffer;
ULONG InWnodeSize;
ULONG NextOffset;
PUCHAR DestPtr;
PWNODE_HEADER LastWnode;
NTSTATUS Status;
ULONG SizeNeeded;
PCREATETHREADWORKITEM WorkItem;
PAGED_CODE();
//
// If there is not a buffer allocated to store the event then
// allocate one
//
if (EventQueue->Buffer == NULL)
{
//
// If we get an event that is larger than the default max
// buffer size then we bump the buffer size up to 64K, unless
// it is larger than 64K where we bump up to the actual size of
// the event.
//
SizeNeeded = (Wnode->BufferSize + 7) & ~7;
if (SizeNeeded > EventQueue->MaxBufferSize) {
EventQueue->MaxBufferSize = (SizeNeeded >= 65536) ? SizeNeeded : 65536;
}
Buffer = WmipAlloc(EventQueue->MaxBufferSize);
if (Buffer != NULL)
{
EventQueue->Buffer = Buffer;
EventQueue->NextOffset = 0;
EventQueue->LastWnode = NULL;
} else {
EventQueue->EventsLost++;
WmipDebugPrintEx((DPFLTR_WMICORE_ID,
DPFLTR_EVENT_INFO_LEVEL,
"WMI: Event 0x%x lost for object %p since could not alloc\n",
EventQueue->EventsLost, Object));
return(STATUS_INSUFFICIENT_RESOURCES);
}
} else {
Buffer = EventQueue->Buffer;
}
//
// See if there is room to queue the WNODE
//
InWnodeSize = Wnode->BufferSize;
NextOffset = ((EventQueue->NextOffset + InWnodeSize) + 7) &~7;
if (NextOffset <= EventQueue->MaxBufferSize)
{
//
// Link the previous wnode to this one, copy in the new wnode
// and update the pointer to next free space
//
DestPtr = Buffer + EventQueue->NextOffset;
LastWnode = EventQueue->LastWnode;
if (LastWnode != NULL)
{
LastWnode->Linkage = (ULONG) ((PCHAR)DestPtr - (PCHAR)LastWnode);
}
EventQueue->LastWnode = (PWNODE_HEADER)DestPtr;
EventQueue->NextOffset = NextOffset;
memcpy(DestPtr, Wnode, InWnodeSize);
//
// Guid object gets signalled when event is placed into queue
//
KeSetEvent(&Object->Event, 0, FALSE);
//
// If consumer requested that we autostart a thread then we do
// that now
//
if (Object->EventQueueAction == RECEIVE_ACTION_CREATE_THREAD)
{
if (KeIsAttachedProcess())
{
//
// If the current thread is attached to a process then
// it is not safe to create a thread. So we queue a
// work item and let the work item create it
//
WorkItem = ExAllocatePoolWithTag(NonPagedPool,
sizeof(CREATETHREADWORKITEM),
WMIPCREATETHREADTAG);
if (WorkItem != NULL)
{
//
// Take reference on object. Reference released in
// worker routine
//
Status = ObReferenceObjectByPointer(Object,
0,
NULL,
KernelMode);
if (NT_SUCCESS(Status))
{
WorkItem->Object = Object;
ExInitializeWorkItem(&WorkItem->WorkItem,
WmipCreatePumpThreadRoutine,
WorkItem);
ExQueueWorkItem(&WorkItem->WorkItem,
DelayedWorkQueue);
} else {
WmipDebugPrintEx((DPFLTR_WMICORE_ID,
DPFLTR_ERROR_LEVEL,
"WMI: Ref on object %p failed %x for queuing notification work item\n",
Object,
Status));
ExFreePool(WorkItem);
}
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
} else {
Status = WmipCreatePumpThread(Object);
}
if (! NT_SUCCESS(Status))
{
EventQueue->EventsLost++;
WmipDebugPrintEx((DPFLTR_WMICORE_ID,
DPFLTR_EVENT_INFO_LEVEL,
"WMI: Event 0x%x lost for object %p since Thread create Failed\n",
EventQueue->EventsLost, Object));
}
} else {
Status = STATUS_SUCCESS;
}
} else {
//
// Not enough space, throw away the event
//
EventQueue->EventsLost++;
WmipDebugPrintEx((DPFLTR_WMICORE_ID,
DPFLTR_EVENT_INFO_LEVEL,
"WMI: Event 0x%x lost for object %p since too large 0x%x\n",
EventQueue->EventsLost, Object, Wnode->BufferSize));
Status = STATUS_BUFFER_TOO_SMALL;
}
return(Status);
}
PWNODE_HEADER WmipDereferenceEvent(
PWNODE_HEADER Wnode
)
{
ULONG WnodeTargetSize;
ULONG IsStaticInstanceNames;
ULONG InstanceNameLen, InstanceNameLen2;
PWNODE_SINGLE_INSTANCE WnodeTarget;
PWCHAR Ptr;
PWNODE_EVENT_REFERENCE WnodeRef = (PWNODE_EVENT_REFERENCE)Wnode;
PBDATASOURCE DataSource;
NTSTATUS Status;
ULONG Retries;
PAGED_CODE();
//
// Determine if the data source is valid or not
//
DataSource = WmipFindDSByProviderId(WnodeRef->WnodeHeader.ProviderId);
if (DataSource == NULL)
{
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_EVENT_INFO_LEVEL,
"WMI: Invalid Data Source in referenced guid \n"));
return(NULL);
}
//
// Compute the size of any dynamic name that must go into the TargetWnode
//
IsStaticInstanceNames = WnodeRef->WnodeHeader.Flags &
WNODE_FLAG_STATIC_INSTANCE_NAMES;
if (IsStaticInstanceNames == 0)
{
InstanceNameLen = *WnodeRef->TargetInstanceName + sizeof(USHORT);
} else {
InstanceNameLen = 0;
}
WnodeTargetSize = WnodeRef->TargetDataBlockSize +
FIELD_OFFSET(WNODE_SINGLE_INSTANCE,
VariableData) +
InstanceNameLen +
8;
Retries = 0;
do
{
WnodeTarget = WmipAllocNP(WnodeTargetSize);
if (WnodeTarget != NULL)
{
//
// Build WNODE_SINGLE_INSTANCE that we use to query for event data
//
memset(WnodeTarget, 0, WnodeTargetSize);
WnodeTarget->WnodeHeader.BufferSize = WnodeTargetSize;
WnodeTarget->WnodeHeader.ProviderId = WnodeRef->WnodeHeader.ProviderId;
memcpy(&WnodeTarget->WnodeHeader.Guid,
&WnodeRef->TargetGuid,
sizeof(GUID));
WnodeTarget->WnodeHeader.Version = WnodeRef->WnodeHeader.Version;
WnodeTarget->WnodeHeader.Flags = WNODE_FLAG_SINGLE_INSTANCE |
IsStaticInstanceNames;
if (IsStaticInstanceNames != 0)
{
WnodeTarget->InstanceIndex = WnodeRef->TargetInstanceIndex;
WnodeTarget->DataBlockOffset = FIELD_OFFSET(WNODE_SINGLE_INSTANCE,
VariableData);
} else {
WnodeTarget->OffsetInstanceName = FIELD_OFFSET(WNODE_SINGLE_INSTANCE,
VariableData);
Ptr = (PWCHAR)OffsetToPtr(WnodeTarget, WnodeTarget->OffsetInstanceName);
InstanceNameLen2 = InstanceNameLen - sizeof(USHORT);
*Ptr++ = (USHORT)InstanceNameLen2;
memcpy(Ptr,
&WnodeRef->TargetInstanceName[1],
InstanceNameLen2);
//
// Round data block offset to 8 byte alignment
//
WnodeTarget->DataBlockOffset = ((FIELD_OFFSET(WNODE_SINGLE_INSTANCE,
VariableData) +
InstanceNameLen2 +
sizeof(USHORT)+7) & 0xfffffff8);
}
Status = WmipDeliverWnodeToDS(IRP_MN_QUERY_SINGLE_INSTANCE,
DataSource,
(PWNODE_HEADER)WnodeTarget,
WnodeTargetSize);
if (NT_SUCCESS(Status) &&
(WnodeTarget->WnodeHeader.Flags & WNODE_FLAG_TOO_SMALL))
{
WnodeTargetSize = ((PWNODE_TOO_SMALL)WnodeTarget)->SizeNeeded;
WmipFree(WnodeTarget);
Retries++;
Status = STATUS_BUFFER_TOO_SMALL;
}
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
} while ((Status == STATUS_BUFFER_TOO_SMALL) && (Retries < 2));
WmipUnreferenceDS(DataSource);
if (! NT_SUCCESS(Status))
{
WmipReportEventLog(EVENT_WMI_CANT_GET_EVENT_DATA,
EVENTLOG_WARNING_TYPE,
0,
Wnode->BufferSize,
Wnode,
0,
NULL);
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_ERROR_LEVEL,
"WMI: Query to dereference WNODE failed %d\n",
Status));
if (WnodeTarget != NULL)
{
WmipFree(WnodeTarget);
WnodeTarget = NULL;
}
} else {
WnodeTarget->WnodeHeader.Flags |= (WnodeRef->WnodeHeader.Flags &
WNODE_FLAG_SEVERITY_MASK) |
WNODE_FLAG_EVENT_ITEM;
}
return((PWNODE_HEADER)WnodeTarget);
}
PWNODE_HEADER WmipIncludeStaticNames(
PWNODE_HEADER Wnode
)
{
PWNODE_HEADER ReturnWnode = Wnode;
PWNODE_HEADER WnodeFull;
PWNODE_ALL_DATA WnodeAllData;
PWNODE_SINGLE_INSTANCE WnodeSI;
PWCHAR InstanceName = NULL;
SIZE_T InstanceNameLen = 0;
ULONG InstanceIndex;
LPGUID EventGuid = &Wnode->Guid;
SIZE_T WnodeFullSize;
PWCHAR TargetInstanceName;
WCHAR Index[MAXBASENAMESUFFIXSIZE+1];
ULONG TargetProviderId;
BOOLEAN IsError;
PBINSTANCESET TargetInstanceSet;
PBGUIDENTRY GuidEntry;
PLIST_ENTRY InstanceSetList;
PBINSTANCESET InstanceSet;
PAGED_CODE();
IsError = TRUE;
TargetInstanceSet = NULL;
GuidEntry = WmipFindGEByGuid(EventGuid, FALSE);
if (GuidEntry != NULL)
{
//
// Loop over all instance sets to find the one that corresponds
// to our provider id
//
TargetProviderId = Wnode->ProviderId;
WmipEnterSMCritSection();
InstanceSetList = GuidEntry->ISHead.Flink;
while (InstanceSetList != &GuidEntry->ISHead)
{
InstanceSet = CONTAINING_RECORD(InstanceSetList,
INSTANCESET,
GuidISList);
if (InstanceSet->ProviderId == TargetProviderId)
{
//
// We found the instance set corrsponding to the provider id
//
TargetInstanceSet = InstanceSet;
WmipReferenceIS(TargetInstanceSet);
break;
}
InstanceSetList = InstanceSetList->Flink;
}
WmipLeaveSMCritSection();
//
// Remove ref on the guid entry as we have refed the TargetInstanceSet
//
WmipUnreferenceGE(GuidEntry);
}
if (TargetInstanceSet != NULL)
{
if ((TargetInstanceSet->Flags &
(IS_INSTANCE_BASENAME | IS_INSTANCE_STATICNAMES)) != 0)
{
if (Wnode->Flags & WNODE_FLAG_ALL_DATA)
{
//
// Fill instance names in WNODE_ALL_DATA. Allocate a
// new buffer to hold all of the original wnode plus
// the instance names. We need to add space for padding
// the wnode to 4 bytes plus space for the array of
// offsets to instance names plus space for the instanc
// names
//
WnodeFullSize = ((Wnode->BufferSize+3) & ~3) +
(TargetInstanceSet->Count * sizeof(ULONG)) +
WmipStaticInstanceNameSize(TargetInstanceSet);
WnodeFull = WmipAlloc(WnodeFullSize);
if (WnodeFull != NULL)
{
memcpy(WnodeFull, Wnode, Wnode->BufferSize);
WnodeAllData = (PWNODE_ALL_DATA)WnodeFull;
WmipInsertStaticNames(WnodeAllData,
(ULONG)WnodeFullSize,
TargetInstanceSet);
ReturnWnode = WnodeFull;
IsError = FALSE;
}
} else if ((Wnode->Flags & WNODE_FLAG_SINGLE_INSTANCE) ||
(Wnode->Flags & WNODE_FLAG_SINGLE_ITEM)) {
//
// Fill instance names in WNODE_SINGLE_INSTANCE or
// _ITEM.
//
WnodeFull = Wnode;
WnodeSI = (PWNODE_SINGLE_INSTANCE)Wnode;
InstanceIndex = WnodeSI->InstanceIndex;
if (InstanceIndex < TargetInstanceSet->Count)
{
if (TargetInstanceSet->Flags & IS_INSTANCE_STATICNAMES)
{
InstanceName = TargetInstanceSet->IsStaticNames->StaticNamePtr[InstanceIndex];
InstanceNameLen = (wcslen(InstanceName) + 2) *
sizeof(WCHAR);
} else if (TargetInstanceSet->Flags & IS_INSTANCE_BASENAME) {
InstanceName = TargetInstanceSet->IsBaseName->BaseName;
InstanceNameLen = (wcslen(InstanceName) + 2 +
MAXBASENAMESUFFIXSIZE) * sizeof(WCHAR);
}
//
// Allocate a new Wnode and fill in the instance
// name. Include space for padding the wnode to a 2
// byte boundry and space for the instance name
//
WnodeFullSize = ((Wnode->BufferSize+1) & ~1) +
InstanceNameLen;
WnodeFull = WmipAlloc(WnodeFullSize);
if (WnodeFull != NULL)
{
memcpy(WnodeFull, Wnode, Wnode->BufferSize);
WnodeFull->BufferSize = (ULONG)WnodeFullSize;
WnodeSI = (PWNODE_SINGLE_INSTANCE)WnodeFull;
WnodeSI->OffsetInstanceName = (Wnode->BufferSize+1)& ~1;
TargetInstanceName = (PWCHAR)((PUCHAR)WnodeSI + WnodeSI->OffsetInstanceName);
if (TargetInstanceSet->Flags & IS_INSTANCE_STATICNAMES)
{
InstanceNameLen -= sizeof(WCHAR);
*TargetInstanceName++ = (USHORT)InstanceNameLen;
StringCbCopy(TargetInstanceName,
InstanceNameLen,
InstanceName);
} else {
if (TargetInstanceSet->Flags & IS_PDO_INSTANCENAME)
{
WnodeFull->Flags |= WNODE_FLAG_PDO_INSTANCE_NAMES;
}
StringCbPrintf(Index,
sizeof(Index),
BASENAMEFORMATSTRING,
TargetInstanceSet->IsBaseName->BaseIndex +
InstanceIndex);
StringCbCopy(TargetInstanceName+1,
InstanceNameLen,
InstanceName);
StringCbCat(TargetInstanceName+1,
InstanceNameLen,
Index);
InstanceNameLen = wcslen(TargetInstanceName+1);
*TargetInstanceName = ((USHORT)InstanceNameLen+1) * sizeof(WCHAR);
}
IsError = FALSE;
ReturnWnode = WnodeFull;
}
}
}
}
}
if (IsError)
{
//
// If we had an error resolving the instance name then report it
// and remove the instance name from the event.
//
WmipReportEventLog(EVENT_WMI_CANT_RESOLVE_INSTANCE,
EVENTLOG_WARNING_TYPE,
0,
Wnode->BufferSize,
Wnode,
0,
NULL);
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_WARNING_LEVEL,
"WMI: Static instance name in event, but error processing\n"));
if (Wnode->Flags & WNODE_FLAG_ALL_DATA)
{
WnodeAllData = (PWNODE_ALL_DATA)Wnode;
WnodeAllData->OffsetInstanceNameOffsets = 0;
} else if ((Wnode->Flags & WNODE_FLAG_SINGLE_INSTANCE) ||
(Wnode->Flags & WNODE_FLAG_SINGLE_ITEM))
{
WnodeSI = (PWNODE_SINGLE_INSTANCE)Wnode;
WnodeSI->OffsetInstanceName = 0;
}
}
if (TargetInstanceSet != NULL)
{
WmipUnreferenceIS(TargetInstanceSet);
}
return(ReturnWnode);
}
NTSTATUS WmipWriteWnodeToObject(
PWMIGUIDOBJECT Object,
PWNODE_HEADER Wnode,
BOOLEAN IsHighPriority
)
/*+++
Routine Description:
This routine will write a WNODE into the queue of events to be returned
for a guid object. If there is an irp already waiting then it will be
satisfied with the event otherwise it will be queued in the objects
buffer.
This routine assumes that the SM Critical section is held
Arguments:
Object is the object to which to send the request
Wnode is the Wnode with the event
IsHighPriority is TRUE if the event should go into the high priority
queue
Return Value:
STATUS_SUCCESS or an error code
---*/
{
PIRP Irp;
ULONG WnodeSize;
PUCHAR OutBuffer;
ULONG OutBufferSize;
PIO_STACK_LOCATION IrpStack;
PWNODE_TOO_SMALL WnodeTooSmall;
NTSTATUS Status;
PAGED_CODE();
//
// Someone has registered to recieve this event so
// see if there is an irp waiting to be completed or
// if we should just queue it
//
Irp = Object->Irp;
if ((Irp != NULL) &&
(IoSetCancelRoutine(Irp, NULL)))
{
//
// There is an irp waiting for this event, copy out the
// event and complete the irp
//
IrpStack = IoGetCurrentIrpStackLocation(Irp);
OutBuffer = Irp->AssociatedIrp.SystemBuffer;
OutBufferSize = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;
WnodeSize = Wnode->BufferSize;
if (WnodeSize > OutBufferSize)
{
//
// There is not enough room to return the event so
// we return a WNODE_TOO_SMALL with the size needed
// and then go and queue the event
//
WmipAssert(OutBufferSize >= sizeof(WNODE_TOO_SMALL));
WnodeTooSmall = (PWNODE_TOO_SMALL)OutBuffer;
WnodeTooSmall->WnodeHeader.BufferSize = sizeof(WNODE_TOO_SMALL);
WnodeTooSmall->WnodeHeader.Flags = WNODE_FLAG_TOO_SMALL;
WnodeTooSmall->SizeNeeded = WnodeSize;
WnodeSize = sizeof(WNODE_TOO_SMALL);
Status = WmipQueueEventToObject(Object,
Wnode,
IsHighPriority);
} else {
//
// Plenty of room, copy the event into the irp
// buffer and complete the irp
//
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_EVENT_INFO_LEVEL,
"WMI: Returning event to waiting irp for object %p\n", Object));
RtlCopyMemory(OutBuffer, Wnode, WnodeSize);
Status = STATUS_SUCCESS;
}
//
// Remove link from all objects associated with the irp
// since now the irp is going away.
//
WmipClearIrpObjectList(Irp);
Irp->IoStatus.Information = WnodeSize;
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
} else {
//
// There is no irp waiting to receive the event so we
// need to queue it if we can
//
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_EVENT_INFO_LEVEL,
"WMI: Queued event to object %p\n", Object));
Status = WmipQueueEventToObject(Object,
Wnode,
IsHighPriority);
}
return(Status);
}
NTSTATUS WmipProcessEvent(
PWNODE_HEADER InWnode,
BOOLEAN IsHighPriority,
BOOLEAN FreeBuffer
)
{
LPGUID Guid;
NTSTATUS Status, ReturnStatus;
PBGUIDENTRY GuidEntry;
PLIST_ENTRY ObjectList, ObjectListNext;
PWMIGUIDOBJECT Object;
LPGUID EventGuid = &InWnode->Guid;
PWNODE_HEADER Wnode, WnodeTarget;
PAGED_CODE();
//
// If the event references a guid that needs to be queried then
// go do the dereferencing here.
//
if (InWnode->Flags & WNODE_FLAG_EVENT_REFERENCE)
{
WnodeTarget = WmipDereferenceEvent(InWnode);
if (WnodeTarget == NULL)
{
// TODO: Eventlog
if (FreeBuffer)
{
ExFreePool(InWnode);
}
return(STATUS_UNSUCCESSFUL);
}
Wnode = WnodeTarget;
} else {
Wnode = InWnode;
WnodeTarget = NULL;
}
//
// Be sure to use the guid of the referenced event, not the event that
// was originally fired.
EventGuid = &Wnode->Guid;
//
// If it is Trace error notification, disable providers
//
#ifndef MEMPHIS
if (IsEqualGUID(EventGuid, & TraceErrorGuid)) {
PWMI_TRACE_EVENT WmiEvent = (PWMI_TRACE_EVENT) InWnode;
ULONG LoggerId = WmiGetLoggerId(InWnode->HistoricalContext);
if ( InWnode->BufferSize >= sizeof(WMI_TRACE_EVENT) ) {
//
// Logger thread terminating will result in DisableTrace
// through StopTrace. No need to call twice.
//
if (WmiEvent->TraceErrorFlag == STATUS_SEVERITY_ERROR) {
WmipDisableTraceProviders(LoggerId);
}
}
}
#endif
//
// See if this event has a static name and if so fill it in
if (Wnode->Flags & WNODE_FLAG_STATIC_INSTANCE_NAMES)
{
Wnode = WmipIncludeStaticNames(Wnode);
}
//
// See if any data provider has registered this event
//
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_EVENT_INFO_LEVEL,
"WMI: Received event\n"));
Guid = &Wnode->Guid;
GuidEntry = WmipFindGEByGuid(Guid, TRUE);
if (GuidEntry != NULL)
{
//
// Yup, so check if there are any open objects to the guid and
// if anyone is interested in receiving events from them
//
ReturnStatus = STATUS_SUCCESS;
WmipEnterSMCritSection();
ObjectList = GuidEntry->ObjectHead.Flink;
while (ObjectList != &GuidEntry->ObjectHead)
{
Object = CONTAINING_RECORD(ObjectList,
WMIGUIDOBJECT,
GEObjectList);
//
// ObRefSafe so that we can be sure that the object is not
// in the process of being deleted. If this function
// returns FALSE then the object is being deleted and so we
// don't want to use it. If TRUE then it is safe to use the
// object
//
ObjectListNext = ObjectList->Flink;
if (ObReferenceObjectSafe(Object))
{
//
// Make sure the object has not been marked as one that
// should not receive any events since it is
// transitioning to a closed state
//
if ((Object->Flags & WMIGUID_FLAG_RECEIVE_NO_EVENTS) == 0)
{
if (Object->Flags & WMIGUID_FLAG_KERNEL_NOTIFICATION)
{
//
// KM clients get a direct callback
//
WMI_NOTIFICATION_CALLBACK Callback;
PVOID Context;
Callback = Object->Callback;
Context = Object->CallbackContext;
if (Callback != NULL)
{
(*Callback)(Wnode, Context);
}
} else {
//
// UM clients get event written into IRP or queued up
//
Status = WmipWriteWnodeToObject(Object,
Wnode,
IsHighPriority);
if (! NT_SUCCESS(Status))
{
//
// If any attempts to queue the event fail then we return
// an error
//
ReturnStatus = STATUS_UNSUCCESSFUL;
}
}
}
ObDereferenceObject(Object);
//
// Note that we cannot touch the object anymore
//
}
ObjectList = ObjectListNext;
}
WmipLeaveSMCritSection();
WmipUnreferenceGE(GuidEntry);
} else {
ReturnStatus = STATUS_WMI_GUID_NOT_FOUND;
}
if (FreeBuffer)
{
//
// Free buffer passed by driver containing event
//
ExFreePool(InWnode);
}
if ((Wnode != InWnode) && (Wnode != WnodeTarget))
{
//
// If we inserted static names then free it
//
WmipFree(Wnode);
}
if (WnodeTarget != NULL)
{
//
// if we dereferenced then free it
//
WmipFree(WnodeTarget);
}
return(ReturnStatus);
}
NTSTATUS WmipUMProviderCallback(
IN WMIACTIONCODE ActionCode,
IN PVOID DataPath,
IN ULONG BufferSize,
IN OUT PVOID Buffer
)
{
PAGED_CODE();
UNREFERENCED_PARAMETER (ActionCode);
UNREFERENCED_PARAMETER (DataPath);
UNREFERENCED_PARAMETER (BufferSize);
UNREFERENCED_PARAMETER (Buffer);
ASSERT(FALSE);
return(STATUS_UNSUCCESSFUL);
}
NTSTATUS WmipRegisterUMGuids(
IN POBJECT_ATTRIBUTES ObjectAttributes,
IN ULONG Cookie,
IN PWMIREGINFO RegInfo,
IN ULONG RegInfoSize,
OUT HANDLE *RequestHandle,
OUT ULONG64 *LoggerContext
)
/*+++
Routine Description:
This routine will register a set of user mode guids with WMI for use
by tracelog. The following steps will occur:
* A request object is created using the passed object attributes.
Although the object created is unnamed, the object name passed
is used to lookup a security descriptor to associate with the
object.
* The guids are registered in the system.
Arguments:
ObjectAttribtes is a pointer to the passed object attributes used to
create the request object
Cookie is a unique id to associate with the request object so that
when a request is delivered the UM code can understand the context
via the cookie.
RegInfo is the registration information passed
RegInfoSize is the number of bytes of registration information passed
*RequestHandle returns with a handle to the request object. UM logger
creation and tracelog enabled/disable requests are delivered to
the object as WMI events.
*LoggerContext returns with the logger context
Return Value:
STATUS_SUCCESS or an error code
---*/
{
NTSTATUS Status;
PDEVICE_OBJECT Callback;
PWMIGUIDOBJECT RequestObject;
PREGENTRY RegEntry;
PBGUIDENTRY GuidEntry;
PWMIREGGUID RegGuid;
PBDATASOURCE DataSource;
PBINSTANCESET InstanceSet;
OBJECT_ATTRIBUTES CapturedObjectAttributes;
UNICODE_STRING CapturedGuidString;
WCHAR CapturedGuidBuffer[WmiGuidObjectNameLength + 1];
PAGED_CODE();
Status = WmipProbeAndCaptureGuidObjectAttributes(&CapturedObjectAttributes,
&CapturedGuidString,
CapturedGuidBuffer,
ObjectAttributes);
if (NT_SUCCESS(Status))
{
Callback = (PDEVICE_OBJECT)(ULONG_PTR) WmipUMProviderCallback;
//
// Establish a regentry for the data provider
//
WmipEnterSMCritSection();
RegEntry = WmipAllocRegEntry(Callback,
WMIREG_FLAG_CALLBACK |
REGENTRY_FLAG_TRACED |
REGENTRY_FLAG_NEWREGINFO |
REGENTRY_FLAG_INUSE |
REGENTRY_FLAG_REG_IN_PROGRESS);
WmipLeaveSMCritSection();
if (RegEntry != NULL)
{
//
// Build a request object for this data source so that any
// enable requests can be posted to it while processing the
// WmiRegInfo
//
Status = WmipOpenGuidObject(&CapturedObjectAttributes,
TRACELOG_REGISTER_GUIDS,
UserMode,
RequestHandle,
&RequestObject);
if (NT_SUCCESS(Status))
{
Status = WmipProcessWmiRegInfo(RegEntry,
RegInfo,
RegInfoSize,
RequestObject,
FALSE,
TRUE);
if (NT_SUCCESS(Status))
{
//
// Initialize/Update InstanceSet
//
DataSource = RegEntry->DataSource;
RegGuid = &RegInfo->WmiRegGuid[0];
InstanceSet = WmipFindISByGuid( DataSource,
&RegGuid->Guid );
if (InstanceSet == NULL)
{
Status = STATUS_WMI_GUID_NOT_FOUND;
}
else {
WmipUnreferenceIS(InstanceSet);
}
//
// Find out if this Guid is currently Enabled. If so find
// its LoggerContext
//
*LoggerContext = 0;
GuidEntry = WmipFindGEByGuid(&RegInfo->WmiRegGuid->Guid,
FALSE);
if (GuidEntry != NULL)
{
if (GuidEntry->Flags & GE_NOTIFICATION_TRACE_FLAG)
{
*LoggerContext = GuidEntry->LoggerContext;
}
WmipUnreferenceGE(GuidEntry);
}
RequestObject->Flags |= WMIGUID_FLAG_REQUEST_OBJECT;
RequestObject->RegEntry = RegEntry;
RequestObject->Cookie = Cookie;
}
else
{
//
// If an error registering guids then clean up regentry
//
RegEntry->Flags |= (REGENTRY_FLAG_RUNDOWN |
REGENTRY_FLAG_NOT_ACCEPTING_IRPS);
WmipUnreferenceRegEntry(RegEntry);
ZwClose(*RequestHandle);
}
//
// remove the ref from when the object was created
//
ObDereferenceObject(RequestObject);
}
else {
RegEntry->Flags |= (REGENTRY_FLAG_RUNDOWN |
REGENTRY_FLAG_NOT_ACCEPTING_IRPS);
WmipUnreferenceRegEntry(RegEntry);
}
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
}
return(Status);
}
NTSTATUS WmipUnregisterGuids(
PWMIUNREGGUIDS UnregGuids
)
{
PBGUIDENTRY GuidEntry;
PAGED_CODE();
//
// Check to see if this GUID got disabled in the middle
// of Unregister Call. If so, send the LoggerContext back
//
GuidEntry = WmipFindGEByGuid(&UnregGuids->Guid, FALSE);
if (GuidEntry != NULL)
{
if ((GuidEntry->Flags & GE_NOTIFICATION_TRACE_FLAG) != 0)
{
UnregGuids->LoggerContext = GuidEntry->LoggerContext;
}
WmipUnreferenceGE(GuidEntry);
return (STATUS_SUCCESS);
}
else {
return (STATUS_WMI_INSTANCE_NOT_FOUND);
}
}
NTSTATUS WmipWriteMBToObject(
IN PWMIGUIDOBJECT RequestObject,
IN PWMIGUIDOBJECT ReplyObject,
IN PUCHAR Message,
IN ULONG MessageSize
)
/*+++
Routine Description:
This routine will build a WNODE out of a message and then write it
into the Request object. If a reply object is specified then the reply
object is linked into the request object so when the reply is written
to the request object it can be routed to the reply object correctly,.
This routine assumes that the SM Critical section is held
Arguments:
RequestObject is the object to which to send the request
ReplyObject is the object to which the request object shoudl reply.
This may be NULL in the case that no reply is needed.
Message is the message to be sent
MessageSize is the size of the message in bytes
Return Value:
STATUS_SUCCESS or an error code
---*/
{
PWNODE_HEADER Wnode;
ULONG WnodeSize;
PUCHAR Payload;
ULONG i;
PMBREQUESTS MBRequest;
NTSTATUS Status;
PAGED_CODE();
//
// Allocate space to build a wnode out of the data passed
//
WnodeSize = sizeof(WNODE_HEADER) + MessageSize;
Wnode = WmipAlloc(WnodeSize);
if (Wnode != NULL)
{
//
// Create an internal wnode with the message as the payload
//
RtlZeroMemory(Wnode, sizeof(WNODE_HEADER));
Wnode->BufferSize = WnodeSize;
Wnode->Flags = WNODE_FLAG_INTERNAL;
Wnode->Guid = RequestObject->Guid;
Wnode->ProviderId = WmiMBRequest;
Payload = (PUCHAR)Wnode + sizeof(WNODE_HEADER);
RtlCopyMemory(Payload, Message, MessageSize);
//
// if this request requires a reply then update the lists for the
// request and reply objects
//
if (ReplyObject != NULL)
{
//
// Find a free spot in the request object to link
// in the reply.
//
Status = STATUS_INSUFFICIENT_RESOURCES;
for (i = 0; i < MAXREQREPLYSLOTS; i++)
{
MBRequest = &RequestObject->MBRequests[i];
if (MBRequest->ReplyObject == NULL)
{
//
// We have a free slot so link request and reply
// objects together and send off the request.
// The request object takes a ref count on the reply
// object since it maintains a pointer to it. The
// refcount is released when the request object writes
// the reply back to the reply object.
//
Status = ObReferenceObjectByPointer(ReplyObject,
0,
WmipGuidObjectType,
KernelMode);
if (NT_SUCCESS(Status))
{
MBRequest->ReplyObject = ReplyObject;
InsertTailList(&ReplyObject->RequestListHead,
&MBRequest->RequestListEntry);
Wnode->Version = i;
Status = WmipWriteWnodeToObject(RequestObject,
Wnode,
TRUE);
if (! NT_SUCCESS(Status))
{
//
// If writing request failed, we need to cleanup
//
ObDereferenceObject(ReplyObject);
MBRequest->ReplyObject = NULL;
RemoveEntryList(&MBRequest->RequestListEntry);
}
}
break;
}
}
} else {
//
// No reply required so we just write the message to the
// object and continue with our business
//
Status = WmipWriteWnodeToObject(RequestObject,
Wnode,
TRUE);
}
WmipFree(Wnode);
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
return(Status);
}
NTSTATUS WmipWriteMessageToGuid(
IN PBGUIDENTRY GuidEntry,
IN PWMIGUIDOBJECT ReplyObject,
IN PUCHAR Message,
IN ULONG MessageSize,
OUT PULONG WrittenCount
)
/*+++
Routine Description:
This routine will loop over all instance sets attached to a guid entry
and if the data source for it is a user mode data source then it will
get a request messsage sent to it.
Note that if there are more than one providers to which a message is
sent, then success is returned as long as writing to one of them is
successful.
Arguments:
GuidEntry is the guid entry for the control guid
ReplyObject is the object to which the request object shoudl reply.
This may be NULL in the case that no reply is needed.
Message is the message to be sent
MessageSize is the size of the message in bytes
Return Value:
STATUS_SUCCESS or an error code
---*/
{
NTSTATUS Status, Status2;
PLIST_ENTRY InstanceSetList;
PBINSTANCESET InstanceSet;
PBDATASOURCE DataSource;
PAGED_CODE();
Status = STATUS_UNSUCCESSFUL;
*WrittenCount = 0;
WmipEnterSMCritSection();
//
// Loop over all instances and send create logger
// request to all user mode data providers
//
InstanceSetList = GuidEntry->ISHead.Flink;
while (InstanceSetList != &GuidEntry->ISHead)
{
InstanceSet = CONTAINING_RECORD(InstanceSetList,
INSTANCESET,
GuidISList);
DataSource = InstanceSet->DataSource;
if (DataSource->Flags & DS_USER_MODE)
{
//
// User mode guy, so send the request to him
//
ASSERT(DataSource->RequestObject != NULL);
Status2 = WmipWriteMBToObject(DataSource->RequestObject,
ReplyObject,
Message,
MessageSize);
if (NT_SUCCESS(Status2))
{
Status = STATUS_SUCCESS;
(*WrittenCount)++;
}
}
InstanceSetList = InstanceSetList->Flink;
}
WmipLeaveSMCritSection();
return(Status);
}
NTSTATUS WmipCreateUMLogger(
IN OUT PWMICREATEUMLOGGER CreateInfo
)
/*+++
Routine Description:
This routine will send a request to create a UM logger. First it will
find the providers associated with the control guid and then create a
reply object which the providers will reply to when the UM logger is
created. Note that the reply object is created as an unnamed object,
but that the guid passed in the object name is used to look up the
security descriptor for the reply object.
Note that if there are more than one providers to which a message is
sent, then success is returned as long as writing to one of them is
successful.
Arguments:
CreateInfo has the information needed to create the UM logger.
Return Value:
STATUS_SUCCESS or an error code
---*/
{
NTSTATUS Status;
PBGUIDENTRY GuidEntry;
HANDLE ReplyHandle;
PWMIGUIDOBJECT ReplyObject;
ULONG MessageSize = 1;
PWNODE_HEADER Wnode;
ULONG ReplyCount;
OBJECT_ATTRIBUTES CapturedObjectAttributes;
UNICODE_STRING CapturedGuidString;
WCHAR CapturedGuidBuffer[WmiGuidObjectNameLength + 1];
PAGED_CODE();
Status = WmipProbeAndCaptureGuidObjectAttributes(&CapturedObjectAttributes,
&CapturedGuidString,
CapturedGuidBuffer,
CreateInfo->ObjectAttributes);
if (NT_SUCCESS(Status))
{
GuidEntry = WmipFindGEByGuid(&CreateInfo->ControlGuid, FALSE);
if (GuidEntry != NULL)
{
//
// Control guid is registered so create a reply object that the
// provider will write to.
//
if (WmipIsControlGuid(GuidEntry))
{
//
// Create the reply object
//
Status = WmipOpenGuidObject(&CapturedObjectAttributes,
TRACELOG_CREATE_INPROC |
TRACELOG_GUID_ENABLE |
WMIGUID_NOTIFICATION,
UserMode,
&ReplyHandle,
&ReplyObject);
if (NT_SUCCESS(Status))
{
//
// Send request to all providers who registered for control
// guid
//
ReplyObject->Flags |= WMIGUID_FLAG_REPLY_OBJECT;
InitializeListHead(&ReplyObject->RequestListHead);
Wnode = (PWNODE_HEADER) ((PUCHAR) CreateInfo+ sizeof(WMICREATEUMLOGGER));
MessageSize = Wnode->BufferSize;
Status = WmipWriteMessageToGuid(GuidEntry,
ReplyObject,
(PUCHAR)Wnode,
MessageSize,
&ReplyCount
);
if (NT_SUCCESS(Status))
{
//
// Create logger requests delivered ok so return handle
// to object that will receive the replies.
//
CreateInfo->ReplyHandle.Handle = ReplyHandle;
CreateInfo->ReplyCount = ReplyCount;
} else {
//
// We were not able to deliver the requests so we do not
// need to keep the reply object open
//
ZwClose(ReplyHandle);
}
//
// remove the ref taken when the object was created
//
ObDereferenceObject(ReplyObject);
}
}
WmipUnreferenceGE(GuidEntry);
} else {
//
// Control guid is not registered so return an error
//
Status = STATUS_WMI_INSTANCE_NOT_FOUND;
}
}
return(Status);
}
NTSTATUS WmipMBReply(
IN HANDLE RequestHandle,
IN ULONG ReplyIndex,
IN PUCHAR Message,
IN ULONG MessageSize
)
/*+++
Routine Description:
This routine will write a MB reply message to the appropriate
reply object and unlink the reply object from the request object;
Arguments:
RequestHandle is the handle to the request object
ReplyIndex is the index to the MBRequest entry for the reply object
Message is the reply message
MessageSize is the size of the reply message
Return Value:
STATUS_SUCCESS or an error code
---*/
{
NTSTATUS Status;
PWMIGUIDOBJECT RequestObject, ReplyObject;
PMBREQUESTS MBRequest;
PAGED_CODE();
Status = ObReferenceObjectByHandle(RequestHandle,
TRACELOG_REGISTER_GUIDS,
WmipGuidObjectType,
UserMode,
&RequestObject,
NULL);
if (NT_SUCCESS(Status))
{
if (ReplyIndex < MAXREQREPLYSLOTS)
{
//
// Is the ReplyIndex passed valid ??
//
WmipEnterSMCritSection();
MBRequest = &RequestObject->MBRequests[ReplyIndex];
ReplyObject = MBRequest->ReplyObject;
if (ReplyObject != NULL)
{
//
// We have figured out who we need to reply to so
// clear out the link between the reply object
// and this request object
//
RemoveEntryList(&MBRequest->RequestListEntry);
MBRequest->ReplyObject = NULL;
ObDereferenceObject(ReplyObject);
Status = WmipWriteMBToObject(ReplyObject,
NULL,
Message,
MessageSize);
if (! NT_SUCCESS(Status))
{
WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_ERROR_LEVEL,
"WMI: WmipWriteMBToObject(%p) failed %x\n",
ReplyObject,
Status));
}
} else {
Status = STATUS_INVALID_PARAMETER;
}
WmipLeaveSMCritSection();
} else {
Status = STATUS_INVALID_PARAMETER;
}
ObDereferenceObject(RequestObject);
}
return(Status);
}
#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg()
#endif