/*++

Copyright (c) Microsoft Corporation. All rights reserved. 

You may only use this code if you agree to the terms of the Windows Research Kernel Source Code License agreement (see License.txt).
If you do not agree to the terms, do not use the code.


Module Name:

   wmi.c

Abstract:

    Device driver interface for WMI

--*/

#include "wmikmp.h"
#include "evntrace.h"
#include "tracep.h"

NTSTATUS
WmipOpenCloseCleanup(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    );
NTSTATUS
WmipIoControl(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    );

NTSTATUS WmipObjectToPDO(
    PFILE_OBJECT FileObject,
    PDEVICE_OBJECT DeviceObject,
    PDEVICE_OBJECT *PDO
    );

BOOLEAN
WmipFastIoDeviceControl(
    IN PFILE_OBJECT FileObject,
    IN BOOLEAN Wait,
    IN PVOID InputBuffer OPTIONAL,
    IN ULONG InputBufferLength,
    OUT PVOID OutputBuffer OPTIONAL,
    IN ULONG OutputBufferLength,
    IN ULONG IoControlCode,
    OUT PIO_STATUS_BLOCK IoStatus,
    IN struct _DEVICE_OBJECT *DeviceObject
    );

NTSTATUS
DriverEntry(
    IN PDRIVER_OBJECT DriverObject,
    IN PUNICODE_STRING RegistryPath
    );


NTSTATUS WmipProbeWnodeAllData(
    PWNODE_ALL_DATA Wnode,
    ULONG InBufferLen,
    ULONG OutBufferLen
    );

NTSTATUS WmipProbeWnodeSingleInstance(
    PWNODE_SINGLE_INSTANCE Wnode,
    ULONG InBufferLen,
    ULONG OutBufferLen,
    BOOLEAN OutBound
    );

NTSTATUS WmipProbeWnodeSingleItem(
    PWNODE_SINGLE_ITEM Wnode,
    ULONG InBufferLen
    );


NTSTATUS WmipProbeWnodeMethodItem(
    PWNODE_METHOD_ITEM Wnode,
    ULONG InBufferLen,
    ULONG OutBufferLen
    );

NTSTATUS WmipProbeWnodeWorker(
    PWNODE_HEADER WnodeHeader,
    ULONG MinWnodeSize,
    ULONG InstanceNameOffset,
    ULONG DataBlockOffset,
    ULONG DataBlockSize,
    ULONG InBufferLen,
    ULONG OutBufferLen,
    BOOLEAN CheckOutBound,
    BOOLEAN CheckInBound
    );


NTSTATUS
WmipSystemControl(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP           Irp
    );

NTSTATUS WmipSendWmiIrp(
    UCHAR MinorFunction,
    ULONG ProviderId,
    PVOID DataPath,
    ULONG BufferLength,
    PVOID Buffer,
    PIO_STATUS_BLOCK Iosb
    );

NTSTATUS WmipProbeWmiRegRequest(
    IN PDEVICE_OBJECT DeviceObject,
    IN PWMIREGREQUEST Buffer,
    IN ULONG InBufferLen,
    IN ULONG OutBufferLen,
    OUT PBOOLEAN MofIgnored    
    );


#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT,WmipDriverEntry)
#pragma alloc_text(INIT,DriverEntry)

#pragma alloc_text(PAGE,WmipOpenCloseCleanup)
#pragma alloc_text(PAGE,WmipIoControl)
#pragma alloc_text(PAGE,WmipForwardWmiIrp)
#pragma alloc_text(PAGE,WmipObjectToPDO)
#pragma alloc_text(PAGE,WmipTranslateFileHandle)
#pragma alloc_text(PAGE,WmipProbeWnodeAllData)
#pragma alloc_text(PAGE,WmipProbeWnodeSingleInstance)
#pragma alloc_text(PAGE,WmipProbeWnodeSingleItem)
#pragma alloc_text(PAGE,WmipProbeWnodeMethodItem)
#pragma alloc_text(PAGE,WmipProbeWnodeWorker)
#pragma alloc_text(PAGE,WmipProbeWmiOpenGuidBlock)
#pragma alloc_text(PAGE,WmipProbeAndCaptureGuidObjectAttributes)
#pragma alloc_text(PAGE,WmipUpdateDeviceStackSize)
#pragma alloc_text(PAGE,WmipSystemControl)
#pragma alloc_text(PAGE,WmipGetDevicePDO)
#pragma alloc_text(PAGE,WmipSendWmiIrp)
#pragma alloc_text(PAGE,WmipProbeWmiRegRequest)

#pragma alloc_text(PAGE,WmipFastIoDeviceControl)

#endif


PDEVICE_OBJECT WmipServiceDeviceObject;
PDEVICE_OBJECT WmipAdminDeviceObject;

//
// This specifies the maximum size that an event can be
ULONG WmipMaxKmWnodeEventSize = DEFAULTMAXKMWNODEEVENTSIZE;



#ifdef ALLOC_DATA_PRAGMA
#pragma data_seg("PAGEDATA")
#endif

#if defined(_AMD64_) || defined(i386)
PVOID WmipDockUndockNotificationEntry;
#endif

KMUTEX WmipSMMutex;
KMUTEX WmipTLMutex;

//
// This maintains the registry path for the wmi device
UNICODE_STRING WmipRegistryPath;

FAST_IO_DISPATCH WmipFastIoDispatch;

#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg("PAGECONST")
#endif

NTSTATUS
DriverEntry(
    IN PDRIVER_OBJECT DriverObject,
    IN PUNICODE_STRING RegistryPath
    )
{
    UNREFERENCED_PARAMETER(DriverObject);
    UNREFERENCED_PARAMETER(RegistryPath);
    // Never called
    return(STATUS_SUCCESS);
}


NTSTATUS
WmipDriverEntry(
    IN PDRIVER_OBJECT DriverObject,
    IN PUNICODE_STRING RegistryPath
    )

/*++

Routine Description:

    This is the callback function when we call IoCreateDriver to create a
    WMI Driver Object.  In this function, we need to remember the
    DriverObject, create a device object and then create a Win32 visible
    symbolic link name so that the WMI user mode component can access us.

Arguments:

    DriverObject - Pointer to the driver object created by the system.

    RegistryPath - is NULL.

Return Value:

   STATUS_SUCCESS

--*/

{
    NTSTATUS Status;
    UNICODE_STRING DeviceName;
    UNICODE_STRING ServiceSymbolicLinkName;
    UNICODE_STRING AdminSymbolicLinkName;
    PSECURITY_DESCRIPTOR AdminDeviceSd;
    PFAST_IO_DISPATCH fastIoDispatch;
    ANSI_STRING AnsiString;

    PAGED_CODE();
    UNREFERENCED_PARAMETER(RegistryPath);

    //
    // First thing to do is make sure our critical section has been initialized
    //
    KeInitializeMutex(&WmipSMMutex, 0);
    KeInitializeMutex(&WmipTLMutex, 0);

    //
    // Initialize internal WMI data structures
    //
    WmipInitializeRegistration(0);
    WmipInitializeNotifications();
    Status = WmipInitializeDataStructs();
    if (! NT_SUCCESS(Status))
    {
        return(Status);
    }

    //
    // Since Io does not pass a registry path for this device we need to make
    // up one
    RtlInitAnsiString(&AnsiString,
                         "\\Registry\\Machine\\System\\CurrentControlSet\\Services\\WMI");
    Status = RtlAnsiStringToUnicodeString(&WmipRegistryPath,
                                          &AnsiString,
                                          TRUE);
    Status = WmipInitializeSecurity();
    if (!NT_SUCCESS(Status))
    {
        return(Status);
    }

    //
    // We allocate a security descriptor to be placed on the Admin device.
    // It will allow only Administrators access to the device and
    // no access to anyone else. 
    //
    Status = WmipCreateAdminSD(&AdminDeviceSd);
    if (! NT_SUCCESS(Status))
    {
        return(Status);
    }

    //
    // Create the service device object and symbolic link
    //
    RtlInitUnicodeString( &DeviceName, WMIServiceDeviceObjectName );
    Status = IoCreateDevice(
                 DriverObject,
                 0,
                 &DeviceName,
                 FILE_DEVICE_UNKNOWN,
                 FILE_DEVICE_SECURE_OPEN, // No standard device characteristics
                 FALSE,                   // This isn't an exclusive device
                 &WmipServiceDeviceObject
                 );

    if (! NT_SUCCESS(Status))
    {
        ExFreePool(AdminDeviceSd);
        return(Status);
    }

    RtlInitUnicodeString( &ServiceSymbolicLinkName,
                          WMIServiceSymbolicLinkName );
    Status = IoCreateSymbolicLink( &ServiceSymbolicLinkName,
                                   &DeviceName );
    if (! NT_SUCCESS(Status))
    {
        IoDeleteDevice( WmipServiceDeviceObject );
        ExFreePool(AdminDeviceSd);
        return(Status);
    }


    //
    // Now create an admin-only device object and symbolic link
    //
    RtlInitUnicodeString( &DeviceName, WMIAdminDeviceObjectName );
    Status = IoCreateDevice(
                 DriverObject,
                 0,
                 &DeviceName,
                 FILE_DEVICE_UNKNOWN,
                 FILE_DEVICE_SECURE_OPEN, // No standard device characteristics
                 FALSE,                   // This isn't an exclusive device
                 &WmipAdminDeviceObject
                 );

    if (! NT_SUCCESS(Status))
    {
        IoDeleteDevice( WmipServiceDeviceObject );
        IoDeleteSymbolicLink(&ServiceSymbolicLinkName);
        ExFreePool(AdminDeviceSd);
        return(Status);
    }


    Status = ObSetSecurityObjectByPointer(WmipAdminDeviceObject,
                                          DACL_SECURITY_INFORMATION |
                                              OWNER_SECURITY_INFORMATION,
                                          AdminDeviceSd);
    
    ExFreePool(AdminDeviceSd);
    AdminDeviceSd = NULL;
    
    if (! NT_SUCCESS(Status))
    {
        IoDeleteDevice( WmipServiceDeviceObject );
        IoDeleteDevice( WmipAdminDeviceObject );
        IoDeleteSymbolicLink(&ServiceSymbolicLinkName);
        return(Status);
    }
    
    RtlInitUnicodeString( &AdminSymbolicLinkName,
                          WMIAdminSymbolicLinkName );
    Status = IoCreateSymbolicLink( &AdminSymbolicLinkName,
                                   &DeviceName );
    if (! NT_SUCCESS(Status))
    {
        IoDeleteSymbolicLink( &ServiceSymbolicLinkName );
        IoDeleteDevice( WmipServiceDeviceObject );
        IoDeleteDevice( WmipAdminDeviceObject );
        return(Status);
    }
    
    //
    // Establish an initial irp stack size
    WmipServiceDeviceObject->StackSize = WmiDeviceStackSize;
    WmipAdminDeviceObject->StackSize = WmiDeviceStackSize;

    //
    // Create dispatch entrypoints
    //
    DriverObject->MajorFunction[IRP_MJ_CREATE] = WmipOpenCloseCleanup;
    DriverObject->MajorFunction[IRP_MJ_CLOSE] = WmipOpenCloseCleanup;
    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = WmipIoControl;
    DriverObject->MajorFunction[IRP_MJ_CLEANUP] = WmipOpenCloseCleanup;
    DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = WmipSystemControl;
    DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = WmipShutdown;

    //
    // Register for notification of docking events
#if  defined(_AMD64_) || defined(i386)
    IoRegisterPlugPlayNotification(
                                  EventCategoryHardwareProfileChange,
                                  0,
                                  NULL,
                                  DriverObject,
                                  WmipDockUndockEventCallback,
                                  NULL,
                                  &WmipDockUndockNotificationEntry);
#endif
    //
    // We reset this flag to let the IO manager know that the device
    // is ready to receive requests. We only do this for the kernel
    // dll since the IO manager does it when WMI loads as a normal
    // device.
    WmipServiceDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
    WmipAdminDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;

    IoWMIRegistrationControl(WmipServiceDeviceObject,
                             WMIREG_ACTION_REGISTER);

    fastIoDispatch = &WmipFastIoDispatch;
    RtlZeroMemory(fastIoDispatch, sizeof(FAST_IO_DISPATCH));
    fastIoDispatch->SizeOfFastIoDispatch = sizeof(FAST_IO_DISPATCH);
    fastIoDispatch->FastIoDeviceControl = WmipFastIoDeviceControl;
    DriverObject->FastIoDispatch = fastIoDispatch;
    RtlZeroMemory(&WmipRefCount[0], MAXLOGGERS*sizeof(ULONG));
    RtlZeroMemory(&WmipLoggerContext[0], MAXLOGGERS*sizeof(PWMI_LOGGER_CONTEXT));
    WmipStartGlobalLogger();        // Try and see if we need to start this
    IoRegisterShutdownNotification(WmipServiceDeviceObject);

    SharedUserData->TraceLogging = 0; //Initialize the Heap and Crisec Coll tracing status off

    return(Status);
}

NTSTATUS
WmipOpenCloseCleanup(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )
{
    PAGED_CODE();
    UNREFERENCED_PARAMETER(DeviceObject);

    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = 0;

    IoCompleteRequest( Irp, IO_NO_INCREMENT );

    return STATUS_SUCCESS;
}

void WmipUpdateDeviceStackSize(
    CCHAR NewStackSize
    )
/*++

Routine Description:

    This routine will update the stack size that is specified in the WMI
    device's device object. This needs to be protected since it can be updated
    when a device registers and whenever an irp is forwarded to a device.
    WMI needs to maintain a stack size one greater than the stack size of the
    largest device stack to which it forwards irps to. Consider a bottom
    driver that registers with WMI and has a stack size of 1. If 2 device
    attach on top of it then WMI will forward to the topmost in the stack
    which would need a stack size of 3, so the original WMI irp (ie the one
    created by the IOCTL to the WMI device) would need a stack size of 4.

Arguments:

    NewStackSize is the new stack size needed

Return Value:

    NT status ccode

--*/
{
    PAGED_CODE();

    WmipEnterSMCritSection();
    if (WmipServiceDeviceObject->StackSize < NewStackSize)
    {
        WmipServiceDeviceObject->StackSize = NewStackSize;
    }
    WmipLeaveSMCritSection();
}


NTSTATUS
WmipIoControl(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )
{
    NTSTATUS Status;
    PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
    ULONG InBufferLen = irpStack->Parameters.DeviceIoControl.InputBufferLength;
    ULONG OutBufferLen = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
    PVOID Buffer =  Irp->AssociatedIrp.SystemBuffer;
    PWNODE_HEADER Wnode = (PWNODE_HEADER)Buffer;
    ULONG Ioctl;

    PAGED_CODE();

    Ioctl = irpStack->Parameters.DeviceIoControl.IoControlCode;

    switch (Ioctl)
    {
        case IOCTL_WMI_OPEN_GUID:
        case IOCTL_WMI_OPEN_GUID_FOR_QUERYSET:
        case IOCTL_WMI_OPEN_GUID_FOR_EVENTS:
        {
            OBJECT_ATTRIBUTES CapturedObjectAttributes;
            UNICODE_STRING CapturedGuidString;
            WCHAR CapturedGuidBuffer[WmiGuidObjectNameLength + 1];
            PWMIOPENGUIDBLOCK InGuidBlock;
            HANDLE Handle;
            ULONG DesiredAccess;

            InGuidBlock = (PWMIOPENGUIDBLOCK)Buffer;

            Status = WmipProbeWmiOpenGuidBlock(&CapturedObjectAttributes,
                                               &CapturedGuidString,
                                               CapturedGuidBuffer,
                                               &DesiredAccess,
                                               InGuidBlock,
                                               InBufferLen,
                                               OutBufferLen);

            if (NT_SUCCESS(Status))
            {
                Status = WmipOpenBlock(Ioctl,
                                       UserMode,
                                       &CapturedObjectAttributes,
                                       DesiredAccess,
                                       &Handle);
                if (NT_SUCCESS(Status))
                {
#if defined(_WIN64)
                    if (IoIs32bitProcess(NULL))
                    {
                        ((PWMIOPENGUIDBLOCK32)InGuidBlock)->Handle.Handle32 = PtrToUlong(Handle);
                    }
                    else
#endif
                    {
                        InGuidBlock->Handle.Handle = Handle;
                    }
                }
            }
            break;
        }

        case IOCTL_WMI_QUERY_ALL_DATA:
        {
            if (OutBufferLen < sizeof(WNODE_ALL_DATA))
            {
                //
                // WMI will not send any request whose output buffer is not
                // at least the size of a WNODE_ALL_DATA.
                Status = STATUS_BUFFER_TOO_SMALL;
                break;
            }

            Status = WmipProbeWnodeAllData((PWNODE_ALL_DATA)Wnode,
                                             InBufferLen,
                                             OutBufferLen);

            if (NT_SUCCESS(Status))
            {
                Status = WmipQueryAllData(NULL,
                                          Irp,
                                          UserMode,
                                          (PWNODE_ALL_DATA)Wnode,
                                          OutBufferLen,
                                          &OutBufferLen);

            } else {
                WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: Invalid IOCTL_WMI_QUERY_ALL_DATA Wnode\n"));
            }
            break;
        }

        case IOCTL_WMI_QAD_MULTIPLE:
        {
            PWMIQADMULTIPLE QadMultiple;

            QadMultiple = (PWMIQADMULTIPLE)Buffer;

            //
            // Check that the input/output sizes make sense and that we have a
            // valid HandleCount.
            //

            if ((OutBufferLen >= sizeof(WNODE_TOO_SMALL)) &&
                RTL_CONTAINS_FIELD(QadMultiple, InBufferLen, HandleCount) &&
                (QadMultiple->HandleCount > 0) &&
                (QadMultiple->HandleCount < QUERYMULIPLEHANDLELIMIT) &&
                RTL_CONTAINS_FIELD(QadMultiple, InBufferLen, Handles[QadMultiple->HandleCount - 1])) {

                Status = WmipQueryAllDataMultiple(0,
                                                  NULL,
                                                  Irp,
                                                  UserMode,
                                                  Buffer,
                                                  OutBufferLen,
                                                  QadMultiple,
                                                  &OutBufferLen);
            } else {
                Status = STATUS_INVALID_PARAMETER;
            }

            break;
        }


        case IOCTL_WMI_QUERY_SINGLE_INSTANCE:
        {
            if (OutBufferLen < sizeof(WNODE_TOO_SMALL))
            {
                //
                // WMI will not send any request whose output buffer is not
                // at least the size of a WNODE_TOO_SMALL.
                Status = STATUS_BUFFER_TOO_SMALL;
                break;
            }

            Status = WmipProbeWnodeSingleInstance((PWNODE_SINGLE_INSTANCE)Wnode,
                                                  InBufferLen,
                                                  OutBufferLen,
                                                  TRUE);

            if (NT_SUCCESS(Status))
            {
                Status = WmipQuerySetExecuteSI(NULL,
                                               Irp,
                                               UserMode,
                                               IRP_MN_QUERY_SINGLE_INSTANCE,
                                               Wnode,
                                               OutBufferLen,
                                               &OutBufferLen);

                if (NT_SUCCESS(Status))
                {
                    WmipAssert(Irp->IoStatus.Information <= OutBufferLen);
                }
            } else {
                WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: Invalid IOCTL_WMI_SINGLE_INSTANCE Wnode\n"));
            }
            break;
        }

        case IOCTL_WMI_QSI_MULTIPLE:
        {
            PWMIQSIMULTIPLE QsiMultiple;

            QsiMultiple = (PWMIQSIMULTIPLE)Buffer;

            //
            // Check that the input/output sizes make sense and that we have a
            // valid QueryCount.
            //

            if ((OutBufferLen >= sizeof(WNODE_TOO_SMALL)) &&
                RTL_CONTAINS_FIELD(QsiMultiple, InBufferLen, QueryCount) &&
                (QsiMultiple->QueryCount > 0) &&
                (QsiMultiple->QueryCount < QUERYMULIPLEHANDLELIMIT) &&
                RTL_CONTAINS_FIELD(QsiMultiple, InBufferLen, QsiInfo[QsiMultiple->QueryCount - 1])) {

                Status = WmipQuerySingleMultiple(Irp,
                                                 UserMode,
                                                 Buffer,
                                                 OutBufferLen,
                                                 QsiMultiple,
                                                 QsiMultiple->QueryCount,
                                                 NULL,
                                                 NULL,
                                                 &OutBufferLen);
            } else {
                Status = STATUS_INVALID_PARAMETER;
            }

            break;
        }

        case IOCTL_WMI_SET_SINGLE_INSTANCE:
        {
            Status = WmipProbeWnodeSingleInstance((PWNODE_SINGLE_INSTANCE)Wnode,
                                                  InBufferLen,
                                                  OutBufferLen,
                                                  FALSE);

            if (NT_SUCCESS(Status))
            {
                Status = WmipQuerySetExecuteSI(NULL,
                                               Irp,
                                               UserMode,
                                               IRP_MN_CHANGE_SINGLE_INSTANCE,
                                               Wnode,
                                               InBufferLen,
                                               &OutBufferLen);

                OutBufferLen = 0;
            } else {
                WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: Invalid IOCTL_WMI_SET_SINGLE_INSTANCE Wnode\n"));
            }
            break;
        }


        case IOCTL_WMI_SET_SINGLE_ITEM:
        {
            Status = WmipProbeWnodeSingleItem((PWNODE_SINGLE_ITEM)Wnode,
                                              InBufferLen);

            if (NT_SUCCESS(Status))
            {
                Status = WmipQuerySetExecuteSI(NULL,
                                               Irp,
                                               UserMode,
                                               IRP_MN_CHANGE_SINGLE_ITEM,
                                               Wnode,
                                               InBufferLen,
                                               &OutBufferLen);

                OutBufferLen = 0;
            } else {
                WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: Invalid IOCTL_WMI_SET_SINGLE_ITEM Wnode\n"));
            }
            break;
        }

        case IOCTL_WMI_EXECUTE_METHOD:
        {
            //
            // The buffer passed is the InputWnode directly followed by the
            // method wnode. This is so that the driver can fill in the
            // output WNODE directly on top of the input wnode.
            PWNODE_METHOD_ITEM MethodWnode = (PWNODE_METHOD_ITEM)Wnode;

            Status = WmipProbeWnodeMethodItem(MethodWnode,
                                              InBufferLen,
                                              OutBufferLen);
            if (NT_SUCCESS(Status))
            {
                Status = WmipQuerySetExecuteSI(NULL,
                                               Irp,
                                               UserMode,
                                               IRP_MN_EXECUTE_METHOD,
                                               Wnode,
                                               OutBufferLen,
                                               &OutBufferLen);

                if (NT_SUCCESS(Status))
                {
                    WmipAssert(Irp->IoStatus.Information <= OutBufferLen);
                }
            }
            break;
        }

        case IOCTL_WMI_TRANSLATE_FILE_HANDLE:
        {
            if (InBufferLen != FIELD_OFFSET(WMIFHTOINSTANCENAME,
                                            InstanceNames))
            {
                Status = STATUS_UNSUCCESSFUL;
            } else {
                Status = WmipTranslateFileHandle((PWMIFHTOINSTANCENAME)Buffer,
                                                 &OutBufferLen,
                                                 NULL,
                                                 NULL,
                                                 NULL,
                                                 NULL);
            }
            break;
        }

        case IOCTL_WMI_GET_VERSION:
        {
            if (OutBufferLen < sizeof(WMIVERSIONINFO))
            {
                Status = STATUS_BUFFER_TOO_SMALL;
            } else {
                ((PWMIVERSIONINFO)Buffer)->Version = WMI_CURRENT_VERSION;
                OutBufferLen = sizeof(WMIVERSIONINFO);
                Status = STATUS_SUCCESS;
            }
            break;
        }


        case IOCTL_WMI_ENUMERATE_GUIDS_AND_PROPERTIES:
        case IOCTL_WMI_ENUMERATE_GUIDS:
        {
            if (OutBufferLen < FIELD_OFFSET(WMIGUIDLISTINFO, GuidList))
            {
                Status = STATUS_BUFFER_TOO_SMALL;
            } else {
                Status = WmipEnumerateGuids(Ioctl,
                                            (PWMIGUIDLISTINFO)Buffer,
                                            OutBufferLen,
                                            &OutBufferLen);

            }
            break;
        }

        case IOCTL_WMI_QUERY_GUID_INFO:
        {
            if (OutBufferLen < sizeof(WMIQUERYGUIDINFO))
            {
                Status = STATUS_BUFFER_TOO_SMALL;
            } else {
                Status = WmipQueryGuidInfo((PWMIQUERYGUIDINFO)Buffer);
                OutBufferLen = sizeof(WMIQUERYGUIDINFO);

            }
            break;
        }

        case IOCTL_WMI_ENUMERATE_MOF_RESOURCES:
        {
            if (OutBufferLen >= sizeof(WMIMOFLIST))
            {
                Status = WmipEnumerateMofResources((PWMIMOFLIST)Buffer,
                                                   OutBufferLen,
                                                      &OutBufferLen);
            } else {
                Status = STATUS_INVALID_PARAMETER;
            }
            break;
        }

        case IOCTL_WMI_RECEIVE_NOTIFICATIONS:
        {
            PWMIRECEIVENOTIFICATION ReceiveNotification;
            ULONG CountExpected;

            if ((InBufferLen >= sizeof(WMIRECEIVENOTIFICATION)) &&
                (OutBufferLen >= sizeof(WNODE_TOO_SMALL)))
            {
                ReceiveNotification = (PWMIRECEIVENOTIFICATION)Buffer;
                
                CountExpected = (InBufferLen -
                                 FIELD_OFFSET(WMIRECEIVENOTIFICATION, Handles)) /
                                sizeof(HANDLE3264);

                if (ReceiveNotification->HandleCount <= CountExpected)
                {
                    Status = WmipReceiveNotifications(ReceiveNotification,
                                                      &OutBufferLen,
                                                      Irp);
                } else {
                     //
                    // Input buffer not large enough which is an error
                    //
                    Status = STATUS_INVALID_DEVICE_REQUEST;
                }
            } else {
                //
                // Input and or output buffers not large enough
                // which is an error
                //
                Status = STATUS_INVALID_DEVICE_REQUEST;
            }
            break;
        }

        case IOCTL_WMI_MARK_HANDLE_AS_CLOSED:
        {
            PWMIMARKASCLOSED MarkAsClosed;

            if (InBufferLen >= sizeof(WMIMARKASCLOSED))
            {
                MarkAsClosed = (PWMIMARKASCLOSED)Buffer;
                Status = WmipMarkHandleAsClosed(MarkAsClosed->Handle.Handle);
                OutBufferLen = 0;
            } else {
                Status = STATUS_INVALID_DEVICE_REQUEST;             
            }
            break;
        }
        
        case IOCTL_WMI_NOTIFY_LANGUAGE_CHANGE:
        {
            LPGUID LanguageGuid;
            PWMILANGUAGECHANGE LanguageChange;
            
            if (DeviceObject == WmipAdminDeviceObject)
            {
                //
                // Only allow this ioctl to be executed on the admin
                // device object
                //
                if (InBufferLen == sizeof(WMILANGUAGECHANGE))
                {
                    LanguageChange = (PWMILANGUAGECHANGE)Buffer;
                    if (LanguageChange->Flags & WMILANGUAGECHANGE_FLAG_ADDED)
                    {
                        LanguageGuid = &GUID_MOF_RESOURCE_ADDED_NOTIFICATION;
                    } else if (LanguageChange->Flags & WMILANGUAGECHANGE_FLAG_REMOVED) {
                        LanguageGuid = &GUID_MOF_RESOURCE_REMOVED_NOTIFICATION;
                    } else {
                        Status = STATUS_INVALID_DEVICE_REQUEST;
                        break;
                    }

                    //
                    // Ensure that language is nul terminated
                    //
                    LanguageChange->Language[MAX_LANGUAGE_SIZE-1] = 0;
                    WmipGenerateMofResourceNotification(LanguageChange->Language,
                                                        L"",
                                                        LanguageGuid,
                                                        MOFEVENT_ACTION_LANGUAGE_CHANGE);

                    OutBufferLen = 0;
                    Status = STATUS_SUCCESS;
                } else {
                    Status = STATUS_INVALID_DEVICE_REQUEST;
                }
            } else {
                Status = STATUS_INVALID_DEVICE_REQUEST;
            }

            break;
        }

        // Event trace logging IOCTLS

        case IOCTL_WMI_UNREGISTER_GUIDS:
        {
            if ((InBufferLen == sizeof(WMIUNREGGUIDS)) &&
                (OutBufferLen == sizeof(WMIUNREGGUIDS)))
            {
                Status = WmipUnregisterGuids((PWMIUNREGGUIDS)Buffer);
            } else {
                Status = STATUS_INVALID_PARAMETER;
            }

            break;
        }
        
        case IOCTL_WMI_REGISTER_GUIDS:
        {
            BOOLEAN MofIgnored = FALSE;
            //
            // Register guids for user mode provider
            //
            Status = WmipProbeWmiRegRequest(
                                            DeviceObject,
                                            Buffer,
                                            InBufferLen,
                                            OutBufferLen,
                                            &MofIgnored
                                           );
            if (NT_SUCCESS(Status))
            {
                HANDLE RequestHandle;
                PWMIREGREQUEST WmiRegRequest;
                PWMIREGINFOW WmiRegInfo;
                ULONG WmiRegInfoSize;
                ULONG GuidCount;
                PWMIREGRESULTS WmiRegResults;
                PWMIREGINFOW WmiRegInfoThunk = NULL;

                WmiRegRequest = (PWMIREGREQUEST)Buffer;
                WmiRegInfo = (PWMIREGINFOW)OffsetToPtr(Buffer, sizeof(WMIREGREQUEST));
                WmiRegInfoSize = InBufferLen - sizeof(WMIREGREQUEST);
                GuidCount = WmiRegInfo->GuidCount;
                WmiRegResults = (PWMIREGRESULTS)Buffer;

                //
                // For WOW64, WMIREGINFOW and WMIREGGUIDW structures both need
                // to be thunked here because of padding and ULONG_PTR in them.
                //
#if defined(_WIN64)
                if (IoIs32bitProcess(NULL))
                {
                    ULONG SizeNeeded, SizeToCopy, i;
                    PWMIREGGUIDW WmiRegGuid;
                    PUCHAR pSource, pTarget;
                    ULONG ImageNameLength = 0;
                    ULONG ResourceNameLength = 0;
                    ULONG Offset = 0;
                    //
                    // Find the GuidCount and allocate storage here.
                    //

                    if (WmiRegInfo->RegistryPath > 0) 
                    {
                        pSource = OffsetToPtr(WmiRegInfo, WmiRegInfo->RegistryPath);
                        ImageNameLength = *( (PUSHORT) pSource) + sizeof(USHORT);
                    }

                    if (WmiRegInfo->MofResourceName > 0)
                    {
                        pSource = OffsetToPtr(WmiRegInfo, WmiRegInfo->MofResourceName);
                        ResourceNameLength = *((PUSHORT)pSource) + sizeof(USHORT);
                    }

                    SizeNeeded = sizeof(WMIREGINFOW) + 
                                         GuidCount * sizeof(WMIREGGUIDW) +
                                         ImageNameLength + ResourceNameLength;

                    SizeNeeded = (SizeNeeded + 7) & ~7;

                    WmiRegInfoThunk = (PWMIREGINFOW) WmipAlloc(SizeNeeded);

                    if (WmiRegInfoThunk == NULL)
                    {
                        Status = STATUS_INSUFFICIENT_RESOURCES;
                        OutBufferLen = 0;
                        break;
                    }
                    RtlZeroMemory(WmiRegInfoThunk, SizeNeeded);
                    pTarget = (PUCHAR)WmiRegInfoThunk;
                    pSource = (PUCHAR)WmiRegInfo;
                    SizeToCopy = WmiRegRequest->WmiRegInfo32Size;
                    RtlCopyMemory(pTarget, pSource, SizeToCopy);

                    pTarget += FIELD_OFFSET(WMIREGINFOW, WmiRegGuid);
                    pSource += SizeToCopy;
                    SizeToCopy = WmiRegRequest->WmiRegGuid32Size;
                    Offset = FIELD_OFFSET(WMIREGINFOW, WmiRegGuid);

                    for (i=0; i < GuidCount; i++)
                    {
                        RtlCopyMemory(pTarget, pSource, SizeToCopy);

                        //
                        // The InstanceCount checks are done here because the
                        // source may not be aligned. 
                        //
                        WmiRegGuid = (PWMIREGGUIDW) pTarget;
                        if ( (WmiRegGuid->InstanceCount > 0) ||
                             (WmiRegGuid->InstanceNameList > 0) )
                        {
                            return STATUS_UNSUCCESSFUL;
                        }
                        pTarget += sizeof(WMIREGGUIDW);
                        pSource += SizeToCopy;
                        Offset += sizeof(WMIREGGUIDW);
                    }

                    if (ImageNameLength > 0) 
                    {
                        pSource = OffsetToPtr(WmiRegInfo, WmiRegInfo->RegistryPath);
                        RtlCopyMemory(pTarget, pSource, ImageNameLength);
                        pTarget += ImageNameLength;
                        WmiRegInfoThunk->RegistryPath = Offset;
                        Offset += ImageNameLength;
                    }

                    if (ResourceNameLength > 0) 
                    {
                        pSource = OffsetToPtr(WmiRegInfo, WmiRegInfo->MofResourceName);
                        RtlCopyMemory(pTarget, pSource, ResourceNameLength);
                        pTarget += ResourceNameLength;
                        WmiRegInfoThunk->MofResourceName = Offset;
                        Offset += ResourceNameLength;
                    }

                    WmiRegInfo = WmiRegInfoThunk;
                    WmiRegInfoSize = SizeNeeded;
                    WmiRegInfo->BufferSize = SizeNeeded;
                }
#endif

                Status = WmipRegisterUMGuids(WmiRegRequest->ObjectAttributes,
                                         WmiRegRequest->Cookie,
                                         WmiRegInfo,
                                         WmiRegInfoSize,
                                         &RequestHandle,
                                         &WmiRegResults->LoggerContext);

                if (NT_SUCCESS(Status))
                {
#if defined(_WIN64)
                    if (IoIs32bitProcess(NULL))
                    {
                        WmiRegResults->RequestHandle.Handle64 = 0;
                        WmiRegResults->RequestHandle.Handle32 = PtrToUlong(RequestHandle);
                    }
                    else
#endif
                    {
                        WmiRegResults->RequestHandle.Handle = RequestHandle;
                    }
                    WmiRegResults->MofIgnored = MofIgnored;

                    OutBufferLen = sizeof(WMIREGRESULTS);
                }

                if (WmiRegInfoThunk != NULL)
                {
                    WmipFree(WmiRegInfoThunk);
                }
            }

            break;
        }

        case IOCTL_WMI_CREATE_UM_LOGGER:
        {
            //
            // Create User mode logger
            //
            PWNODE_HEADER Wnode;
            ULONG MinLength;

#if defined(_WIN64)
            if (IoIs32bitProcess(NULL))
            {
                ULONG SizeNeeded; 
                PUCHAR src, dest;
                PWMICREATEUMLOGGER32 WmiCreateUmLogger32 = (PWMICREATEUMLOGGER32)Buffer;
                PWMICREATEUMLOGGER WmiCreateUmLoggerThunk;

                MinLength = sizeof(WMICREATEUMLOGGER32) + sizeof(WNODE_HEADER);
                if (InBufferLen < MinLength) {
                    Status = STATUS_INVALID_PARAMETER;
                    OutBufferLen = 0;
                    break;
                }

                Wnode = (PWNODE_HEADER)((PUCHAR)WmiCreateUmLogger32 + sizeof(WMICREATEUMLOGGER32));

                if (Wnode->BufferSize > (InBufferLen-sizeof(WMICREATEUMLOGGER32)) ) {
                    Status = STATUS_INVALID_PARAMETER;
                    OutBufferLen = 0;
                    break;
                }

                SizeNeeded = InBufferLen + sizeof(WMICREATEUMLOGGER) - sizeof(WMICREATEUMLOGGER32);

                SizeNeeded = (SizeNeeded + 7) & ~7;

                WmiCreateUmLoggerThunk = (PWMICREATEUMLOGGER) WmipAlloc(SizeNeeded);

                if (WmiCreateUmLoggerThunk == NULL)
                {
                    Status = STATUS_INSUFFICIENT_RESOURCES;
                    OutBufferLen = 0;
                    break;
                }

                RtlZeroMemory(WmiCreateUmLoggerThunk, SizeNeeded);
                WmiCreateUmLoggerThunk->ObjectAttributes = 
                                        UlongToPtr(WmiCreateUmLogger32->ObjectAttributes);
                WmiCreateUmLoggerThunk->ControlGuid = WmiCreateUmLogger32->ControlGuid;

                dest = (PUCHAR)WmiCreateUmLoggerThunk + sizeof(WMICREATEUMLOGGER);
                src = (PUCHAR)WmiCreateUmLogger32 + sizeof(WMICREATEUMLOGGER32);

                RtlCopyMemory(dest, src, Wnode->BufferSize); 

                Status = WmipCreateUMLogger(WmiCreateUmLoggerThunk);
                WmiCreateUmLogger32->ReplyHandle.Handle64 = 0;
                WmiCreateUmLogger32->ReplyHandle.Handle32 = PtrToUlong(WmiCreateUmLoggerThunk->ReplyHandle.Handle);
                WmiCreateUmLogger32->ReplyCount = WmiCreateUmLoggerThunk->ReplyCount;

                WmipFree(WmiCreateUmLoggerThunk);
            }
            else 
#endif
            {
                MinLength = sizeof(WMICREATEUMLOGGER) + sizeof(WNODE_HEADER);
                if (InBufferLen < MinLength) {
                    Status = STATUS_INVALID_PARAMETER;
                    OutBufferLen = 0;
                    break;
                }

                Wnode = (PWNODE_HEADER) ((PUCHAR)Buffer + sizeof(WMICREATEUMLOGGER));

                if (Wnode->BufferSize > (InBufferLen-sizeof(WMICREATEUMLOGGER)) ) {
                    Status = STATUS_INVALID_PARAMETER;
                    OutBufferLen = 0;
                    break;
                }
                Status = WmipCreateUMLogger((PWMICREATEUMLOGGER)Buffer);
            }

            break;
        }

        case IOCTL_WMI_MB_REPLY:
        {
            //
            // MB Reply message
            //
            PUCHAR Message;
            ULONG MessageSize;
            PWMIMBREPLY WmiMBReply;

            if (InBufferLen >= FIELD_OFFSET(WMIMBREPLY, Message))
            {
                WmiMBReply = (PWMIMBREPLY)Buffer;
                Message = (PUCHAR)Buffer + FIELD_OFFSET(WMIMBREPLY, Message);
                MessageSize = InBufferLen - FIELD_OFFSET(WMIMBREPLY, Message);

                Status = WmipMBReply(WmiMBReply->Handle.Handle,
                                     WmiMBReply->ReplyIndex,
                                     Message,
                                     MessageSize);
            } else {
                Status = STATUS_INVALID_PARAMETER;
            }
            OutBufferLen = 0;
        }


        case IOCTL_WMI_ENABLE_DISABLE_TRACELOG:
        {
            PWMITRACEENABLEDISABLEINFO TraceEnableInfo;

            OutBufferLen = 0;
            if (InBufferLen == sizeof(WMITRACEENABLEDISABLEINFO))
            {
                TraceEnableInfo = (PWMITRACEENABLEDISABLEINFO)Buffer;
                Status = WmipEnableDisableTrace(Ioctl,
                                                TraceEnableInfo);
            } else {
                Status = STATUS_UNSUCCESSFUL;
            }
            break;
        }


        case IOCTL_WMI_START_LOGGER:
        {
            PWMI_LOGGER_INFORMATION LoggerInfo;
#ifdef _WIN64
            ULONG LoggerBuf, LogFileBuf;
#endif

            if ((InBufferLen < sizeof(WMI_LOGGER_INFORMATION)) ||
                (OutBufferLen < sizeof(WMI_LOGGER_INFORMATION)) ) {
                OutBufferLen = 0;
                Status = STATUS_UNSUCCESSFUL;
                break;
            }

            if ( !(Wnode->Flags & WNODE_FLAG_TRACED_GUID) ) {
                OutBufferLen = 0;
                Status = STATUS_UNSUCCESSFUL;
                break;
            }

            LoggerInfo = (PWMI_LOGGER_INFORMATION) Wnode;
            LoggerInfo->Wow = FALSE;
#ifdef _WIN64
            if (IoIs32bitProcess(Irp)) {
                LoggerBuf = ( (PUNICODE_STRING32) &LoggerInfo->LoggerName)->Buffer;
                LoggerInfo->LoggerName.Buffer = UlongToPtr(LoggerBuf);
                LogFileBuf = ( (PUNICODE_STRING32) &LoggerInfo->LogFileName)->Buffer;
                LoggerInfo->LogFileName.Buffer = UlongToPtr(LogFileBuf);
                LoggerInfo->Wow = TRUE;
            }
            else {
                LoggerBuf = 0;
                LogFileBuf = 0;
            }
#endif
            Status = WmipStartLogger( LoggerInfo );
            OutBufferLen = sizeof (WMI_LOGGER_INFORMATION);
#ifdef _WIN64
            if (IoIs32bitProcess(Irp)) {
                ( (PUNICODE_STRING32) &LoggerInfo->LoggerName)->Buffer = LoggerBuf;
                ( (PUNICODE_STRING32) &LoggerInfo->LogFileName)->Buffer = LogFileBuf;
            }
#endif
            break;
        }

        case IOCTL_WMI_STOP_LOGGER:
        {
            PWMI_LOGGER_INFORMATION LoggerInfo;
#ifdef _WIN64
            ULONG LoggerBuf, LogFileBuf;
#endif

            if ((InBufferLen < sizeof(WMI_LOGGER_INFORMATION)) ||
                (OutBufferLen < sizeof(WMI_LOGGER_INFORMATION)) ) {
                OutBufferLen = 0;
                Status = STATUS_UNSUCCESSFUL;
                break;
            }

            if ( !(Wnode->Flags & WNODE_FLAG_TRACED_GUID) ) {
                OutBufferLen = 0;
                Status = STATUS_UNSUCCESSFUL;
                break;
            }

            LoggerInfo = (PWMI_LOGGER_INFORMATION) Wnode;
#ifdef _WIN64
            if (IoIs32bitProcess(Irp)) {
                LoggerBuf = ( (PUNICODE_STRING32) &LoggerInfo->LoggerName)->Buffer;
                LoggerInfo->LoggerName.Buffer = UlongToPtr(LoggerBuf);
                LogFileBuf = ( (PUNICODE_STRING32) &LoggerInfo->LogFileName)->Buffer;
                LoggerInfo->LogFileName.Buffer = UlongToPtr(LogFileBuf);
            }
            else {
                LoggerBuf = 0;
                LogFileBuf = 0;
            }
#endif
            Status = WmiStopTrace( LoggerInfo );
            OutBufferLen = sizeof (WMI_LOGGER_INFORMATION);
#ifdef _WIN64
            if (IoIs32bitProcess(Irp)) {
                ( (PUNICODE_STRING32) &LoggerInfo->LoggerName)->Buffer = LoggerBuf;
                ( (PUNICODE_STRING32) &LoggerInfo->LogFileName)->Buffer = LogFileBuf;
            }
#endif
            break;
        }

        case IOCTL_WMI_QUERY_LOGGER:
        {
            PWMI_LOGGER_INFORMATION LoggerInfo;
#ifdef _WIN64
            ULONG LoggerBuf, LogFileBuf;
#endif

            if ((InBufferLen < sizeof(WMI_LOGGER_INFORMATION)) ||
                (OutBufferLen < sizeof(WMI_LOGGER_INFORMATION)) ) {
                OutBufferLen = 0;
                Status = STATUS_UNSUCCESSFUL;
                break;
            }

            if ( !(Wnode->Flags & WNODE_FLAG_TRACED_GUID) ) {
                OutBufferLen = 0;
                Status = STATUS_UNSUCCESSFUL;
                break;
            }

            LoggerInfo = (PWMI_LOGGER_INFORMATION) Wnode;
#ifdef _WIN64
            if (IoIs32bitProcess(Irp)) {
                LoggerBuf = ( (PUNICODE_STRING32) &LoggerInfo->LoggerName)->Buffer;
                LoggerInfo->LoggerName.Buffer = UlongToPtr(LoggerBuf);
                LogFileBuf = ( (PUNICODE_STRING32) &LoggerInfo->LogFileName)->Buffer;
                LoggerInfo->LogFileName.Buffer = UlongToPtr(LogFileBuf);
            }
            else {
                LoggerBuf = 0;
                LogFileBuf = 0;
            }
#endif
            Status = WmipQueryLogger( LoggerInfo, NULL );
            OutBufferLen = sizeof (WMI_LOGGER_INFORMATION);
#ifdef _WIN64
            if (IoIs32bitProcess(Irp)) {
                ( (PUNICODE_STRING32) &LoggerInfo->LoggerName)->Buffer = LoggerBuf;
                ( (PUNICODE_STRING32) &LoggerInfo->LogFileName)->Buffer = LogFileBuf;
            }
#endif
            break;
        }

        case IOCTL_WMI_UPDATE_LOGGER:
        {
            PWMI_LOGGER_INFORMATION LoggerInfo;
#ifdef _WIN64
            ULONG LoggerBuf, LogFileBuf;
#endif

            if ((InBufferLen < sizeof(WMI_LOGGER_INFORMATION)) ||
                (OutBufferLen < sizeof(WMI_LOGGER_INFORMATION)) ) {
                OutBufferLen = 0;
                Status = STATUS_UNSUCCESSFUL;
                break;
            }

            if ( !(Wnode->Flags & WNODE_FLAG_TRACED_GUID) ) {
                OutBufferLen = 0;
                Status = STATUS_UNSUCCESSFUL;
                break;
            }

            LoggerInfo = (PWMI_LOGGER_INFORMATION) Wnode;
#ifdef _WIN64
            if (IoIs32bitProcess(Irp)) {
                LoggerBuf = ( (PUNICODE_STRING32) &LoggerInfo->LoggerName)->Buffer;
                LoggerInfo->LoggerName.Buffer = UlongToPtr(LoggerBuf);
                LogFileBuf = ( (PUNICODE_STRING32) &LoggerInfo->LogFileName)->Buffer;
                LoggerInfo->LogFileName.Buffer = UlongToPtr(LogFileBuf);
            }
            else {
                LoggerBuf = 0;
                LogFileBuf = 0;
            }
#endif
            Status = WmiUpdateTrace( LoggerInfo );
            OutBufferLen = sizeof (WMI_LOGGER_INFORMATION);
#ifdef _WIN64
            if (IoIs32bitProcess(Irp)) {
                ( (PUNICODE_STRING32) &LoggerInfo->LoggerName)->Buffer = LoggerBuf;
                ( (PUNICODE_STRING32) &LoggerInfo->LogFileName)->Buffer = LogFileBuf;
            }
#endif
            break;
        }

        case IOCTL_WMI_FLUSH_LOGGER:
        {
            PWMI_LOGGER_INFORMATION LoggerInfo;
#ifdef _WIN64
            ULONG LoggerBuf, LogFileBuf;
#endif

            if ((InBufferLen < sizeof(WMI_LOGGER_INFORMATION)) ||
                (OutBufferLen < sizeof(WMI_LOGGER_INFORMATION)) ) {
                OutBufferLen = 0;
                Status = STATUS_UNSUCCESSFUL;
                break;
            }

            if ( !(Wnode->Flags & WNODE_FLAG_TRACED_GUID) ) {
                OutBufferLen = 0;
                Status = STATUS_UNSUCCESSFUL;
                break;
            }

            LoggerInfo = (PWMI_LOGGER_INFORMATION) Wnode;
#ifdef _WIN64
            if (IoIs32bitProcess(Irp)) {
                LoggerBuf = ( (PUNICODE_STRING32) &LoggerInfo->LoggerName)->Buffer;
                LoggerInfo->LoggerName.Buffer = UlongToPtr(LoggerBuf);
                LogFileBuf = ( (PUNICODE_STRING32) &LoggerInfo->LogFileName)->Buffer;
                LoggerInfo->LogFileName.Buffer = UlongToPtr(LogFileBuf);
            }
            else {
                LoggerBuf = 0;
                LogFileBuf = 0;
            }
#endif
            Status = WmiFlushTrace( LoggerInfo );
            OutBufferLen = sizeof (WMI_LOGGER_INFORMATION);
#ifdef _WIN64
            if (IoIs32bitProcess(Irp)) {
                ( (PUNICODE_STRING32) &LoggerInfo->LoggerName)->Buffer = LoggerBuf;
                ( (PUNICODE_STRING32) &LoggerInfo->LogFileName)->Buffer = LogFileBuf;
            }
#endif
            break;
        }

        case IOCTL_WMI_TRACE_EVENT:
        { // NOTE: This relies on WmiTraceEvent to probe the buffer!
            OutBufferLen = 0;
            if ( InBufferLen < sizeof(WNODE_HEADER) ) {
                Status = STATUS_UNSUCCESSFUL;
                break;
            }

            Status = WmiTraceEvent(
                        (PWNODE_HEADER)
                        irpStack->Parameters.DeviceIoControl.Type3InputBuffer,
                        KeGetPreviousMode()
                        );
            break;
        }

        case IOCTL_WMI_TRACE_MESSAGE:
        { // NOTE: This relies on WmiTraceUserMessage to probe the buffer!
            OutBufferLen = 0;
            if ( InBufferLen < sizeof(MESSAGE_TRACE_USER) ) {
                Status = STATUS_UNSUCCESSFUL;
                break;
            }
            Status = WmiTraceUserMessage(
                        (PMESSAGE_TRACE_USER)
                        irpStack->Parameters.DeviceIoControl.Type3InputBuffer,
                        InBufferLen
                        );
            break;
        }

        case IOCTL_WMI_SET_MARK:
        {
            OutBufferLen = 0;
            if ( InBufferLen <= FIELD_OFFSET(WMI_SET_MARK_INFORMATION, Mark)) {
                Status = STATUS_UNSUCCESSFUL;
                break;
            }

            Status = WmiSetMark( (PVOID) Wnode, InBufferLen );
            break;
        }

        case IOCTL_WMI_CLOCK_TYPE:
        {
            if ((InBufferLen < sizeof(WMI_LOGGER_INFORMATION)) ||
                (OutBufferLen < sizeof(WMI_LOGGER_INFORMATION))) {
                OutBufferLen = 0;
                Status = STATUS_INFO_LENGTH_MISMATCH;
                break;
            }

            WmipValidateClockType((PWMI_LOGGER_INFORMATION) Wnode);

            Status = STATUS_SUCCESS;
            break;
        }

        case IOCTL_WMI_NTDLL_LOGGERINFO:
        {

            if ((InBufferLen < sizeof(WMINTDLLLOGGERINFO)) ||
                (OutBufferLen < sizeof(WMINTDLLLOGGERINFO))) {
                OutBufferLen = 0;
                Status = STATUS_UNSUCCESSFUL;
                break;
            }
            
            Status = WmipNtDllLoggerInfo((PWMINTDLLLOGGERINFO)Buffer);

            break;
        }

        default:
        {
            WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: Unsupported IOCTL %x\n",
                     irpStack->Parameters.DeviceIoControl.IoControlCode));

            Status = STATUS_INVALID_DEVICE_REQUEST;

        }
    }

    if (Status != STATUS_PENDING)
    {
        Irp->IoStatus.Status = Status;
        Irp->IoStatus.Information = NT_SUCCESS(Status) ? OutBufferLen : 0;
        IoCompleteRequest( Irp, IO_NO_INCREMENT );
    }

    return(Status);
}

NTSTATUS
WmipSystemControl(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP           Irp
    )
{
    PAGED_CODE();

    return(IoWMISystemControl((PWMILIB_INFO)&WmipWmiLibInfo,
                               DeviceObject,
                               Irp));
}


NTSTATUS WmipWmiIrpCompletion(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp,
    IN PVOID Context
    )
/*++

Routine Description:

    WMI forwarded IRP completion routine. Set an event and return
    STATUS_MORE_PROCESSING_REQUIRED. WmipForwardWmiIrp will wait on this
    event and then re-complete the irp after cleaning up.

Arguments:

    DeviceObject is the device object of the WMI driver
    Irp is the WMI irp that was just completed
    Context is a PKEVENT that WmipForwardWmiIrp will wait on


Return Value:

    NT status code

--*/
{
    PIRPCOMPCTX IrpCompCtx;
    PREGENTRY RegEntry;
    PKEVENT Event;

    UNREFERENCED_PARAMETER(DeviceObject);
    UNREFERENCED_PARAMETER(Irp);

    IrpCompCtx = (PIRPCOMPCTX)Context;
    RegEntry = IrpCompCtx->RegEntry;
    Event = &IrpCompCtx->Event;

    WmipDecrementIrpCount(RegEntry);

    KeSetEvent(Event, IO_NO_INCREMENT, FALSE);

    return(STATUS_MORE_PROCESSING_REQUIRED);
}

NTSTATUS WmipGetDevicePDO(
    PDEVICE_OBJECT DeviceObject,
    PDEVICE_OBJECT *PDO
    )
{
    PIRP Irp;
    PIO_STACK_LOCATION IrpSp;
    IO_STATUS_BLOCK IoStatusBlock;
    PDEVICE_RELATIONS DeviceRelations;
    NTSTATUS Status;
    KEVENT Event;

    PAGED_CODE();

    *PDO = NULL;
    KeInitializeEvent( &Event,
                       NotificationEvent,
                       FALSE );

    Irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP,
                                           DeviceObject,
                                           NULL,
                                           0,
                                           NULL,
                                           &Event,
                                           &IoStatusBlock );

    if (Irp == NULL)
    {
        return(STATUS_INSUFFICIENT_RESOURCES);
    }

    IrpSp = IoGetNextIrpStackLocation( Irp );
    IrpSp->MinorFunction = IRP_MN_QUERY_DEVICE_RELATIONS;
    IrpSp->Parameters.QueryDeviceRelations.Type = TargetDeviceRelation;

    Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;

    Status = IoCallDriver( DeviceObject, Irp );

    if (Status == STATUS_PENDING)
    {
        KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL );
        Status = IoStatusBlock.Status;

    }

    if (NT_SUCCESS(Status))
    {
        DeviceRelations = (PDEVICE_RELATIONS)IoStatusBlock.Information;
        ASSERT(DeviceRelations);
        ASSERT(DeviceRelations->Count == 1);
        *PDO = DeviceRelations->Objects[0];
        ExFreePool(DeviceRelations);
    }
    return(Status);
}

NTSTATUS WmipObjectToPDO(
    PFILE_OBJECT FileObject,
    PDEVICE_OBJECT DeviceObject,
    PDEVICE_OBJECT *PDO
    )
/*++

Routine Description:

    This routine will determine the PDO which is the target of a file handle.
    The mechanism is to build a IRP_MJ_PNP irp with IRP_MN_QUERY_RELATIONS
    and query for TargetDeviceRelation. This irp is supposed to be passed down
    a device stack until it hits the PDO which will fill in its device object
    and return. Note that some drivers may not support this.

Arguments:

    FileObject is the file object for device that is being queried

    DeviceObject is the device object that is being queried

    *PDO returns with the PDO that is targeted by the file object. When
        the caller has finished using the PDO it must ObDereferenceObject it.

Return Value:

    NT status code

--*/
{
    NTSTATUS Status;

    PAGED_CODE();


    if (DeviceObject == NULL)
    {
        DeviceObject = IoGetRelatedDeviceObject(FileObject);
    }

    if (DeviceObject != NULL)
    {
        Status = WmipGetDevicePDO(DeviceObject, PDO);
    } else {
        Status = STATUS_NO_SUCH_DEVICE;
    }
    return(Status);
}


NTSTATUS WmipForwardWmiIrp(
    PIRP Irp,
    UCHAR MinorFunction,
    ULONG ProviderId,
    PVOID DataPath,
    ULONG BufferLength,
    PVOID Buffer
    )
/*++

Routine Description:

    If the provider is a driver then this routine will allocate a new irp
    with the correct stack size and send it to the driver. If the provider
    is a callback then it is called directly.

    It is assumed that the caller has performed any security checks required

Arguments:

    Irp is the IOCTL irp that initiated the request
    MinorFunction specifies the minor function code of the WMI Irp
    WmiRegistrationId is the id passed by the user mode code. This routine
        will look it up to determine the device object pointer.
    DataPath is the value for the DataPath parameter of the WMI irp
    BufferLength is the value for the BufferLength parameter of the WMI irp
    Buffer is the value for the Buffer parameter of the WMI irp

Return Value:

    NT status code

--*/
{
    PREGENTRY RegEntry;
    NTSTATUS Status;
    PIO_STACK_LOCATION irpStack;
    PDEVICE_OBJECT TargetDeviceObject;
    CCHAR DeviceStackSize;
    IRPCOMPCTX IrpCompCtx;
    PWNODE_HEADER Wnode = (PWNODE_HEADER)Buffer;
    LOGICAL IsPnPIdRequest;
    PDEVICE_OBJECT DeviceObject;

    PAGED_CODE();


    WmipAssert( (MinorFunction >= IRP_MN_QUERY_ALL_DATA) &&
                (MinorFunction <= IRP_MN_REGINFO_EX) );

    irpStack = IoGetCurrentIrpStackLocation(Irp);

    //
    // For non-file handle based requests we get the registration entry
    // to validate the target and check for a callback

    RegEntry = WmipFindRegEntryByProviderId(ProviderId, TRUE);

    if (RegEntry != NULL)
    {
        if (RegEntry->Flags & REGENTRY_FLAG_NOT_ACCEPTING_IRPS)
        {
            WmipUnreferenceRegEntry(RegEntry);
            WmipDecrementIrpCount(RegEntry);

            if ((MinorFunction == IRP_MN_QUERY_SINGLE_INSTANCE) ||
                (MinorFunction == IRP_MN_CHANGE_SINGLE_INSTANCE))
            {
                Status = STATUS_WMI_INSTANCE_NOT_FOUND;
            } else {
                Status = STATUS_UNSUCCESSFUL;
            }

            return(Status);
        }

        DeviceObject = RegEntry->DeviceObject;
        
        if (RegEntry->Flags & REGENTRY_FLAG_CALLBACK)
        {
            ULONG Size = 0;
            //
            // This guy registered as a callback so do the callback and go.
            Status = (*RegEntry->WmiEntry)(MinorFunction,
                                           DataPath,
                                           BufferLength,
                                           Buffer,
                                           RegEntry->WmiEntry,
                                           &Size
                                           );
            Irp->IoStatus.Status = Status;
            Irp->IoStatus.Information = Size;

            WmipUnreferenceRegEntry(RegEntry);
            WmipDecrementIrpCount(RegEntry);

            return(Status);
        }

    } else {
        WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: Invalid device object passed from user mode %x\n",
             ProviderId));
        if ((MinorFunction == IRP_MN_QUERY_SINGLE_INSTANCE) ||
            (MinorFunction == IRP_MN_CHANGE_SINGLE_INSTANCE))
        {
            Status = STATUS_WMI_INSTANCE_NOT_FOUND;
        } else {
            Status = STATUS_UNSUCCESSFUL;
        }

        return(Status);
    }

    //
    // Determine if this is a query for the device pnp id guid
    IsPnPIdRequest = ((MinorFunction != IRP_MN_REGINFO) &&
                      (MinorFunction != IRP_MN_REGINFO_EX)) &&
                  ((IsEqualGUID(&Wnode->Guid, &WmipDataProviderPnpidGuid)) ||
                   (IsEqualGUID(&Wnode->Guid, &WmipDataProviderPnPIdInstanceNamesGuid)));
    if (IsPnPIdRequest && (RegEntry->PDO != NULL))
    {
        //
        // Its the PnPId request and WMI is handling it on behalf of the
        // device then switch the device object to our own
        DeviceObject = WmipServiceDeviceObject;
        IsPnPIdRequest = FALSE;
    }

    //
    // Get the top of the device stack for our target WMI device. Note that
    // IoGetAttachedDeviceReference also takes an object reference
    // which we get rid of after the the irp is completed by the
    // data provider driver.
    TargetDeviceObject = IoGetAttachedDeviceReference(DeviceObject);
    DeviceStackSize = TargetDeviceObject->StackSize + 1;

    //
    // Check that there are enough stack locations in our irp so that we
    // can forward it to the top of the device stack. We must also check
    // if our target device is the WMI data or service device otherwise
    // the number of stack locations for it will keep incrementing until
    // the machine crashes
    if ((DeviceStackSize <= WmipServiceDeviceObject->StackSize) ||
        (TargetDeviceObject == WmipServiceDeviceObject))
    {
        //
        // There are enough stack locations in the WMI irp to forward
        // Remember some context information in our irp stack and use
        // it as our completion context value

        KeInitializeEvent( &IrpCompCtx.Event,
                       SynchronizationEvent,
                       FALSE );

        IrpCompCtx.RegEntry = RegEntry;

        IoSetCompletionRoutine(Irp,
                                   WmipWmiIrpCompletion,
                                   (PVOID)&IrpCompCtx,
                                   TRUE,
                                   TRUE,
                                   TRUE);

        //
        // Setup next irp stack location with WMI irp info
        irpStack = IoGetNextIrpStackLocation(Irp);
        irpStack->MajorFunction = IRP_MJ_SYSTEM_CONTROL;
        irpStack->MinorFunction = MinorFunction;
        irpStack->Parameters.WMI.ProviderId = (ULONG_PTR)DeviceObject;
        irpStack->Parameters.WMI.DataPath = DataPath;
        irpStack->Parameters.WMI.BufferSize = BufferLength;
        irpStack->Parameters.WMI.Buffer = Buffer;

        //
        // Initialize irp status to STATUS_NOT_SUPPORTED so that we can
        // detect the case where no data provider responded to the irp
        Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;

        IoMarkIrpPending(Irp);
        Status = IoCallDriver(TargetDeviceObject, Irp);

        if (Status == STATUS_PENDING) {
             KeWaitForSingleObject( &IrpCompCtx.Event,
                                    Executive,
                                    KernelMode,
                                    FALSE,
                                    (PLARGE_INTEGER) NULL );
             Status = Irp->IoStatus.Status;
        }

        //
        // Check if the status code is still STATUS_NOT_SUPPORTED. If this is
        // the case then most likely no data provider responded to the irp.
        // So we want to change the status code to something more relevant
        // to WMI like STATUS_WMI_GUID_NOT_FOUND
        if (Status == STATUS_NOT_SUPPORTED)
        {
            Status = STATUS_WMI_GUID_NOT_FOUND;
            Irp->IoStatus.Status = STATUS_WMI_GUID_NOT_FOUND;
        }

#if DBG
        if (((MinorFunction == IRP_MN_REGINFO) || (MinorFunction == IRP_MN_REGINFO_EX))  &&
            (NT_SUCCESS(Status)) &&
            (Irp->IoStatus.Information == 0))
        {
            WmipDebugPrintEx((DPFLTR_WMICORE_ID, DPFLTR_INFO_LEVEL,"WMI: %p completed IRP_MN_REGINFO with size 0 (%p, %x)\n",
                     DeviceObject, Buffer, BufferLength));
        }
#endif

        //
        // If this was a registration request then we need to see if there are
        // any PDOs that need to be translated into static instance names.
        if (((MinorFunction == IRP_MN_REGINFO) ||
             (MinorFunction == IRP_MN_REGINFO_EX)) &&
            (NT_SUCCESS(Status)) &&
            (Irp->IoStatus.Information > FIELD_OFFSET(WMIREGINFOW,
                                                      WmiRegGuid)))
        {
            WmipTranslatePDOInstanceNames(Irp,
                                          MinorFunction,
                                          BufferLength,
                                          RegEntry);
        }

        //
        // Dereference regentry which was taken when forwarding the irp
        WmipUnreferenceRegEntry(RegEntry);
    } else {
        //
        // There are not enough stack locations to forward this irp.
        // We bump the stack count for the WMI device and return
        // an error asking to try the irp again.
        WmipUnreferenceRegEntry(RegEntry);
        WmipDecrementIrpCount(RegEntry);

        WmipUpdateDeviceStackSize(DeviceStackSize);
        Status = STATUS_WMI_TRY_AGAIN;
    }

    //
    // Dereference the target device which was the top of the stack to
    // which we forwarded the irp.
    ObDereferenceObject(TargetDeviceObject);

    return(Status);
}

NTSTATUS WmipSendWmiIrp(
    UCHAR MinorFunction,
    ULONG ProviderId,
    PVOID DataPath,
    ULONG BufferLength,
    PVOID Buffer,
    PIO_STATUS_BLOCK Iosb
    )
/*++

Routine Description:

    This routine will allocate a new irp and then forward it on as a WMI
    irp appropriately. The routine handles the case where the stack size
    is too small and will retry the irp.

Arguments:

    See WmipForwardWmiIrp

Return Value:

    NT status code

--*/
{
    PIRP Irp;
    PIO_STACK_LOCATION IrpStack;
    NTSTATUS Status;

    PAGED_CODE();

    Irp = NULL;
    do
    {
           Irp = IoAllocateIrp((CCHAR)(WmipServiceDeviceObject->StackSize+1),
                            FALSE);

        if (Irp == NULL)
        {
            return(STATUS_INSUFFICIENT_RESOURCES);
        }

        IoSetNextIrpStackLocation(Irp);
        IrpStack = IoGetCurrentIrpStackLocation(Irp);
        IrpStack->DeviceObject = WmipServiceDeviceObject;
        Irp->Tail.Overlay.Thread = PsGetCurrentThread();
        Irp->AssociatedIrp.SystemBuffer = Buffer;

        Status = WmipForwardWmiIrp(
                                   Irp,
                                   MinorFunction,
                                   ProviderId,
                                   DataPath,
                                   BufferLength,
                                   Buffer);

        *Iosb = Irp->IoStatus;

        IoFreeIrp(Irp);
    } while (Status == STATUS_WMI_TRY_AGAIN);

    return(Status);
}


NTSTATUS WmipTranslateFileHandle(
    IN OUT PWMIFHTOINSTANCENAME FhToInstanceName,
    IN OUT PULONG OutBufferLen,
    IN HANDLE FileHandle,
    IN PDEVICE_OBJECT DeviceObject,
    IN PWMIGUIDOBJECT GuidObject,
    OUT PUNICODE_STRING InstanceNameString
    )
/*++

Routine Description:

    This routine will translate a file handle or device object into the
    device instance name for the target PDO of the device object
    pointed to by the file handle.

Arguments:

    FhToInstanceName passes in the file handle and returns the device
        instance name.

Return Value:

    NT status code

--*/
{
    PDEVICE_OBJECT PDO;
    UNICODE_STRING DeviceInstanceName;
    PFILE_OBJECT FileObject = NULL;
    NTSTATUS Status;
    ULONG SizeNeeded;
    PWCHAR InstanceName;
    ULONG Length;
    PWCHAR HandleName;
    ULONG HandleNameLen;
    PWCHAR BaseName;
    SIZE_T BaseNameLen;
    PBGUIDENTRY GuidEntry;
    PLIST_ENTRY InstanceSetList;
    PBINSTANCESET InstanceSet;
    ULONG BaseIndex;

    PAGED_CODE();


    if (FhToInstanceName != NULL)
    {
        WmipAssert(FileHandle == NULL);
        WmipAssert(GuidObject == NULL);
        WmipAssert(InstanceNameString == NULL);
        WmipAssert(DeviceObject == NULL);
        FileHandle = FhToInstanceName->FileHandle.Handle;
        if (FileHandle == NULL)
        {
            return(STATUS_INVALID_HANDLE);
        }
    }

    if (FileHandle != NULL)
    {
        //
        // Make reference to the file object so it doesn't go away
        //
        Status = ObReferenceObjectByHandle(FileHandle,
                                           0,
                                           IoFileObjectType,
                                           KernelMode,
                                           &FileObject,
                                           NULL);
    } else {
        //
        // Make reference to the device object so it doesn't go away
        //
        Status = ObReferenceObjectByPointer(DeviceObject,
                                            FILE_ALL_ACCESS,
                                            NULL,
                                            KernelMode);
    }

    if (NT_SUCCESS(Status))
    {
        Status = WmipObjectToPDO(FileObject,
                                 DeviceObject,
                                 &PDO);
        if (NT_SUCCESS(Status))
        {
            //
            // Map file object to PDO
            Status = WmipPDOToDeviceInstanceName(PDO,
                                                 &DeviceInstanceName);
            if (NT_SUCCESS(Status))
            {
                //
                // Now see if we can find an instance name
                //
                HandleName = DeviceInstanceName.Buffer;
                HandleNameLen = DeviceInstanceName.Length / sizeof(WCHAR);
                if (FhToInstanceName != NULL)
                {
                    Status = ObReferenceObjectByHandle(FhToInstanceName->KernelHandle.Handle,
                                                       WMIGUID_QUERY,
                                                       WmipGuidObjectType,
                                                       UserMode,
                                                       &GuidObject,
                                                       NULL);
                } else {
                    Status = ObReferenceObjectByPointer(GuidObject,
                                                        WMIGUID_QUERY,
                                                        WmipGuidObjectType,
                                                        KernelMode);
                }

                if (NT_SUCCESS(Status))
                {
                    Status = STATUS_WMI_INSTANCE_NOT_FOUND;
                    GuidEntry = GuidObject->GuidEntry;
                    BaseIndex = 0;

                    WmipEnterSMCritSection();
                    if (GuidEntry->ISCount > 0)
                    {
                        InstanceSetList = GuidEntry->ISHead.Flink;
                        while ((InstanceSetList != &GuidEntry->ISHead) &&
                               ! NT_SUCCESS(Status))
                        {
                            InstanceSet = CONTAINING_RECORD(InstanceSetList,
                                                        INSTANCESET,
                                                        GuidISList);
                            if (InstanceSet->Flags & IS_INSTANCE_BASENAME)
                            {
                                BaseName = InstanceSet->IsBaseName->BaseName;
                                BaseNameLen = wcslen(BaseName);

                                //
                                // If the instance set has a base name
                                // and the beginning of it matches the
                                // PnPId and it has only an _ after it
                                // then we have got a match
                                //
                                if ((_wcsnicmp(BaseName,
                                              HandleName,
                                              HandleNameLen) == 0) &&
                                    (BaseNameLen == (HandleNameLen+1)) &&
                                    (BaseName[BaseNameLen-1] == L'_'))
                                {
                                    BaseIndex = InstanceSet->IsBaseName->BaseIndex;
                                    Status = STATUS_SUCCESS;
                                }
                            }
                            InstanceSetList = InstanceSetList->Flink;
                        }
                    }

                    WmipLeaveSMCritSection();

                    if (NT_SUCCESS(Status))
                    {
                        if (FhToInstanceName != NULL)
                        {
                            FhToInstanceName->BaseIndex = BaseIndex;
                            SizeNeeded = DeviceInstanceName.Length + 2 * sizeof(WCHAR) +
                                  FIELD_OFFSET(WMIFHTOINSTANCENAME,
                                               InstanceNames);
                            if (*OutBufferLen >= SizeNeeded)
                            {
                                InstanceName = &FhToInstanceName->InstanceNames[0];
                                Length = DeviceInstanceName.Length;

                                FhToInstanceName->InstanceNameLength = (USHORT)(Length + 2 * sizeof(WCHAR));
                                RtlCopyMemory(InstanceName,
                                              DeviceInstanceName.Buffer,
                                              DeviceInstanceName.Length);

                                //
                                // Double NUL terminate string
                                //
                                Length /= 2;
                                InstanceName[Length++] = UNICODE_NULL;
                                InstanceName[Length] = UNICODE_NULL;

                                *OutBufferLen = SizeNeeded;
                            } else if (*OutBufferLen >= sizeof(ULONG)) {
                                FhToInstanceName->SizeNeeded = SizeNeeded;
                                *OutBufferLen = sizeof(ULONG);
                            } else {
                                Status = STATUS_UNSUCCESSFUL;
                            }
                        } else {
                            InstanceNameString->MaximumLength = DeviceInstanceName.Length + 32;
                            InstanceName = ExAllocatePoolWithTag(PagedPool,
                                                                 InstanceNameString->MaximumLength,
                                                                 WmipInstanceNameTag);
                            if (InstanceName != NULL)
                            {
                                StringCbPrintf(InstanceName,
                                                    InstanceNameString->MaximumLength,
                                                    L"%ws_%d",
                                                    DeviceInstanceName.Buffer,
                                                    BaseIndex);
                                InstanceNameString->Buffer = InstanceName;
                                InstanceNameString->Length = (USHORT)wcslen(InstanceName) * sizeof(WCHAR);
                            } else {
                                Status = STATUS_INSUFFICIENT_RESOURCES;
                            }
                        }
                    }

                    ObDereferenceObject(GuidObject);
                }
                RtlFreeUnicodeString(&DeviceInstanceName);
            }
            ObDereferenceObject(PDO);
        }

        if (FileHandle != NULL)
        {
            ObDereferenceObject(FileObject);
        } else {
            ObDereferenceObject(DeviceObject);
        }
    }
    return(Status);
}

BOOLEAN
WmipFastIoDeviceControl(
    IN PFILE_OBJECT FileObject,
    IN BOOLEAN Wait,
    IN PVOID InputBuffer OPTIONAL,
    IN ULONG InputBufferLength,
    OUT PVOID OutputBuffer OPTIONAL,
    IN ULONG OutputBufferLength,
    IN ULONG IoControlCode,
    OUT PIO_STATUS_BLOCK IoStatus,
    IN struct _DEVICE_OBJECT *DeviceObject
    )
{
    PAGED_CODE();

    UNREFERENCED_PARAMETER(FileObject);
    UNREFERENCED_PARAMETER(Wait);
    UNREFERENCED_PARAMETER(OutputBuffer);
    UNREFERENCED_PARAMETER(OutputBufferLength);
    UNREFERENCED_PARAMETER(DeviceObject);

    if (IoControlCode == IOCTL_WMI_TRACE_EVENT) {
        if (InputBufferLength < sizeof(EVENT_TRACE_HEADER))
            return FALSE;

        IoStatus->Status = WmiTraceEvent( InputBuffer, KeGetPreviousMode() );
        return TRUE;
    } else if (IoControlCode == IOCTL_WMI_TRACE_MESSAGE) {
        if (InputBufferLength < sizeof(MESSAGE_TRACE_USER))
            return FALSE;

        IoStatus->Status = WmiTraceUserMessage( InputBuffer, InputBufferLength );
        return TRUE;
    }
    return FALSE;
}

NTSTATUS WmipProbeWnodeWorker(
    PWNODE_HEADER WnodeHeader,
    ULONG MinWnodeSize,
    ULONG InstanceNameOffset,
    ULONG DataBlockOffset,
    ULONG DataBlockSize,
    ULONG InBufferLen,
    ULONG OutBufferLen,
    BOOLEAN CheckOutBound,
    BOOLEAN CheckInBound
    )
/*++

Routine Description:

    Probe the incoming Wnode to ensure that any offsets in the
    header point to memory that is valid within the buffer. Also validate
    that the Wnode is properly formed.

    This routine assumes that the input and output buffers has been
    probed enough to determine that it is at least as large as
    MinWnodeSize and MinWnodeSize must be at least as large as
    sizeof(WNODE_HEADER)

    WNODE Rules:

    9. For outbound data WnodeDataBlockOffset != 0
    5. For inbound Wnode->DataBlockOffset must be 0 (implying no data) or
       Wnode->DataBlockOffset must be <= incoming buffer size and >=
       sizeof(WNODE_SINGLE_INSTANCE), that is
       the data block must start in the incoming buffer, but after the
       WNODE_SINGLE_INSTANCE header.
    6. Wnode and Wnode->DataBlockOffset must be aligned on an 8 byte boundray.
    7. For inbound data (SetSingleInstance) (Wnode->DataBlockOffset +
       Wnode->DataBlockSize) < incoming buffer length. That is the entire
       data block must fit within the incoming buffer.
    8. For outbound data (QuerySingleInstance) Wnode->DataBlockOffset
       must be <= outgoing buffer length. That is the start of the outgoing
       data block must fit within the outgoing data buffer. Note that it is
       the provider's responsibility to determine if there will be enough
       space in the outgoing buffer to write the returned data.

    10. Wnode->OffsetInstanceNames must be aligned on a 2 byte boundary
    11. Wnode->OffsetInstanceNames must be <= (incoming buffer size) +
        sizeof(USHORT), that is it must start within the incoming buffer and
        the USHORT that specifies the length must be within the incoming
        buffer.
    12. The entire instance name string must fit with the incoming buffer
    13. For outbound data (QuerySingleInstance) the entire instance name
        must start and fit within the output buffer.
    14. Wnode->DataBlockOffset must be placed after any instance name and
        not overlap the instance name.



Arguments:

    WnodeHeader - pointer to WNODE to be probed

    InBufferLen - Size of the incoming buffer

    OutBufferLen - Size of the outgoing buffer

    MinWnodeSize - minimum size that the WNODE can be

    InstanceNameOffset - Offset within WNODE to instance name

    DataBlockOffset - Offset within WNODE to data block

     DataBlockSize - Size of data block

    CheckOutBound - If TRUE, WNODE needs to be validated for provider to
                    return data.

    CheckInBound - If TRUE WNODE needs to be validated for provider to
                   receive data

Return Value:

    NT status code

--*/
{
    NTSTATUS Status = STATUS_SUCCESS;
    PWCHAR InstanceNamePtr;

    PAGED_CODE();

    if (InstanceNameOffset != 0)
    {
        //
        // Validate instance name begins beyond WNODE header
        if (InstanceNameOffset < MinWnodeSize)
        {
            return(STATUS_UNSUCCESSFUL);
        }

        //
        // Validate InstanceName is aligned properly. This is left
        // in the free build since alphas may have alignment requiremnts
        // in handling USHORTs and WCHARs

        //
        // Validate that USHORT holding instance name length is within
        // WNODE
        if (( ! WmipIsAligned(InstanceNameOffset, 2)) ||
            (InstanceNameOffset > InBufferLen - sizeof(USHORT)) )
        {
            return(STATUS_UNSUCCESSFUL);
        }

        //
        // Validate Dynamic Instance Name text is fully within
        // input buffer and output buffer for outbound WNODEs
        InstanceNamePtr = (PWCHAR)OffsetToPtr(WnodeHeader,
                                                  InstanceNameOffset);
        InstanceNameOffset += sizeof(USHORT) + *InstanceNamePtr;
        if ( (InstanceNameOffset > InBufferLen) ||
             ( (CheckOutBound) && (InstanceNameOffset > OutBufferLen)) )
        {
            return(STATUS_UNSUCCESSFUL);
        }

        //
        // If data block is specified then it must be placed after the
        // end of the instance name
        if ((DataBlockOffset != 0) &&
            (DataBlockOffset < InstanceNameOffset))
        {
            return(STATUS_UNSUCCESSFUL);
        }

    }

    //
    // Ensure data block offset is placed after the WNODE header
    // header
    if ((DataBlockOffset != 0) &&
        (DataBlockOffset < MinWnodeSize))
    {
        return(STATUS_UNSUCCESSFUL);
    }

    //
    // Ensure data block is aligned properly
    if (! WmipIsAligned(DataBlockOffset, 8))
    {
        return(STATUS_UNSUCCESSFUL);
    }

    //
    // For incoming WNODE, make sure the data block
    // does not extend beyond the input buffer.
    if ((CheckInBound) &&
        (DataBlockOffset != 0) &&
        ( (DataBlockSize > InBufferLen) ||
          (DataBlockOffset > InBufferLen - DataBlockSize) ) )
    {
        return(STATUS_UNSUCCESSFUL);
    }

    if (CheckOutBound)
    {
        //
        // For outgoing WNODE make sure there is
        // enough room to write the WNODE header

        //
        // For outgoing WNODE make sure the data block
        // offset is within the bounds of the output buffer
        if ( (OutBufferLen < MinWnodeSize) ||
             (DataBlockOffset > OutBufferLen) )
        {
            return(STATUS_UNSUCCESSFUL);
        }

        //
        // Make sure that the data block offset is specified so provider
        // can know where to write data
        if (DataBlockOffset == 0)
        {
            return(STATUS_UNSUCCESSFUL);
        }
    }

    return(Status);
}
NTSTATUS WmipProbeWnodeAllData(
    PWNODE_ALL_DATA Wnode,
    ULONG InBufferLen,
    ULONG OutBufferLen
    )
/*++

Routine Description:

    Probe the incoming WNODE_ALL_DATA to ensure that any offsets in the
    header point to memory that is valid within the buffer. Also validate
    that the WNODE_ALL_DATA is properly formed.

    This routine MUST succeed before any fields in the WNODE_ALL_DATA can be
    used by any  kernel components when passed in from user mode. Note that
    we can trust that the input and output buffer are properly sized since
    the WMI IOCTLs are METHOD_BUFFERED and the IO manager does that for us.


    WNODE_ALL_DATA_RULES:

    1. Wnode is aligned on a 8 byte boundary
    2. The incoming buffer must be at least as large as sizeof(WNODE_HEADER)
    3. The outgoing buffer must be at least as large as sizeof(WNODE_ALL_DATA)
    5. WnodeHeader->BufferSize must equal incoming buffer size

Arguments:

    Wnode - WNODE_ALL_DATA to be validated

    InBufferLen - Size of the incoming buffer

    OutBufferLen - Size of the outgoing buffer


Return Value:

    NT status code

--*/
{
    NTSTATUS Status;
    PWNODE_HEADER WnodeHeader = (PWNODE_HEADER)Wnode;

    PAGED_CODE();

    //
    // Io is supposed to guarantee this
    //
    WmipAssert(WmipIsAligned(Wnode, 8));
    
    //
    // Make sure that enough of the WNODE_ALL_DATA was passed so that we
    // can look at it and the drivers can fill it in
    //
    if (OutBufferLen < sizeof(WNODE_ALL_DATA))
    {
        return(STATUS_UNSUCCESSFUL);
    }

    Status = WmipValidateWnodeHeader(WnodeHeader,
                                         InBufferLen,
                                         sizeof(WNODE_HEADER),
                                         WNODE_FLAG_ALL_DATA,
                                         0xffffff7e);
    return(Status);
}

NTSTATUS WmipProbeWnodeSingleInstance(
    PWNODE_SINGLE_INSTANCE Wnode,
    ULONG InBufferLen,
    ULONG OutBufferLen,
    BOOLEAN OutBound
    )
/*++

Routine Description:

    Probe the incoming WNODE_SINGLE_INSTANCE to ensure that any offsets in the
    header point to memory that is valid within the buffer. Also validate
    that the WNODE_SINGLE_INSTANCE is properly formed.

    This routine MUST succeed before any fields in the WNODE_SINGLE_INSTANCE
    can be used by any  kernel components when passed in from user mode.
    Note that we can trust that the input and output buffer are properly
    sized since the WMI IOCTLs are METHOD_BUFFERED and the IO manager does
    that for us.

    WNODE_SINGLE_INSTANCE Rules:

    1. The incoming buffer must be at least as large as
       sizeof(WNODE_SINGLE_INSTANCE)
    2. The outgoing buffer must be at least as large as
       sizeof(WNODE_SINGLE_INSTANCE)
    3. WnodeHeader->ProviderId must be non null, Actual value validated when
       irp is forwarded.
    4. WnodeHeader->BufferSize must equal incoming buffer size
    5. Wnode->DataBlockOffset must be 0 (implying no data) or
       Wnode->DataBlockOffset must be <= incoming buffer size and >=
       sizeof(WNODE_SINGLE_INSTANCE), that is
       the data block must start in the incoming buffer, but after the
       WNODE_SINGLE_INSTANCE header.
    6. Wnode and Wnode->DataBlockOffset must be aligned on an 8 byte boundary.
    7. For inbound data (SetSingleInstance) (Wnode->DataBlockOffset +
       Wnode->DataBlockSize) <= incoming buffer length. That is the entire
       data block must fit within the incoming buffer.
    8. For outbound data (QuerySingleInstance) Wnode->DataBlockOffset
       must be <= outgoing buffer length. That is the start of the outgoing
       data block must fit within the outgoing data buffer. Note that it is
       the provider's responsibility to determine if there will be enough
       space in the outgoing buffer to write the returned data.
    9. For outbound data (QuerySingleInstance) WnodeDataBlockOffset != 0

    10. Wnode->OffsetInstanceNames must be aligned on a 2 byte boundary
    11. Wnode->OffsetInstanceNames + sizeof(USHORT) must be <= incoming
        buffer size, that is it must start within the incoming buffer and
        the USHORT that specifies the length must be within the incoming
        buffer.
    12. The entire instance name string must fit with the incoming buffer
    13. For outbound data (QuerySingleInstance) the entire instance name
        must start and fit within the output buffer.
    14. Wnode->DataBlockOffset must be placed after any instance name and
        not overlap the instance name.



Arguments:

    Wnode - WNODE_SINGLE_INSTANCE to be validated

    InBufferLen - Size of the incoming buffer

    OutBufferLen - Size of the outgoing buffer

    OutBound - If FALSE, WNODE_SINGLE_INSTANCE has inbound data that must be
              validated to be within the input buffer. If FALSE,
              WNODE_SINGLE_INSTANCE is expected to be filled with data
              by the driver so ensure that data buffer is validated to
              be within the output buffer.

Return Value:

    NT status code

--*/
{
    PWNODE_HEADER WnodeHeader = (PWNODE_HEADER)Wnode;
    NTSTATUS Status;

    PAGED_CODE();

    //
    // Io makes sure WNODE is on a 8 byte boundary
    //
    WmipAssert(WmipIsAligned((PUCHAR)Wnode, 8));

    //
    // Make sure that enough of the WNODE_SINGLE_INSTANCE was passed
    // so that we can look at it
    //
    if ((InBufferLen < FIELD_OFFSET(WNODE_SINGLE_INSTANCE, VariableData)) ||
        ( (OutBound) && (OutBufferLen < FIELD_OFFSET(WNODE_SINGLE_INSTANCE,
                                                     VariableData))))
    {
        return(STATUS_UNSUCCESSFUL);
    }


    Status = WmipProbeWnodeWorker(WnodeHeader,
                                  FIELD_OFFSET(WNODE_SINGLE_INSTANCE,
                                               VariableData),
                                  Wnode->OffsetInstanceName,
                                  Wnode->DataBlockOffset,
                                  Wnode->SizeDataBlock,
                                  InBufferLen,
                                  OutBufferLen,
                                  OutBound,
                                  (BOOLEAN)(! OutBound));

    if (NT_SUCCESS(Status))
    {
        Status = WmipValidateWnodeHeader(WnodeHeader,
                                 InBufferLen,
                                 FIELD_OFFSET(WNODE_SINGLE_INSTANCE,
                                              VariableData),
                                 WNODE_FLAG_SINGLE_INSTANCE,
                                 0xffffff7d);
    }

    return(Status);
}

NTSTATUS WmipProbeWnodeSingleItem(
    PWNODE_SINGLE_ITEM Wnode,
    ULONG InBufferLen
    )
/*++

Routine Description:

    Probe the incoming WNODE_SINGLE_ITEM to ensure that any offsets in the
    header point to memory that is valid within the buffer. Also validate
    that the WNODE_SINGLE_ITEM is properly formed.

    This routine MUST succeed before any fields in the WNODE_SINGLE_INSTANCE
    can be used by any  kernel components when passed in from user mode.
    Note that we can trust that the input and output buffer are properly
    sized since the WMI IOCTLs are METHOD_BUFFERED and the IO manager does
    that for us.

WNODE_SINGLE_ITEM rules:

    1. The incoming buffer must be at least as large as
       sizeof(WNODE_SINGLE_ITEM)
    2. The outgoing buffer must be at least as large as
       sizeof(WNODE_SINGLE_ITEM)
    3. WnodeHeader->ProviderId must be non null, Actual value validated when
       irp is forwarded.
    4. WnodeHeader->BufferSize must equal incoming buffer size
    5. Wnode->DataBlockOffset must be 0 (implying no data) or
       Wnode->DataBlockOffset must be <= incoming buffer size and >=
       sizeof(WNODE_SINGLE_ITEM), that is
       the data block must start in the incoming buffer, but after the
       WNODE_SINGLE_ITEM header.
    6. Wnode and Wnode->DataBlockOffset must be aligned on an 8 byte boundary.
    7. (Wnode->DataBlockOffset + Wnode->SizeDataItem) <
       incoming buffer length. That is the entire
       data block must fit within the incoming buffer.
    8. Wnode->DataItemId must not be 0

    9. Wnode->OffsetInstanceNames must be aligned on a 2 byte boundary
    10. Wnode->OffsetInstanceNames must be <= (incoming buffer size) +
        sizeof(USHORT), that is it must start within the incoming buffer and
        the USHORT that specifies the length must be within the incoming
        buffer.
    11. The entire instance name string must fit with the incoming buffer
    12. Wnode->DataBlockOffset must be placed after any instance name and
        not overlap the instance name.

Arguments:

    Wnode - WNODE_SINGLE_ITEM to be validated

    InBufferLen - Size of the incoming buffer

Return Value:

    NT status code

--*/
{
    PWNODE_HEADER WnodeHeader = (PWNODE_HEADER)Wnode;
    NTSTATUS Status;

    PAGED_CODE();

    //
    // Io Makes sure WNODE is on a 8 byte boundary
    //
    WmipAssert(WmipIsAligned((PUCHAR)Wnode, 8));

    //
    // Make sure that enough of the WNODE_SINGLE_ITEM was passed
    // so that we can look at it
    //
    if (InBufferLen < FIELD_OFFSET(WNODE_SINGLE_ITEM, VariableData))
    {
        return(STATUS_UNSUCCESSFUL);
    }


    //
    // We don't use sizeof(WNODE_SINGLE_ITEM), but rather use the offset
    // to the variable data since in the case of WNODE_SINGLE_ITEM they are
    // different. The C compiler will round up the packing to 8 bytes so
    // the former is 48 and the later is 44.
    //
    Status = WmipProbeWnodeWorker(WnodeHeader,
                                  (ULONG)((ULONG_PTR)(&((PWNODE_SINGLE_ITEM)0)->VariableData)),
                                  Wnode->OffsetInstanceName,
                                  Wnode->DataBlockOffset,
                                  Wnode->SizeDataItem,
                                  InBufferLen,
                                  0,
                                  FALSE,
                                  TRUE);

    if (NT_SUCCESS(Status))
    {
        Status = WmipValidateWnodeHeader(WnodeHeader,
                                     InBufferLen,
                                     FIELD_OFFSET(WNODE_SINGLE_ITEM,
                                                  VariableData),
                                     WNODE_FLAG_SINGLE_ITEM,
                                     0xffffff7b);
    }

    return(Status);
}


NTSTATUS WmipProbeWnodeMethodItem(
    PWNODE_METHOD_ITEM Wnode,
    ULONG InBufferLen,
    ULONG OutBufferLen
    )
/*++

Routine Description:

    Probe the incoming WNODE_METHOD_ITEM to ensure that any offsets in the
    header point to memory that is valid within the buffer. Also validate
    that the WNODE_METHOD_ITEM is properly formed.

    This routine MUST succeed before any fields in the WNODE_METHOD_INSTANCE
    can be used by any  kernel components when passed in from user mode.
    Note that we can trust that the input and output buffer are properly
    sized since the WMI IOCTLs are METHOD_BUFFERED and the IO manager does
    that for us.

    WNODE_METHOD_ITEM Rules:

    1. The incoming buffer must be at least as large as
       sizeof(WNODE_METHOD_ITEM)
    2. The outgoing buffer must be at least as large as
       sizeof(WNODE_METHOD_ITEM)
    3. WnodeHeader->ProviderId must be non null, Actual value validated when
       irp is forwarded and Wnode->MethodId must not be 0
    4. WnodeHeader->BufferSize must equal incoming buffer size
    5. Wnode->DataBlockOffset must be 0 (implying no data) or
       Wnode->DataBlockOffset must be <= incoming buffer size and >=
       sizeof(WNODE_METHOD_ITEM), that is
       the data block must start in the incoming buffer, but after the
       WNODE_METHOD_ITEM header.
    6. Wnode and Wnode->DataBlockOffset must be aligned on an 8 byte boundary.
    7. For inbound data (Wnode->DataBlockOffset +
       Wnode->DataBlockSize) < incoming buffer length. That is the entire
       data block must fit within the incoming buffer.
    8. For outbound data Wnode->DataBlockOffset
       must be <= outgoing buffer length. That is the start of the outgoing
       data block must fit within the outgoing data buffer. Note that it is
       the provider's responsibility to determine if there will be enough
       space in the outgoing buffer to write the returned data.
    9. WnodeDataBlockOffset != 0

    10. Wnode->OffsetInstanceNames must be aligned on a 2 byte boundary
    11. Wnode->OffsetInstanceNames must be <= (incoming buffer size) +
        sizeof(USHORT), that is it must start within the incoming buffer and
        the USHORT that specifies the length must be within the incoming
        buffer.
    12. The entire instance name string must fit with the incoming buffer
    13. For outbound data the entire instance name
        must start and fit within the output buffer.
    14. Wnode->DataBlockOffset must be placed after any instance name and
        not overlap the instance name.


Arguments:

    Wnode - WNODE_METHOD_ITEM to be validated

    InBufferLen - Size of the incoming buffer

    OutBufferLen - Size of the Output buffer

Return Value:

    NT status code

--*/
{
    PWNODE_HEADER WnodeHeader = (PWNODE_HEADER)Wnode;
    NTSTATUS Status;

    PAGED_CODE();

    //
    // Make sure WNODE is on a 8 byte boundary
    //
    WmipAssert(WmipIsAligned((PUCHAR)Wnode, 8));

    //
    // Make sure that enough of the WNODE_METHOD_ITEM was passed
    // so that we can look at it
    //
    if (InBufferLen < FIELD_OFFSET(WNODE_METHOD_ITEM, VariableData))
    {
        return(STATUS_UNSUCCESSFUL);
    }

    Status = WmipProbeWnodeWorker(WnodeHeader,
                                  (ULONG)((ULONG_PTR)(&((PWNODE_METHOD_ITEM)0)->VariableData)),
                                  Wnode->OffsetInstanceName,
                                  Wnode->DataBlockOffset,
                                  Wnode->SizeDataBlock,
                                  InBufferLen,
                                  OutBufferLen,
                                  TRUE,
                                  TRUE);

    if (NT_SUCCESS(Status))
    {
        Status = WmipValidateWnodeHeader(WnodeHeader,
                                 InBufferLen,
                                 FIELD_OFFSET(WNODE_METHOD_ITEM,
                                              VariableData),
                                 WNODE_FLAG_METHOD_ITEM,
                                 0xffff7f7f);
    }

    return(Status);
}

NTSTATUS WmipProbeAndCaptureGuidObjectAttributes(
    POBJECT_ATTRIBUTES CapturedObjectAttributes,
    PUNICODE_STRING CapturedGuidString,
    PWCHAR CapturedGuidBuffer,
    POBJECT_ATTRIBUTES ObjectAttributes
    )
{
    PAGED_CODE();
    
#if defined(_WIN64)
    if (IoIs32bitProcess(NULL))
    {
        POBJECT_ATTRIBUTES32 ObjectAttributes32;
        PUNICODE_STRING32 GuidString32;

        //
        // Probe the embedded object attributes and string name
        //
        ObjectAttributes32 = (POBJECT_ATTRIBUTES32)ObjectAttributes;

        try
        {
            //
            // Probe, capture and validate the OBJECT_ATTRIBUTES
            //
            ProbeForRead( ObjectAttributes32,
                          sizeof(OBJECT_ATTRIBUTES32),
                          sizeof(ULONG) );

            CapturedObjectAttributes->Length = sizeof(OBJECT_ATTRIBUTES);
            CapturedObjectAttributes->RootDirectory = UlongToPtr(ObjectAttributes32->RootDirectory);
            CapturedObjectAttributes->Attributes = ObjectAttributes32->Attributes;
            CapturedObjectAttributes->SecurityDescriptor = UlongToPtr(ObjectAttributes32->SecurityDescriptor);
            CapturedObjectAttributes->SecurityQualityOfService = UlongToPtr(ObjectAttributes32->SecurityQualityOfService);

            //
            // Now probe and validate the guid nane string passed
            //
            GuidString32 = UlongToPtr(ObjectAttributes32->ObjectName);
            ProbeForRead(GuidString32,
                         sizeof(UNICODE_STRING32),
                         sizeof(ULONG));

            CapturedGuidString->Length = GuidString32->Length;
            CapturedGuidString->MaximumLength = GuidString32->MaximumLength;
            CapturedGuidString->Buffer = UlongToPtr(GuidString32->Buffer);

            if (CapturedGuidString->Length != ((WmiGuidObjectNameLength) * sizeof(WCHAR)))
            {
                return(STATUS_INVALID_PARAMETER);
            }

            ProbeForRead(CapturedGuidString->Buffer,
                         CapturedGuidString->Length,
                         sizeof(UCHAR));

            RtlCopyMemory(CapturedGuidBuffer,
                          CapturedGuidString->Buffer,
                          WmiGuidObjectNameLength * sizeof(WCHAR));

            CapturedGuidBuffer[WmiGuidObjectNameLength] = UNICODE_NULL;
            CapturedGuidString->Buffer = CapturedGuidBuffer;
        } except(EXCEPTION_EXECUTE_HANDLER) {
            return(GetExceptionCode());
        }

    }
    else
#endif
    {
        PUNICODE_STRING GuidString;

        //
        // Probe the embedded object attributes and string name
        //
        try
        {
            //
            // Probe, capture and validate the OBJECT_ATTRIBUTES
            //
            *CapturedObjectAttributes = ProbeAndReadStructure( ObjectAttributes,
                                                              OBJECT_ATTRIBUTES);

            //
            // Now probe and validate the guid nane string passed
            //
            GuidString = CapturedObjectAttributes->ObjectName;
            *CapturedGuidString = ProbeAndReadUnicodeString(GuidString);

            if (CapturedGuidString->Length != ((WmiGuidObjectNameLength) * sizeof(WCHAR)))
            {
                return(STATUS_INVALID_PARAMETER);
            }

            ProbeForRead(CapturedGuidString->Buffer,
                         CapturedGuidString->Length,
                         sizeof(UCHAR));

            RtlCopyMemory(CapturedGuidBuffer,
                          CapturedGuidString->Buffer,
                          WmiGuidObjectNameLength * sizeof(WCHAR));

            CapturedGuidBuffer[WmiGuidObjectNameLength] = UNICODE_NULL;
            CapturedGuidString->Buffer = CapturedGuidBuffer;

        } except(EXCEPTION_EXECUTE_HANDLER) {
            return(GetExceptionCode());
        }
    }

    CapturedObjectAttributes->ObjectName = CapturedGuidString;
    
    return(STATUS_SUCCESS);
}

NTSTATUS WmipProbeWmiOpenGuidBlock(
    POBJECT_ATTRIBUTES CapturedObjectAttributes,
    PUNICODE_STRING CapturedGuidString,
    PWCHAR CapturedGuidBuffer,
    PULONG DesiredAccess,
    PWMIOPENGUIDBLOCK InGuidBlock,
    ULONG InBufferLen,
    ULONG OutBufferLen
    )
{
    NTSTATUS Status;
    POBJECT_ATTRIBUTES ObjectAttributes;
    
    PAGED_CODE();

#if defined(_WIN64)
    if (IoIs32bitProcess(NULL))
    {
        PWMIOPENGUIDBLOCK32 InGuidBlock32;

        if ((InBufferLen != sizeof(WMIOPENGUIDBLOCK32)) ||
            (OutBufferLen != sizeof(WMIOPENGUIDBLOCK32)))
        {
            return(STATUS_UNSUCCESSFUL);
        }

        //
        // Probe the embedded object attributes and string name
        //
        InGuidBlock32 = (PWMIOPENGUIDBLOCK32)InGuidBlock;
        ObjectAttributes = ULongToPtr(InGuidBlock32->ObjectAttributes);
        *DesiredAccess = InGuidBlock32->DesiredAccess;
    }
    else
#endif
    {
        //
        // Ensure the input and output buffer sizes are correct
        //
        if ((InBufferLen != sizeof(WMIOPENGUIDBLOCK)) ||
            (OutBufferLen != sizeof(WMIOPENGUIDBLOCK)))
        {
            return(STATUS_UNSUCCESSFUL);
        }

        //
        // Probe the embedded object attributes and string name
        //
        ObjectAttributes = InGuidBlock->ObjectAttributes;
        *DesiredAccess = InGuidBlock->DesiredAccess;
    }

    Status = WmipProbeAndCaptureGuidObjectAttributes(CapturedObjectAttributes,
                                                     CapturedGuidString,
                                                     CapturedGuidBuffer,
                                                     ObjectAttributes);

    if (NT_SUCCESS(Status))
    {
        if ((CapturedObjectAttributes->RootDirectory != NULL) ||
            (CapturedObjectAttributes->Attributes != 0) ||
            (CapturedObjectAttributes->SecurityDescriptor != NULL) ||
            (CapturedObjectAttributes->SecurityQualityOfService != NULL))
        {
            Status = STATUS_INVALID_PARAMETER;
        }
    }

    return(Status);
}

NTSTATUS WmipProbeWmiRegRequest(
    PDEVICE_OBJECT DeviceObject, 
    PWMIREGREQUEST Buffer,
    ULONG InBufferLen,
    ULONG OutBufferLen,
    PBOOLEAN pMofIgnored
    )
/*++

Routine Description:

    Probe the incoming WMIREGREQUEST to ensure that any offsets in the
    header point to memory that is valid within the buffer. Also validate
    that the WMIREGINFO is properly formed.

    This routine MUST succeed before any fields in the WMI_REG_INFO
    can be used by any  kernel components when passed in from user mode.
    Note that we can trust that the input and output buffer are properly
    sized since the WMI IOCTLs are METHOD_BUFFERED and the IO manager does
    that for us.

    WMIREGREQUEST Rules:

    1. The incoming buffer must be at least as large as
       sizeof(WMIREGREQUEST) + sizeof(WMIREGINFOW)
    2. The outgoing buffer must be at least as large as
       sizeof(WMIREGRESULTS)
    3. WmiRegInfo->BufferSize must be less than incoming Buffer size minus
       sizeof(WMIREGREQUEST)
    4. GuidCount must be at least 1 and less than MAXWMIGUIDCOUNT
    5. WmiRegInfo->BufferSize must be greater than equal to
        sizeof(WMIREGINFOW) + WmiRegInfo->GuidCount * sizeof(WMIREGGUIDW)
    5. WmiRegInfo->RegistryPath offset must be within the incoming buffer
    6. WmiRegInfo->MofResourcePath offset must be within the incoming buffer
    7. RegistryPath and MofResourceName strings are counted unicode strings. 
       Their length must be within the incoming buffer
    8. For WOW64, RefInfo32Size and RegGuid32Size passed in must be non-zero and 
       cannot be larger than their 64 bit counter part. 
    9. Since we decipher the counted strings at a buffer offset, the offset
       must be aligned to 2 bytes (for USHORT). 
   10. Trace Registrations do not use InstanceNames within REGGUIDW. 
       Therefore InstanceCount and InstanceNameList fields must be zero. 

Arguments:

    Buffer - WMIREGREQUEST to be validated

    InBufferLen - Size of the incoming buffer

    OutBufferLen - Size of the Output buffer

Return Value:

    NT status code

--*/

{
    ULONG WmiRegInfoSize;
    PWMIREGINFOW WmiRegInfo;
    PWMIREGREQUEST WmiRegRequest;
    PWMIREGGUIDW WmiRegGuid;
    ULONG GuidCount;
    ULONG SizeNeeded; 
    ULONG ImageNameLength=0;
    ULONG ResourceNameLength=0;
    PUCHAR pSource;
    ULONG i;

    PAGED_CODE();

    //
    // Incoming Buffer must be atleast the sizeof WMIREGREQUEST + WMIREGINFO
    //
    *pMofIgnored = FALSE;

    if (InBufferLen >= (sizeof(WMIREGREQUEST) + sizeof(WMIREGINFO)))
    {
        WmiRegRequest = (PWMIREGREQUEST)Buffer;
        WmiRegInfo = (PWMIREGINFOW) OffsetToPtr (Buffer, sizeof(WMIREGREQUEST));
        WmiRegInfoSize = WmiRegInfo->BufferSize;

        GuidCount = WmiRegInfo->GuidCount;

        //
        // BufferSize specified must be within the size of incoming Buffer.
        //

        if (WmiRegInfoSize  <= (InBufferLen - sizeof(WMIREGREQUEST)) )
        {
            if ((GuidCount == 0) || (GuidCount > WMIMAXREGGUIDCOUNT))
            {
                return STATUS_UNSUCCESSFUL;
            }

            //
            // If the Registration call came through Admin device, we are
            // okay to send the BinaryMof through. If it came through the
            // DataDevice, then we need to disable the Binary MOF. We do
            // that by simply zapping the MofResourceName and RegistryPath
            //
            if (DeviceObject != WmipAdminDeviceObject)
            {
                if (WmiRegInfo->MofResourceName > 0) 
                {
                    *pMofIgnored = TRUE;
                }
                WmiRegInfo->RegistryPath = 0;
                WmiRegInfo->MofResourceName = 0;
            }

            //
            // Make sure the RegistryPath and MofResourceName offsets are
            // within the REGINFO buffer.
            //

            if ( (WmiRegInfo->RegistryPath >= WmiRegInfoSize) ||
                 (WmiRegInfo->MofResourceName >= WmiRegInfoSize) ) {
               return STATUS_UNSUCCESSFUL;
            }

            //
            // Validate the Counted Strings. 
            // 

            if (WmiRegInfo->RegistryPath > 0) 
            {
                //
                // String Offsets need to be aligned to 2 Bytes
                //
                if (( WmiRegInfo->RegistryPath & 1) != 0) 
                {
                    return STATUS_UNSUCCESSFUL;
                }
                ImageNameLength = *((PUSHORT)OffsetToPtr(WmiRegInfo, WmiRegInfo->RegistryPath) );
                ImageNameLength += sizeof(USHORT);

                if ((WmiRegInfo->RegistryPath + ImageNameLength ) > WmiRegInfoSize) 
                {
                    return STATUS_UNSUCCESSFUL;
                }

            }

            if (WmiRegInfo->MofResourceName > 0) 
            {
                if ((WmiRegInfo->MofResourceName & 1) != 0) 
                {
                    return STATUS_UNSUCCESSFUL;
                }

                ResourceNameLength = *((PUSHORT)OffsetToPtr(WmiRegInfo, WmiRegInfo->MofResourceName)); 
                ResourceNameLength += sizeof(USHORT);

                if ( (WmiRegInfo->MofResourceName + ResourceNameLength) > WmiRegInfoSize)
                {
                    return STATUS_UNSUCCESSFUL;
                }
            }
            // Note: If the ImagePath and MofResource trample over each other but stayed
            // within BufferSize , we will not catch it. 

#if defined(_WIN64)
            if (IoIs32bitProcess(NULL))
            {
                //
                // Check to make sure the 32 bit structure sizes passed in 
                // by the caller is comparable to the 64-bit counterparts
                // 

                if ((WmiRegRequest->WmiRegInfo32Size == 0) || 
                    (WmiRegRequest->WmiRegInfo32Size > sizeof(WMIREGINFOW)) )
                {
                    return STATUS_UNSUCCESSFUL;
                }

                if ((WmiRegRequest->WmiRegGuid32Size == 0) ||
                    (WmiRegRequest->WmiRegGuid32Size > sizeof(WMIREGGUIDW)) )
                {
                    return STATUS_UNSUCCESSFUL;
                }

                //
                // InstanceCount and InstanceNameList in 
                // WMIREGGUIDW structure must be zero. This check is 
                // done after thunking gor WOW64. 
                //


                SizeNeeded =  WmiRegRequest->WmiRegInfo32Size +
                              GuidCount * WmiRegRequest->WmiRegGuid32Size +
                              ImageNameLength +
                              ResourceNameLength;
                if (SizeNeeded > WmiRegInfoSize) { 
                    return STATUS_UNSUCCESSFUL;
                }
                
            }
            else 
#endif
            {

                SizeNeeded = sizeof(WMIREGINFOW) + 
                             GuidCount * sizeof(WMIREGGUIDW) + 
                             ImageNameLength + 
                             ResourceNameLength;
                
                if (SizeNeeded > WmiRegInfoSize) { 
                    return STATUS_UNSUCCESSFUL;
                }
                
                //
                // Check to see if the InstanceCount and InstanceNameList in 
                // WMIREGGUIDW structure is zero
                //
                pSource = OffsetToPtr(WmiRegInfo, sizeof(WMIREGINFOW) );
                for (i=0; i < GuidCount; i++) {
                    WmiRegGuid = (PWMIREGGUIDW) pSource;
                    if ( (WmiRegGuid->InstanceCount > 0) ||
                         (WmiRegGuid->InstanceNameList > 0) )
                    {
                        return STATUS_UNSUCCESSFUL;
                    }
                    pSource += sizeof(WMIREGGUIDW);
                }

            }

            //
            // Now validate the OutBuffer size needed
            //

            if (sizeof(WMIREGRESULTS) > OutBufferLen) 
            {
                return STATUS_UNSUCCESSFUL;
            }

            //
            // All tests passed. Return SUCCESS
            //
            return STATUS_SUCCESS;
        }
    }
    return STATUS_UNSUCCESSFUL;
}

#ifdef ALLOC_DATA_PRAGMA
#pragma data_seg()
#pragma const_seg()
#endif

