/*++

Copyright (c) 1997 Microsoft Corporation

Module Name:

    logsup.c

Abstract:

    WMI logger api set. The routines here will need to appear like they
    are system calls. They are necessary to do the necessary error checking
    and do most of the legwork that can be done outside the kernel. The
    kernel portion will subsequently only deal with the actual logging
    and tracing.

Author:

    28-May-1997 JeePang

Revision History:

--*/

#ifndef MEMPHIS
#include <nt.h>
#include <ntrtl.h>          // for ntutrl.h
#include <nturtl.h>         // for RTL_CRITICAL_SECTION in winbase.h/wtypes.h
#include <wtypes.h>         // for LPGUID in wmium.h
#include "wmiump.h"
#include "evntrace.h"
#include "traceump.h"
#include "tracelib.h"
#include <math.h>
#include "trcapi.h"

NTSTATUS
WmipProcessRunDown(
    IN PWMI_LOGGER_CONTEXT Logger,
    IN ULONG StartFlag,
    IN ULONG fEnableFlags
    );

NTSTATUS
WmipThreadRunDown(
    IN PWMI_LOGGER_CONTEXT Logger,
    IN PSYSTEM_PROCESS_INFORMATION pProcessInfo,
    IN ULONG StartFlag,
    IN BOOLEAN bExtended
    );

ULONG WmiTraceAlignment = DEFAULT_TRACE_ALIGNMENT;
/*
ULONG
WmipStartLogger(
    IN OUT PWMI_LOGGER_INFORMATION LoggerInfo
    )
/*++

Routine Description:

    This is the actual routine to communicate with the kernel to start
    the logger. All the required parameters must be in LoggerInfo.

Arguments:

    LoggerInfo      The actual parameters to be passed to and return from
                    kernel.

Return Value:

    The status of performing the action requested.

--*//*
{
    ULONG Status;
    ULONG BufferSize;
    ACCESS_MASK DesiredAccess = 0;
    LPGUID Guid;
    PVOID SavedChecksum;
    ULONG SavedLogFileMode;
    BOOLEAN IsKernelTrace = FALSE;
    BOOLEAN bLogFile = FALSE;
    BOOLEAN bRealTime = FALSE;


    Guid = &LoggerInfo->Wnode.Guid;
    if (IsEqualGUID(Guid, &SystemTraceControlGuid) ||
        IsEqualGUID(Guid, &WmiEventLoggerGuid)) {
        IsKernelTrace = TRUE;
        DesiredAccess |= TRACELOG_ACCESS_KERNEL_LOGGER;
    }
    if ((LoggerInfo->LogFileName.Length > 0) &&
        (LoggerInfo->LogFileName.Buffer != NULL)) {
        DesiredAccess |= TRACELOG_CREATE_ONDISK;
        bLogFile = TRUE;
    }
    SavedLogFileMode = LoggerInfo->LogFileMode;
    if (SavedLogFileMode & EVENT_TRACE_REAL_TIME_MODE) {
        DesiredAccess |= TRACELOG_CREATE_REALTIME;
        bRealTime = TRUE;
    }
    Status = WmipCheckGuidAccess( Guid, DesiredAccess );

    if (Status != ERROR_SUCCESS) {
        return Status;
    }
    //
    // Set the Default Clock Type
    //

    if (LoggerInfo->Wnode.ClientContext != EVENT_TRACE_CLOCK_SYSTEMTIME) {
        LoggerInfo->Wnode.ClientContext = EVENT_TRACE_CLOCK_PERFCOUNTER;
    }

    if (SavedLogFileMode & EVENT_TRACE_PRIVATE_LOGGER_MODE) {
        Status = WmipSendUmLogRequest(
                    WmiStartLoggerCode,
                    LoggerInfo
                    );
    }
    else if (IsKernelTrace) {
        //
        // In order to capture the process/thread rundown accurately, we need to
        // start kernel logger in two steps. Start logger with delay write,
        // do rundown from user mode and then updatelogger with filename.
        //
        WMI_LOGGER_INFORMATION DelayLoggerInfo;
        ULONG EnableFlags = LoggerInfo->EnableFlags;
        LARGE_INTEGER Frequency;
        WMI_REF_CLOCK RefClock;
        //
        // If it's only realtime start logger in one step
        //

        if (bRealTime && !bLogFile) {

            BufferSize = LoggerInfo->BufferSize * 1024;
            Status =  WmipSendWmiKMRequest(
                        NULL,
                        IOCTL_WMI_START_LOGGER,
                        LoggerInfo,
                        LoggerInfo->Wnode.BufferSize,
                        LoggerInfo,
                        LoggerInfo->Wnode.BufferSize,
                        &BufferSize,
                        NULL
                        );
            return WmipSetDosError(Status);
        }

        if (EnableFlags & EVENT_TRACE_FLAG_EXTENSION) {
            PTRACE_ENABLE_FLAG_EXTENSION tFlagExt;

            tFlagExt = (PTRACE_ENABLE_FLAG_EXTENSION)
                       &LoggerInfo->EnableFlags;
            EnableFlags = *(PULONG)((PCHAR)LoggerInfo + tFlagExt->Offset);
        }

        //
        // Take a reference timestamp before actually starting the logger
        //
        RefClock.StartTime.QuadPart = WmipGetSystemTime();
        if (LoggerInfo->Wnode.ClientContext == EVENT_TRACE_CLOCK_PERFCOUNTER) {
            Status = NtQueryPerformanceCounter(&RefClock.StartPerfClock, &Frequency);
        }
        else {
            RefClock.StartPerfClock = RefClock.StartTime;
        }


        RtlCopyMemory(&DelayLoggerInfo, LoggerInfo, sizeof(WMI_LOGGER_INFORMATION));
        RtlZeroMemory(&DelayLoggerInfo.LogFileName, sizeof(UNICODE_STRING) );

        DelayLoggerInfo.Wnode.BufferSize = sizeof(WMI_LOGGER_INFORMATION);

        DelayLoggerInfo.LogFileMode |= EVENT_TRACE_DELAY_OPEN_FILE_MODE;

        //
        // Since there's no filename in step 1 of StartLogger we need to mask
        // the NEWFILE mode to prevent kernel trying to generate a file
        //
        DelayLoggerInfo.LogFileMode &= ~EVENT_TRACE_FILE_MODE_NEWFILE;

        DelayLoggerInfo.EnableFlags = (EVENT_TRACE_FLAG_PROCESS & EnableFlags);
        DelayLoggerInfo.EnableFlags |= (EVENT_TRACE_FLAG_THREAD & EnableFlags);
        DelayLoggerInfo.EnableFlags |= (EVENT_TRACE_FLAG_IMAGE_LOAD & EnableFlags);

        BufferSize = DelayLoggerInfo.BufferSize * 1024;
        Status = WmipSendWmiKMRequest(
                    NULL,
                    IOCTL_WMI_START_LOGGER,
                    &DelayLoggerInfo,
                    DelayLoggerInfo.Wnode.BufferSize,
                    &DelayLoggerInfo,
                    DelayLoggerInfo.Wnode.BufferSize,
                    &BufferSize,
                    NULL
                    );
        if (Status != ERROR_SUCCESS) {
            return Status;
        }
        //
        // We need to pick up any parameter adjustment done by the kernel
        // here so UpdateTrace does not fail.
        //
        LoggerInfo->Wnode.HistoricalContext = DelayLoggerInfo.Wnode.HistoricalContext;
        LoggerInfo->MinimumBuffers          = DelayLoggerInfo.MinimumBuffers;
        LoggerInfo->MaximumBuffers          = DelayLoggerInfo.MaximumBuffers;
        LoggerInfo->NumberOfBuffers         = DelayLoggerInfo.NumberOfBuffers;
        LoggerInfo->BufferSize              = DelayLoggerInfo.BufferSize;
        LoggerInfo->AgeLimit                = DelayLoggerInfo.AgeLimit;

        BufferSize = LoggerInfo->BufferSize * 1024;

        //
        //  Add the LogHeader
        //
        LoggerInfo->Checksum = NULL;
        Status = WmipAddLogHeaderToLogFile(LoggerInfo, &RefClock, FALSE);

        if (Status == ERROR_SUCCESS) {
            SavedChecksum = LoggerInfo->Checksum;
            //
            // Update the logger with the filename
            //
            Status = WmipSendWmiKMRequest(
                        NULL,
                        IOCTL_WMI_UPDATE_LOGGER,
                        LoggerInfo,
                        LoggerInfo->Wnode.BufferSize,
                        LoggerInfo,
                        LoggerInfo->Wnode.BufferSize,
                        &BufferSize,
                        NULL
                        );


            if (LoggerInfo->Checksum != NULL) {
                WmipFree(LoggerInfo->Checksum);
            }
        }

        if (Status != ERROR_SUCCESS) {
            ULONG lStatus;

            //
            // Logger must be stopped now
            //
            lStatus = WmipSendWmiKMRequest(
                    NULL,
                    IOCTL_WMI_STOP_LOGGER,
                    LoggerInfo,
                    LoggerInfo->Wnode.BufferSize,
                    LoggerInfo,
                    LoggerInfo->Wnode.BufferSize,
                    &BufferSize,
                    NULL
                    );

            LoggerInfo->LogFileMode = SavedLogFileMode;
            return WmipSetDosError(Status);
        }
        else {
            if (LoggerInfo->LogFileHandle != NULL) {
                NtClose(LoggerInfo->LogFileHandle);
                LoggerInfo->LogFileHandle = NULL;
            }
        }
    }
    else {
        LoggerInfo->Checksum = NULL;
        Status = WmipAddLogHeaderToLogFile(LoggerInfo, NULL, FALSE);
        if (Status != ERROR_SUCCESS) {
            return WmipSetDosError(Status);
        }

        BufferSize = LoggerInfo->BufferSize * 1024;
        SavedChecksum = LoggerInfo->Checksum;

        Status = WmipSendWmiKMRequest(       // actually start the logger here
        		    NULL,
                    IOCTL_WMI_START_LOGGER,
                    LoggerInfo,
                    LoggerInfo->Wnode.BufferSize,
                    LoggerInfo,
                    LoggerInfo->Wnode.BufferSize,
                    &BufferSize,
		            NULL
                    );
        if (Status == ERROR_SUCCESS) {
            if (LoggerInfo->LogFileHandle != NULL) {
                NtClose(LoggerInfo->LogFileHandle);
                LoggerInfo->LogFileHandle = NULL;
            }
        }

        if (SavedChecksum != NULL) {
            WmipFree(SavedChecksum);
        }
    }
    //
    // Restore the LogFileMode
    //
    LoggerInfo->LogFileMode = SavedLogFileMode;

    return WmipSetDosError(Status);
}*/


ULONG
WmipFinalizeLogFileHeader(
    IN PWMI_LOGGER_INFORMATION LoggerInfo
    )
{
    ULONG                     Status    = ERROR_SUCCESS;
    ULONG                     ErrorCode = ERROR_SUCCESS;
    HANDLE                    LogFile   = INVALID_HANDLE_VALUE;
    LARGE_INTEGER             CurrentTime;
    ULONG                     BuffersWritten;
    WMI_LOGGER_CONTEXT        Logger;
    IO_STATUS_BLOCK           IoStatus;
    FILE_POSITION_INFORMATION FileInfo;
    FILE_STANDARD_INFORMATION FileSize;
    PWMI_BUFFER_HEADER        Buffer;  // need to initialize buffer first
    SYSTEM_BASIC_INFORMATION  SystemInfo;
    ULONG                     EnableFlags;

    RtlZeroMemory(&Logger, sizeof(WMI_LOGGER_CONTEXT));

    Logger.BufferSpace = NULL;
    if (LoggerInfo->LogFileName.Length > 0 ) {
        // open the file for writing synchronously for the logger
        //    others may want to read it as well.
        //
        LogFile = WmipCreateFileW(
                   (LPWSTR)LoggerInfo->LogFileName.Buffer,
                   GENERIC_READ | GENERIC_WRITE,
                   FILE_SHARE_READ,
                   NULL,
                   OPEN_EXISTING,
                   FILE_ATTRIBUTE_NORMAL,
                   NULL
                   );
        if (LogFile == INVALID_HANDLE_VALUE) {
            ErrorCode = WmipSetDosError(WmipGetLastError());
            goto cleanup;
        }

        Logger.BuffersWritten = LoggerInfo->BuffersWritten;

        Logger.BufferSpace = WmipAlloc(LoggerInfo->BufferSize * 1024);
        if (Logger.BufferSpace == NULL) {
            ErrorCode = WmipSetDosError(ERROR_NOT_ENOUGH_MEMORY);
            goto cleanup;
        }
        Buffer = (PWMI_BUFFER_HEADER) Logger.BufferSpace;
        RtlZeroMemory(Buffer, LoggerInfo->BufferSize * 1024);
        Buffer->Wnode.BufferSize = LoggerInfo->BufferSize * 1024;
        Buffer->ClientContext.Alignment = (UCHAR)WmiTraceAlignment;
        Buffer->EventsLost = 0;
        Buffer->Offset = sizeof(WMI_BUFFER_HEADER);
        Buffer->Wnode.Guid = LoggerInfo->Wnode.Guid;
        Status = NtQuerySystemInformation(
                    SystemBasicInformation,
                    &SystemInfo, sizeof (SystemInfo), NULL);

        if (!NT_SUCCESS(Status)) {
            ErrorCode = WmipSetNtStatus(Status);
            goto cleanup;
        }
        Logger.TimerResolution = SystemInfo.TimerResolution;
        Logger.LogFileHandle = LogFile;
        Logger.BufferSize = LoggerInfo->BufferSize * 1024;

        // For Circular LogFile the process rundown data is appended at the
        // last buffer written and not to the end of file.
        //
        Status = NtQueryInformationFile(
                    LogFile,
                    &IoStatus,
                    &FileSize,
                    sizeof(FILE_STANDARD_INFORMATION),
                    FileStandardInformation
                        );
        if (!NT_SUCCESS(Status)) {
            ErrorCode = WmipSetNtStatus(Status);
            goto cleanup;
        }

        if (IsEqualGUID(&LoggerInfo->Wnode.Guid, &SystemTraceControlGuid)) {

            if (LoggerInfo->LogFileMode != EVENT_TRACE_FILE_MODE_CIRCULAR) {
                FileInfo.CurrentByteOffset = FileSize.EndOfFile;
            }
            else {
                ULONG BufferSize = LoggerInfo->BufferSize;  // in KB
                ULONG BuffersWritten = LoggerInfo->BuffersWritten;
                ULONG maxBuffers = (LoggerInfo->MaximumFileSize * 1024) / BufferSize;
                ULONG LastBuffer;
                ULONG StartBuffers;

                FileInfo.CurrentByteOffset.QuadPart =
                                         LOGFILE_FIELD_OFFSET(StartBuffers);
                Status = NtSetInformationFile(
                                     LogFile,
                                     &IoStatus,
                                     &FileInfo,
                                     sizeof(FILE_POSITION_INFORMATION),
                                     FilePositionInformation
                                     );
                if (!NT_SUCCESS(Status)) {
                    ErrorCode = WmipSetNtStatus(Status);
                    goto cleanup;
                }

                Status = NtReadFile(
                            LogFile,
                            NULL,
                            NULL,
                            NULL,
                            &IoStatus,
                            &StartBuffers,
                            sizeof(ULONG),
                            NULL,
                            NULL
                            );
                if (!NT_SUCCESS(Status)) {
                    ErrorCode = WmipSetNtStatus(Status);
                    goto cleanup;
                }

                LastBuffer = (maxBuffers > StartBuffers) ?
                             (StartBuffers + (BuffersWritten - StartBuffers)
                             % (maxBuffers - StartBuffers))
                             : 0;
                FileInfo.CurrentByteOffset.QuadPart =  LastBuffer *
                                                       BufferSize * 1024;
            }

            Status = NtSetInformationFile(
                         LogFile,
                         &IoStatus,
                         &FileInfo,
                         sizeof(FILE_POSITION_INFORMATION),
                         FilePositionInformation
                         );
            if (!NT_SUCCESS(Status)) {
                ErrorCode = WmipSetNtStatus(Status);
                goto cleanup;
            }

            EnableFlags = LoggerInfo->EnableFlags;

            if (EnableFlags & EVENT_TRACE_FLAG_EXTENSION) {
                PTRACE_ENABLE_FLAG_EXTENSION tFlagExt;

                tFlagExt = (PTRACE_ENABLE_FLAG_EXTENSION)
                           &LoggerInfo->EnableFlags;

                if (LoggerInfo->Wnode.BufferSize >= (tFlagExt->Offset + sizeof(ULONG)) )  {
                    EnableFlags = *(PULONG)((PCHAR)LoggerInfo + tFlagExt->Offset);
                }
                else {
                    EnableFlags = 0;    // Should not happen.
                }
            }

            if (LoggerInfo->Wnode.ClientContext != EVENT_TRACE_CLOCK_SYSTEMTIME) {
                LoggerInfo->Wnode.ClientContext = EVENT_TRACE_CLOCK_PERFCOUNTER;
            }
            Logger.UsePerfClock = LoggerInfo->Wnode.ClientContext;

            WmipProcessRunDown(&Logger, FALSE, EnableFlags);
            {
                PWMI_BUFFER_HEADER Buffer1 =
                                (PWMI_BUFFER_HEADER) Logger.BufferSpace;
                    if (Buffer1->Offset < Logger.BufferSize) {
                        RtlFillMemory(
                                (char *) Logger.BufferSpace + Buffer1->Offset,
                                Logger.BufferSize - Buffer1->Offset,
                                0xFF);
                    }
            }
            Status = NtWriteFile(
                        LogFile,
                        NULL,
                        NULL,
                        NULL,
                        &IoStatus,
                        Logger.BufferSpace,
                        Logger.BufferSize,
                        NULL,
                        NULL);
            if (NT_SUCCESS(Status)) {
                NtFlushBuffersFile(Logger.LogFileHandle, &IoStatus);
                Logger.BuffersWritten++;
            }
        }
        else { // For Application Traces, need to dump the guidmaps again.

    // Set the FilePointer to end of file so that Rundown data may be appended.
    //
            FileInfo.CurrentByteOffset = FileSize.EndOfFile;
            Status = NtSetInformationFile(
                     LogFile,
                     &IoStatus,
                     &FileInfo,
                     sizeof(FILE_POSITION_INFORMATION),
                     FilePositionInformation
                     );
            if (!NT_SUCCESS(Status)) {
                ErrorCode = WmipSetNtStatus(Status);
                goto cleanup;
            }

            // Dump the Guid Maps once more at the End.
            //
            Buffer->EventsLost = 0;
            Buffer->Offset = sizeof(WMI_BUFFER_HEADER);
            BuffersWritten = Logger.BuffersWritten;

            if (WmipDumpGuidMaps(&Logger, NULL, FALSE) > 0) {
                if (Buffer->Offset < Logger.BufferSize) {
                    RtlFillMemory(
                            (char *) Logger.BufferSpace + Buffer->Offset,
                            Logger.BufferSize - Buffer->Offset,
                            0xFF);
                }
                Status = NtWriteFile(
                            LogFile,
                            NULL,
                            NULL,
                            NULL,
                            &IoStatus,
                            Logger.BufferSpace,
                            Logger.BufferSize,
                            NULL,
                            NULL);
                if (NT_SUCCESS(Status)) {
                    NtFlushBuffersFile(Logger.LogFileHandle, &IoStatus);
                    Logger.BuffersWritten = BuffersWritten;
                }
            }
        }

        // TODO: should use memory-mapped file

        // Update the EndTime stamp field in LogFile.
        //
        FileInfo.CurrentByteOffset.QuadPart =
                                LOGFILE_FIELD_OFFSET(EndTime);
        Status = NtSetInformationFile(
                     LogFile,
                     &IoStatus,
                     &FileInfo,
                     sizeof(FILE_POSITION_INFORMATION),
                     FilePositionInformation
                     );
        if (!NT_SUCCESS(Status)) {
            ErrorCode = WmipSetNtStatus(Status);
            goto cleanup;
        }

        // End Time is always wallclock time.
        //
        CurrentTime.QuadPart = WmipGetSystemTime();
        Status = NtWriteFile(
                    LogFile,
                    NULL,
                    NULL,
                    NULL,
                    &IoStatus,
                    &CurrentTime,
                    sizeof(ULONGLONG),
                    NULL,
                    NULL
                    );
        if (NT_SUCCESS(Status)) {
            NtFlushBuffersFile(Logger.LogFileHandle, &IoStatus);
        }

       // Update the Number of Buffers Written field in the header
       //
        FileInfo.CurrentByteOffset.QuadPart =
                            LOGFILE_FIELD_OFFSET(BuffersWritten);
        Status = NtSetInformationFile(
                     LogFile,
                     &IoStatus,
                     &FileInfo,
                     sizeof(FILE_POSITION_INFORMATION),
                     FilePositionInformation
                     );
        if (!NT_SUCCESS(Status)) {
            ErrorCode = WmipSetNtStatus(Status);
            goto cleanup;
        }

        Status = NtWriteFile(
                    LogFile,
                    NULL,
                    NULL,
                    NULL,
                    &IoStatus,
                    &Logger.BuffersWritten,
                    sizeof(ULONG),
                    NULL,
                    NULL
                    );
        if (NT_SUCCESS(Status)) {
            NtFlushBuffersFile(Logger.LogFileHandle, &IoStatus);
        }

        ErrorCode = RtlNtStatusToDosError(Status);
        LoggerInfo->BuffersWritten = Logger.BuffersWritten;

        //
        // Write the BuffersLost information into the logfile
        //


        FileInfo.CurrentByteOffset.QuadPart =
                            LOGFILE_FIELD_OFFSET(BuffersLost);
        Status = NtSetInformationFile(
                     LogFile,
                     &IoStatus,
                     &FileInfo,
                     sizeof(FILE_POSITION_INFORMATION),
                     FilePositionInformation
                     );
        if (!NT_SUCCESS(Status)) {
            ErrorCode = WmipSetNtStatus(Status);
            goto cleanup;
        }

        Status = NtWriteFile(
                    LogFile,
                    NULL,
                    NULL,
                    NULL,
                    &IoStatus,
                    &LoggerInfo->LogBuffersLost,
                    sizeof(ULONG),
                    NULL,
                    NULL
                    );
        if (NT_SUCCESS(Status)) {
            NtFlushBuffersFile(Logger.LogFileHandle, &IoStatus);
        }

        //
        // Write the EventsLost information into the logfile
        //


        FileInfo.CurrentByteOffset.QuadPart =
                            LOGFILE_FIELD_OFFSET(EventsLost);
        Status = NtSetInformationFile(
                     LogFile,
                     &IoStatus,
                     &FileInfo,
                     sizeof(FILE_POSITION_INFORMATION),
                     FilePositionInformation
                     );
        if (!NT_SUCCESS(Status)) {
            ErrorCode = WmipSetNtStatus(Status);
            goto cleanup;
        }

        Status = NtWriteFile(
                    LogFile,
                    NULL,
                    NULL,
                    NULL,
                    &IoStatus,
                    &LoggerInfo->EventsLost,
                    sizeof(ULONG),
                    NULL,
                    NULL
                    );
        if (NT_SUCCESS(Status)) {
            NtFlushBuffersFile(Logger.LogFileHandle, &IoStatus);
        }

    }

cleanup:
    if (LogFile != INVALID_HANDLE_VALUE) {
        NtClose(LogFile);
    }
    if (Logger.BufferSpace != NULL) {
        WmipFree(Logger.BufferSpace);
    }
    return WmipSetDosError(ErrorCode);
}

ULONG
WmipStopLogger(
    IN OUT PWMI_LOGGER_INFORMATION LoggerInfo
    )
/*++

Routine Description:

    This is the actual routine to communicate with the kernel to stop
    the logger. All the properties of the logger will be returned in LoggerInfo.

Arguments:

    LoggerInfo      The actual parameters to be passed to and return from
                    kernel.

Return Value:

    The status of performing the action requested.

--*/
{
    ULONG ErrorCode, ReturnSize;
    PTRACE_ENABLE_CONTEXT pContext;

    if (LoggerInfo == NULL)
        return WmipSetDosError(ERROR_INVALID_PARAMETER);

    if (LoggerInfo->Wnode.BufferSize < sizeof(WMI_LOGGER_INFORMATION))
        return WmipSetDosError(ERROR_INVALID_PARAMETER);

    if ( !(LoggerInfo->Wnode.Flags & WNODE_FLAG_TRACED_GUID) )
        return WmipSetDosError(ERROR_INVALID_PARAMETER);

    pContext = (PTRACE_ENABLE_CONTEXT) & LoggerInfo->Wnode.HistoricalContext;
    if (   (pContext->InternalFlag != 0)
        && (pContext->InternalFlag != EVENT_TRACE_INTERNAL_FLAG_PRIVATE)) {
        // Currently only one possible InternalFlag value. This will filter
        // out some bogus LoggerHandle
        //
        return WmipSetDosError(ERROR_INVALID_HANDLE);
    }

    if (LoggerInfo->LogFileMode & EVENT_TRACE_PRIVATE_LOGGER_MODE) {
        ErrorCode = WmipSendUmLogRequest(WmiStopLoggerCode, LoggerInfo);
        pContext->InternalFlag |= EVENT_TRACE_INTERNAL_FLAG_PRIVATE;
        pContext->LoggerId     = 1;
    }
    else {


        ErrorCode = WmipSendWmiKMRequest(
                        NULL,
                        IOCTL_WMI_STOP_LOGGER,
                        LoggerInfo,
                        LoggerInfo->Wnode.BufferSize,
                        LoggerInfo,
                        LoggerInfo->Wnode.BufferSize,
                        &ReturnSize,
                        NULL
                        );

//
// if logging to a file, then update the EndTime, BuffersWritten and do
// process rundown for kernel trace.
//
        if (ErrorCode == ERROR_SUCCESS) {
            ErrorCode = WmipFinalizeLogFileHeader(LoggerInfo);
        }
    }

    return WmipSetDosError(ErrorCode);
}


ULONG
WmipQueryLogger(
    IN OUT PWMI_LOGGER_INFORMATION LoggerInfo,
    IN ULONG Update
    )
/*++

Routine Description:

    This is the actual routine to communicate with the kernel to query
    the logger. All the properties of the logger will be returned in LoggerInfo.

Arguments:

    LoggerInfo      The actual parameters to be passed to and return from
                    kernel.

Return Value:

    The status of performing the action requested.

--*/
{
    ULONG Status, ReturnSize;
    HANDLE LogFileHandle = NULL;
    PTRACE_ENABLE_CONTEXT pContext;
    BOOLEAN bAddAppendFlag = FALSE;
    ULONG SavedLogFileMode;

    if (LoggerInfo == NULL)
        return WmipSetDosError(ERROR_INVALID_PARAMETER);

    if (LoggerInfo->Wnode.BufferSize < sizeof(WMI_LOGGER_INFORMATION))
        return WmipSetDosError(ERROR_INVALID_PARAMETER);

    if ( !(LoggerInfo->Wnode.Flags & WNODE_FLAG_TRACED_GUID) )
        return WmipSetDosError(ERROR_INVALID_PARAMETER);

    LoggerInfo->Checksum      = NULL;
    LoggerInfo->LogFileHandle = NULL;
    pContext = (PTRACE_ENABLE_CONTEXT) &LoggerInfo->Wnode.HistoricalContext;

    if (   (pContext->InternalFlag != 0)
        && (pContext->InternalFlag != EVENT_TRACE_INTERNAL_FLAG_PRIVATE)) {
        // Currently only one possible InternalFlag value. This will filter
        // out some bogus LoggerHandle
        //
        return WmipSetDosError(ERROR_INVALID_HANDLE);
    }

    //
    // If UPDATE and a new logfile is given throw in the LogFileHeader
    //
    if (   Update
        && LoggerInfo->LogFileName.Length > 0
        && !(   (LoggerInfo->LogFileMode & EVENT_TRACE_PRIVATE_LOGGER_MODE)
             || (pContext->InternalFlag & EVENT_TRACE_INTERNAL_FLAG_PRIVATE))) {
        Status = WmipAddLogHeaderToLogFile(LoggerInfo, NULL, Update);
        if (Status  != ERROR_SUCCESS) {
            return WmipSetDosError(Status);
        }

        LogFileHandle = LoggerInfo->LogFileHandle;
        bAddAppendFlag = TRUE;
        //
        // If we are switching to a new file, make sure it is append mode
        //
        SavedLogFileMode = LoggerInfo->LogFileMode;
    }


    if (LoggerInfo->LogFileMode & EVENT_TRACE_PRIVATE_LOGGER_MODE ||
        pContext->InternalFlag & EVENT_TRACE_INTERNAL_FLAG_PRIVATE) {

        pContext->InternalFlag |= EVENT_TRACE_INTERNAL_FLAG_PRIVATE;
        pContext->LoggerId     = 1;

        Status = WmipSendUmLogRequest(
                    (Update) ? (WmiUpdateLoggerCode) : (WmiQueryLoggerCode),
                    LoggerInfo
                    );
    }
    else {
        Status = WmipSendWmiKMRequest(
                    NULL,
                    (Update ? IOCTL_WMI_UPDATE_LOGGER : IOCTL_WMI_QUERY_LOGGER),
                    LoggerInfo,
                    LoggerInfo->Wnode.BufferSize,
                    LoggerInfo,
                    LoggerInfo->Wnode.BufferSize,
                    &ReturnSize,
                    NULL
                    );

        if (LoggerInfo->Checksum != NULL) {
            WmipFree(LoggerInfo->Checksum);
        }
    }
    if (bAddAppendFlag) {
        LoggerInfo->LogFileMode = SavedLogFileMode;
    }
    return WmipSetDosError(Status);
}

PVOID
WmipGetTraceBuffer(
    IN PWMI_LOGGER_CONTEXT Logger,
    IN PSYSTEM_THREAD_INFORMATION pThread,
    IN ULONG GroupType,
    IN ULONG RequiredSize
    )
{
    PSYSTEM_TRACE_HEADER Header;
    PWMI_BUFFER_HEADER Buffer;
    THREAD_BASIC_INFORMATION ThreadInfo;
    KERNEL_USER_TIMES ThreadCpu;
    NTSTATUS Status;
    ULONG BytesUsed;
    PCLIENT_ID Cid;

    RequiredSize += sizeof (SYSTEM_TRACE_HEADER);   // add in header

    RequiredSize = (ULONG) ALIGN_TO_POWER2(RequiredSize, WmiTraceAlignment);

    Buffer = (PWMI_BUFFER_HEADER) Logger->BufferSpace;

    if (RequiredSize > Logger->BufferSize - sizeof(WMI_BUFFER_HEADER)) {
        WmipSetDosError(ERROR_BUFFER_OVERFLOW);
        return NULL;
    }

    if (RequiredSize > (Logger->BufferSize - Buffer->Offset)) {
        ULONG Status;
        IO_STATUS_BLOCK IoStatus;

        if (Buffer->Offset < Logger->BufferSize) {
            RtlFillMemory(
                    (char *) Buffer + Buffer->Offset,
                    Logger->BufferSize - Buffer->Offset,
                    0xFF);
        }
        Status = NtWriteFile(
                    Logger->LogFileHandle,
                    NULL,
                    NULL,
                    NULL,
                    &IoStatus,
                    Buffer,
                    Logger->BufferSize,
                    NULL,
                    NULL);
        Buffer->EventsLost = 0;
        Buffer->Offset = sizeof(WMI_BUFFER_HEADER);
        if (!NT_SUCCESS(Status)) {
            return NULL;
        }
        Logger->BuffersWritten++;
    }
    Header = (PSYSTEM_TRACE_HEADER) ((char*)Buffer + Buffer->Offset);

    if (Logger->UsePerfClock == EVENT_TRACE_CLOCK_PERFCOUNTER) {
        LARGE_INTEGER Frequency;
        ULONGLONG Counter = 0;
        Status = NtQueryPerformanceCounter((PLARGE_INTEGER)&Counter,
                                            &Frequency);
        Header->SystemTime.QuadPart = Counter;
    }
    else {
        Header->SystemTime.QuadPart = WmipGetSystemTime();
    }

    Header->Header = (GroupType << 16) + RequiredSize;
    Header->Marker = SYSTEM_TRACE_MARKER;

    if (pThread == NULL) {
        Status = NtQueryInformationThread(
                    NtCurrentThread(),
                    ThreadBasicInformation,
                    &ThreadInfo,
                    sizeof ThreadInfo, NULL);
        if (NT_SUCCESS(Status)) {
            Cid = &ThreadInfo.ClientId;
            Header->ThreadId = HandleToUlong(Cid->UniqueThread);
            Header->ProcessId = HandleToUlong(Cid->UniqueProcess);
        }

        Status = NtQueryInformationThread(
                    NtCurrentThread(),
                    ThreadTimes,
                    &ThreadCpu, sizeof ThreadCpu, NULL);
        if (NT_SUCCESS(Status)) {
            Header->KernelTime = (ULONG) (ThreadCpu.KernelTime.QuadPart
                                      / Logger->TimerResolution);
            Header->UserTime   = (ULONG) (ThreadCpu.UserTime.QuadPart
                                      / Logger->TimerResolution);
        }
    }
    else {
        Cid = &pThread->ClientId;
        Header->ThreadId = HandleToUlong(Cid->UniqueThread);
        Header->ProcessId = HandleToUlong(Cid->UniqueProcess);
        Header->KernelTime = (ULONG) (pThread->KernelTime.QuadPart
                                / Logger->TimerResolution);
        Header->UserTime = (ULONG) (pThread->UserTime.QuadPart
                                / Logger->TimerResolution);
    }

    Buffer->Offset += RequiredSize;
    // If there is room, throw in a end of buffer marker.

    BytesUsed = Buffer->Offset;
    if ( BytesUsed <= (Logger->BufferSize-sizeof(ULONG)) ) {
        *((long*)((char*)Buffer+Buffer->Offset)) = -1;
    }
    return (PVOID) ( (char*) Header + sizeof(SYSTEM_TRACE_HEADER) );
}


VOID
WmipCopyPropertiesToInfo(
    IN PEVENT_TRACE_PROPERTIES Properties,
    IN PWMI_LOGGER_INFORMATION Info
    )
{
    ULONG SavedBufferSize = Info->Wnode.BufferSize;

    RtlCopyMemory(&Info->Wnode, &Properties->Wnode, sizeof(WNODE_HEADER));

    Info->Wnode.BufferSize = SavedBufferSize;

    Info->BufferSize            = Properties->BufferSize;
    Info->MinimumBuffers        = Properties->MinimumBuffers;
    Info->MaximumBuffers        = Properties->MaximumBuffers;
    Info->NumberOfBuffers       = Properties->NumberOfBuffers;
    Info->FreeBuffers           = Properties->FreeBuffers;
    Info->EventsLost            = Properties->EventsLost;
    Info->BuffersWritten        = Properties->BuffersWritten;
    Info->LoggerThreadId        = Properties->LoggerThreadId;
    Info->MaximumFileSize       = Properties->MaximumFileSize;
    Info->EnableFlags           = Properties->EnableFlags;
    Info->LogFileMode           = Properties->LogFileMode;
    Info->FlushTimer            = Properties->FlushTimer;
    Info->LogBuffersLost        = Properties->LogBuffersLost;
    Info->AgeLimit              = Properties->AgeLimit;
    Info->RealTimeBuffersLost   = Properties->RealTimeBuffersLost;
}

VOID
WmipCopyInfoToProperties(
    IN PWMI_LOGGER_INFORMATION Info,
    IN PEVENT_TRACE_PROPERTIES Properties
    )
{
    ULONG SavedSize = Properties->Wnode.BufferSize;
    RtlCopyMemory(&Properties->Wnode, &Info->Wnode, sizeof(WNODE_HEADER));
    Properties->Wnode.BufferSize = SavedSize;

    Properties->BufferSize            = Info->BufferSize;
    Properties->MinimumBuffers        = Info->MinimumBuffers;
    Properties->MaximumBuffers        = Info->MaximumBuffers;
    Properties->NumberOfBuffers       = Info->NumberOfBuffers;
    Properties->FreeBuffers           = Info->FreeBuffers;
    Properties->EventsLost            = Info->EventsLost;
    Properties->BuffersWritten        = Info->BuffersWritten;
    Properties->LoggerThreadId        = Info->LoggerThreadId;
    Properties->MaximumFileSize       = Info->MaximumFileSize;
    Properties->EnableFlags           = Info->EnableFlags;
    Properties->LogFileMode           = Info->LogFileMode;
    Properties->FlushTimer            = Info->FlushTimer;
    Properties->LogBuffersLost        = Info->LogBuffersLost;
    Properties->AgeLimit              = Info->AgeLimit;
    Properties->RealTimeBuffersLost   = Info->RealTimeBuffersLost;
}

NTSTATUS
WmipThreadRunDown(
    IN PWMI_LOGGER_CONTEXT Logger,
    IN PSYSTEM_PROCESS_INFORMATION pProcessInfo,
    IN ULONG StartFlag,
    IN BOOLEAN bExtended
    )
{
    PSYSTEM_THREAD_INFORMATION pThreadInfo;
    ULONG GroupType;
    ULONG i;
    ULONG Size;
    ULONG SystemThreadInfoSize;
    PWMI_EXTENDED_THREAD_INFORMATION ThreadInfo;

    pThreadInfo = (PSYSTEM_THREAD_INFORMATION) (pProcessInfo+1);

    GroupType = EVENT_TRACE_GROUP_THREAD +
                ((StartFlag) ? EVENT_TRACE_TYPE_DC_START
                             : EVENT_TRACE_TYPE_DC_END);

    Size = sizeof(WMI_EXTENDED_THREAD_INFORMATION);


    SystemThreadInfoSize = (bExtended)  ? sizeof(SYSTEM_EXTENDED_THREAD_INFORMATION)
                                        : sizeof(SYSTEM_THREAD_INFORMATION);
    for (i=0; i < pProcessInfo->NumberOfThreads; i++) {
        if (pThreadInfo == NULL)
            break;
        ThreadInfo = (PWMI_EXTENDED_THREAD_INFORMATION)
                      WmipGetTraceBuffer( Logger,
                                          pThreadInfo,
                                          GroupType,
                                          Size );

        if (ThreadInfo) {
            ThreadInfo->ProcessId =
                HandleToUlong(pThreadInfo->ClientId.UniqueProcess);
            ThreadInfo->ThreadId =
                HandleToUlong(pThreadInfo->ClientId.UniqueThread);

            if (bExtended) {
                PSYSTEM_EXTENDED_THREAD_INFORMATION pExtThreadInfo;
                pExtThreadInfo = (PSYSTEM_EXTENDED_THREAD_INFORMATION) pThreadInfo;
                ThreadInfo->StackBase = pExtThreadInfo->StackBase;
                ThreadInfo->StackLimit = pExtThreadInfo->StackLimit;

                ThreadInfo->StartAddr = pExtThreadInfo->ThreadInfo.StartAddress;
                ThreadInfo->Win32StartAddr = pExtThreadInfo->Win32StartAddress;
                ThreadInfo->UserStackBase = NULL;
                ThreadInfo->UserStackLimit = NULL;
                ThreadInfo->WaitMode = -1;
            }
            else {
                ThreadInfo->StackBase = NULL;
                ThreadInfo->StackLimit = NULL;
                ThreadInfo->StartAddr = NULL;
                ThreadInfo->Win32StartAddr = NULL;
                ThreadInfo->UserStackBase = NULL;
                ThreadInfo->UserStackLimit = NULL;
                ThreadInfo->WaitMode = -1;
            }
        }
        pThreadInfo  = (PSYSTEM_THREAD_INFORMATION)( (char*)pThreadInfo + SystemThreadInfoSize );
    }
    return STATUS_SUCCESS;
}

void
WmipLogImageLoadEvent(
    IN HANDLE ProcessID,
    IN PWMI_LOGGER_CONTEXT pLogger,
    IN PRTL_PROCESS_MODULE_INFORMATION pModuleInfo,
    IN PSYSTEM_THREAD_INFORMATION pThreadInfo
)
{
    UNICODE_STRING wstrModuleName;
    ANSI_STRING    astrModuleName;
    ULONG          sizeModuleName;
    ULONG          sizeBuffer;
    PCHAR          pAuxInfo;
    PWMI_IMAGELOAD_INFORMATION ImageLoadInfo;

    if ((pLogger == NULL) || (pModuleInfo == NULL) || (pThreadInfo == NULL))
        return;

    RtlInitAnsiString( & astrModuleName, pModuleInfo->FullPathName);

    sizeModuleName = sizeof(WCHAR) * (astrModuleName.Length);
    sizeBuffer     = sizeModuleName + sizeof(WCHAR)
                   + FIELD_OFFSET (WMI_IMAGELOAD_INFORMATION, FileName);

    ImageLoadInfo = (PWMI_IMAGELOAD_INFORMATION)
                     WmipGetTraceBuffer(
                        pLogger,
                        pThreadInfo,
                        EVENT_TRACE_GROUP_PROCESS + EVENT_TRACE_TYPE_LOAD,
                        sizeBuffer);

    if (ImageLoadInfo == NULL) {
        return;
    }

    ImageLoadInfo->ImageBase = pModuleInfo->ImageBase;
    ImageLoadInfo->ImageSize = pModuleInfo->ImageSize;
    ImageLoadInfo->ProcessId = HandleToUlong(ProcessID);

    wstrModuleName.Buffer    = (LPWSTR) &ImageLoadInfo->FileName[0];

    wstrModuleName.MaximumLength = (USHORT) sizeModuleName + sizeof(WCHAR);
    RtlAnsiStringToUnicodeString(& wstrModuleName, & astrModuleName, FALSE);
}

ULONG
WmipSysModuleRunDown(
    IN PWMI_LOGGER_CONTEXT        pLogger,
    IN PSYSTEM_THREAD_INFORMATION pThreadInfo
    )
{
    NTSTATUS   status = STATUS_SUCCESS;
    char     * pLargeBuffer1;
    ULONG      ReturnLength;
    ULONG      CurrentBufferSize;

    ULONG                           i;
    PRTL_PROCESS_MODULES            pModules;
    PRTL_PROCESS_MODULE_INFORMATION pModuleInfo;

    pLargeBuffer1 = WmipMemReserve(MAX_BUFFER_SIZE);

    if (pLargeBuffer1 == NULL)
    {
        status = STATUS_NO_MEMORY;
        goto Cleanup;
    }

    if (WmipMemCommit(pLargeBuffer1, BUFFER_SIZE) == NULL)
    {
        status = STATUS_NO_MEMORY;
        goto Cleanup;
    }

    CurrentBufferSize = BUFFER_SIZE;

retry:
    status = NtQuerySystemInformation(
                    SystemModuleInformation,
                    pLargeBuffer1,
                    CurrentBufferSize,
                    &ReturnLength);

    if (status == STATUS_INFO_LENGTH_MISMATCH)
    {
        // Increase buffer size.
        //
        CurrentBufferSize += 8192;

        if (WmipMemCommit(pLargeBuffer1, CurrentBufferSize) == NULL)
        {
            status = STATUS_NO_MEMORY;
            goto Cleanup;
        }
        goto retry;
    }

    if (!NT_SUCCESS(status))
    {
        goto Cleanup;
    }

    pModules = (PRTL_PROCESS_MODULES) pLargeBuffer1;

    for (i = 0, pModuleInfo = & (pModules->Modules[0]);
         i < pModules->NumberOfModules;
         i ++, pModuleInfo ++)
    {
        WmipLogImageLoadEvent(NULL, pLogger, pModuleInfo, pThreadInfo);
    }

Cleanup:
    if (pLargeBuffer1)
    {
        WmipMemFree(pLargeBuffer1);
    }
    return WmipSetNtStatus(status);
}

ULONG
WmipProcessModuleRunDown(
    IN PWMI_LOGGER_CONTEXT        pLogger,
    IN HANDLE                     ProcessID,
    IN PSYSTEM_THREAD_INFORMATION pThreadInfo)
{
    NTSTATUS               status = STATUS_SUCCESS;
    ULONG                  i;
    PRTL_DEBUG_INFORMATION pLargeBuffer1 = NULL;

    pLargeBuffer1 = RtlCreateQueryDebugBuffer(0, FALSE);
    if (pLargeBuffer1 == NULL)
    {
        status = STATUS_NO_MEMORY;
        goto Cleanup;
    }

    status = RtlQueryProcessDebugInformation(
                    ProcessID,
                    RTL_QUERY_PROCESS_MODULES,
                    pLargeBuffer1);

    if ( !NT_SUCCESS(status) || (pLargeBuffer1->Modules == NULL) )
    {
        goto Cleanup;
    }

    for (i = 0; i < pLargeBuffer1->Modules->NumberOfModules; i ++)
    {
        WmipLogImageLoadEvent(
                ProcessID,
                pLogger,
                & (pLargeBuffer1->Modules->Modules[i]),
                pThreadInfo);
    }

Cleanup:
    if (pLargeBuffer1)
    {
        RtlDestroyQueryDebugBuffer(pLargeBuffer1);
    }
    return WmipSetNtStatus(status);
}

NTSTATUS
WmipProcessRunDown(
    IN PWMI_LOGGER_CONTEXT Logger,
    IN ULONG StartFlag,
    IN ULONG fEnableFlags
    )
{
    PSYSTEM_PROCESS_INFORMATION  pProcessInfo;
    PSYSTEM_THREAD_INFORMATION   pThreadInfo;
    char* LargeBuffer1;
    NTSTATUS status;
    ULONG ReturnLength;
    ULONG CurrentBufferSize;
    ULONG GroupType;
    ULONG TotalOffset = 0;
    OBJECT_ATTRIBUTES objectAttributes;
    BOOLEAN WasEnabled = TRUE;
    BOOLEAN bExtended = TRUE;

    LargeBuffer1 = WmipMemReserve ( MAX_BUFFER_SIZE );
    if (LargeBuffer1 == NULL) {
        return STATUS_NO_MEMORY;
    }

    if (WmipMemCommit (LargeBuffer1, BUFFER_SIZE) == NULL) {
        return STATUS_NO_MEMORY;
    }

    CurrentBufferSize = BUFFER_SIZE;
    retry:
    if (bExtended) {
        status = NtQuerySystemInformation(
                    SystemExtendedProcessInformation,
                    LargeBuffer1,
                    CurrentBufferSize,
                    &ReturnLength
                    );
    }
    else {
        status = NtQuerySystemInformation(
                    SystemProcessInformation,
                    LargeBuffer1,
                    CurrentBufferSize,
                    &ReturnLength
                    );
    }

    if (status == STATUS_INFO_LENGTH_MISMATCH) {

        //
        // Increase buffer size.
        //

        CurrentBufferSize += 8192;

        if (WmipMemCommit (LargeBuffer1, CurrentBufferSize) == NULL) {
            return STATUS_NO_MEMORY;
        }
        goto retry;
    }

    if (!NT_SUCCESS(status)) {

        if (bExtended) {
            bExtended = FALSE;
            goto retry;
        }

        WmipMemFree(LargeBuffer1);
        return(status);
    }


    //
    // Adjust Privileges to obtain the module information
    //

    if (fEnableFlags & EVENT_TRACE_FLAG_IMAGE_LOAD) {
        status = RtlAdjustPrivilege(SE_DEBUG_PRIVILEGE,
                                    TRUE, FALSE, &WasEnabled);
        if (!NT_SUCCESS(status)) {
            WmipMemFree(LargeBuffer1);
            return  (status);
        }
    }


    TotalOffset = 0;
    pProcessInfo = (SYSTEM_PROCESS_INFORMATION *) LargeBuffer1;
    while (TRUE) {
        ULONG Size;
        ULONG Length = 0;
        ULONG SidLength = 0;
        PUCHAR AuxPtr;
        PULONG_PTR AuxInfo;
        ANSI_STRING s;
        HANDLE Token;
        HANDLE pProcess;
        PCLIENT_ID Cid;
        ULONG TempInfo[128];
        PWMI_PROCESS_INFORMATION WmiProcessInfo;

        Size = FIELD_OFFSET(WMI_PROCESS_INFORMATION, Sid);

        GroupType = EVENT_TRACE_GROUP_PROCESS +
                    ((StartFlag) ? EVENT_TRACE_TYPE_DC_START
                                 : EVENT_TRACE_TYPE_DC_END);

        pThreadInfo = (PSYSTEM_THREAD_INFORMATION) (pProcessInfo+1);
        if (pProcessInfo->NumberOfThreads > 0) {
            Cid = (PCLIENT_ID) &pThreadInfo->ClientId;
        }
        else {
            Cid = NULL;
        }

        // if at termination, rundown thread first before process
        if ( (!StartFlag) &&
             (fEnableFlags & EVENT_TRACE_FLAG_THREAD) ){
            status = WmipThreadRunDown(Logger,
                                       pProcessInfo,
                                       StartFlag,
                                       bExtended);
            if (!NT_SUCCESS(status)) {
                break;
            }

        }

        if (fEnableFlags & EVENT_TRACE_FLAG_PROCESS) {

            if ( pProcessInfo->ImageName.Buffer  &&
                     pProcessInfo->ImageName.Length > 0 ) {
                RtlUnicodeStringToAnsiString(
                                     &s,
                                     (PUNICODE_STRING)&pProcessInfo->ImageName,
                                     TRUE);
                Length = s.Length + 1;
            }
            else {
                Length = 1;
            }

            InitializeObjectAttributes(
                    &objectAttributes, 0, 0, NULL, NULL);
            status = NtOpenProcess(
                                  &pProcess,
                                  PROCESS_QUERY_INFORMATION,
                                  &objectAttributes,
                                  Cid);
            if (NT_SUCCESS(status)) {
                status = NtOpenProcessToken(
                                      pProcess,
                                      TOKEN_READ,
                                      &Token);
                if (NT_SUCCESS(status)) {

                    status = NtQueryInformationToken(
                                             Token,
                                             TokenUser,
                                             TempInfo,
                                             256,
                                             &SidLength);
                    NtClose(Token);
                }
                NtClose(pProcess);
            }
            if ( (!NT_SUCCESS(status)) || SidLength <= 0) {
                TempInfo[0] = 0;
                SidLength = sizeof(ULONG);
            }

            Size += Length + SidLength;
            WmiProcessInfo = (PWMI_PROCESS_INFORMATION)
                              WmipGetTraceBuffer( Logger,
                                                  pThreadInfo,
                                                  GroupType,
                                                  Size);
            if (WmiProcessInfo == NULL) {
                status = STATUS_NO_MEMORY;
                break;
            }
            WmiProcessInfo->ProcessId = HandleToUlong(pProcessInfo->UniqueProcessId);
            WmiProcessInfo->ParentId = HandleToUlong(pProcessInfo->InheritedFromUniqueProcessId);
            WmiProcessInfo->SessionId = pProcessInfo->SessionId;
            WmiProcessInfo->PageDirectoryBase = pProcessInfo->PageDirectoryBase;
            WmiProcessInfo->ExitStatus = 0;

            AuxPtr = (PUCHAR) (&WmiProcessInfo->Sid);

            RtlCopyMemory(AuxPtr, &TempInfo, SidLength);
            AuxPtr += SidLength;

            if ( Length > 1) {
                RtlCopyMemory(AuxPtr, s.Buffer, Length);
                AuxPtr += Length;
                RtlFreeAnsiString(&s);
            }
            *AuxPtr = '\0';
            AuxPtr++;
        }

        // if at beginning, trace threads after process
        if (StartFlag) {

            if (fEnableFlags & EVENT_TRACE_FLAG_THREAD) {
                WmipThreadRunDown(Logger, pProcessInfo, StartFlag, bExtended);
            }

            if (fEnableFlags & EVENT_TRACE_FLAG_IMAGE_LOAD) {
                if (pProcessInfo->UniqueProcessId == 0) {
                    WmipSysModuleRunDown(Logger, pThreadInfo);
                }
                else
                    WmipProcessModuleRunDown(
                            Logger,
                            (HANDLE) pProcessInfo->UniqueProcessId,
                            pThreadInfo);
            }
        }
        if (pProcessInfo->NextEntryOffset == 0) {
            break;
        }
        TotalOffset += pProcessInfo->NextEntryOffset;
        pProcessInfo = (PSYSTEM_PROCESS_INFORMATION)&LargeBuffer1[TotalOffset];
    }

    //
    // Restore privileges back to what it was before
    //

    if (fEnableFlags & EVENT_TRACE_FLAG_IMAGE_LOAD) {
        status = RtlAdjustPrivilege(SE_DEBUG_PRIVILEGE,
                                    WasEnabled,
                                    FALSE,
                                    &WasEnabled);
    }


    WmipMemFree(LargeBuffer1);

    return status;
}

VOID
WmipInitString(
    IN PVOID Destination,
    IN PVOID Buffer,
    IN ULONG Size
    )
{
    PSTRING s = (PSTRING) Destination;

    s->Buffer = Buffer;
    s->Length = 0;
    if (Buffer != NULL)
        s->MaximumLength = (USHORT) Size;
    else
        s->MaximumLength = 0;
}

VOID
WmipGenericTraceEnable(
    IN ULONG RequestCode,
    IN PVOID Buffer,
    IN OUT PVOID *RequestAddress
    )
{
    PGUIDMAPENTRY pControlGMEntry = *RequestAddress;
    PWNODE_HEADER Wnode = (PWNODE_HEADER) Buffer;
    PTRACE_REG_INFO pTraceRegInfo;
    PTRACE_ENABLE_CONTEXT pContext = (PTRACE_ENABLE_CONTEXT)&Wnode->HistoricalContext;

    *RequestAddress = NULL;

    if (Wnode == NULL || pControlGMEntry == NULL)
        return;

    if (!Wnode->Flags & WNODE_FLAG_TRACED_GUID)
        return;

    pTraceRegInfo  = pControlGMEntry->pControlGuidData;

    WmipAssert(pTraceRegInfo != NULL);


    if (pTraceRegInfo->InProgressEvent != NULL) {
        LARGE_INTEGER Timeout = {(ULONG)(-NOTIFY_RETRY_COUNT * 1000 * 10), -1};
// TODO: Raghu - what if it times out??
        NtWaitForSingleObject(pTraceRegInfo->InProgressEvent, 0, &Timeout);
    }

    *RequestAddress = pTraceRegInfo->NotifyRoutine;

    if (RequestCode == WMI_ENABLE_EVENTS) {
        pControlGMEntry->LoggerContext = Wnode->HistoricalContext;
        if (pContext->InternalFlag & EVENT_TRACE_INTERNAL_FLAG_PRIVATE) {
            pTraceRegInfo->EnabledState = TRUE;
            if (!WmipIsPrivateLoggerOn())
               *RequestAddress = NULL; // Do not notify if the logger is not up.
        }
    }
    else if (RequestCode == WMI_DISABLE_EVENTS) {
        if (pContext->InternalFlag & EVENT_TRACE_INTERNAL_FLAG_PRIVATE) {
            pTraceRegInfo->EnabledState = FALSE;
        }
        pControlGMEntry->LoggerContext = 0;
    }
}

ULONG
WmipAddLogHeaderToLogFile(
    IN OUT PWMI_LOGGER_INFORMATION LoggerInfo,
    IN PWMI_REF_CLOCK              RefClock,
    IN     ULONG                   Update
    )
{
    NTSTATUS Status;
    HANDLE LogFile = INVALID_HANDLE_VALUE;
    ULONG BufferSize;
    ULONG MemorySize;
    ULONG TraceKernel;
    SYSTEM_BASIC_INFORMATION SystemInfo;
    WMI_LOGGER_CONTEXT Logger;
    IO_STATUS_BLOCK IoStatus;
    PWMI_BUFFER_HEADER Buffer;
    FILE_POSITION_INFORMATION FileInfo;
    LPWSTR FileName = NULL;
    LPWSTR FileNameBuffer = NULL;
    ULONG HeaderSize;

    struct WMI_LOGFILE_HEADER {
           WMI_BUFFER_HEADER    BufferHeader;
           SYSTEM_TRACE_HEADER  SystemHeader;
           TRACE_LOGFILE_HEADER LogFileHeader;
    };
    struct WMI_LOGFILE_HEADER LoggerBuffer;
    BOOLEAN bLogFileAppend =
                    (LoggerInfo->LogFileMode & EVENT_TRACE_FILE_MODE_APPEND)
                  ? (TRUE) : (FALSE);

    if (LoggerInfo == NULL)
        return WmipSetDosError(ERROR_INVALID_PARAMETER);

    if (LoggerInfo->Wnode.BufferSize < sizeof(WMI_LOGGER_INFORMATION))
        return WmipSetDosError(ERROR_INVALID_PARAMETER);

    if ( !(LoggerInfo->Wnode.Flags & WNODE_FLAG_TRACED_GUID) )
        return WmipSetDosError(ERROR_INVALID_PARAMETER);

    if ((LoggerInfo->LogFileMode & EVENT_TRACE_FILE_MODE_NEWFILE)  &&
        (LoggerInfo->LogFileName.Length > 0)) {
        FileName = (LPWSTR) WmipAlloc(LoggerInfo->LogFileName.Length + 64);
        if (FileName == NULL) {
            return WmipSetDosError(ERROR_NOT_ENOUGH_MEMORY);
        }
        swprintf(FileName, LoggerInfo->LogFileName.Buffer, 1);
        FileNameBuffer = FileName;
    }
    if (FileName == NULL)
        FileName = (LPWSTR) LoggerInfo->LogFileName.Buffer;

    //
    // If it is Append Mode, we need to open the file and make sure the
    // pick up the BufferSize
    //

    if ( bLogFileAppend ) {

        ULONG ReadSize   = sizeof(WMI_BUFFER_HEADER)
                         + sizeof(SYSTEM_TRACE_HEADER)
                         + sizeof(TRACE_LOGFILE_HEADER);
        ULONG nBytesRead = 0;

        //
        //  Update and Append do not mix. To Append LoggerInfo
        //  must have LogFileName
        //

        if ( (Update) || (LoggerInfo->LogFileName.Length <= 0) ) {
            if (FileNameBuffer != NULL) {
                WmipFree(FileNameBuffer);
            }
            return WmipSetDosError(ERROR_INVALID_PARAMETER);
        }

        LogFile = WmipCreateFileW(FileName,
                              GENERIC_READ | GENERIC_WRITE,
                              FILE_SHARE_READ,
                              NULL,
                              OPEN_EXISTING,
                              FILE_ATTRIBUTE_NORMAL,
                              NULL);
        if (LogFile == INVALID_HANDLE_VALUE) {
            // cannot OPEN_EXISTING, assume that logfile is not there and
            // create a new one.
            //
            bLogFileAppend = FALSE;
            LoggerInfo->LogFileMode = LoggerInfo->LogFileMode
                                    & (~ (EVENT_TRACE_FILE_MODE_APPEND));
        }

        else {
            // read TRACE_LOGFILE_HEADER structure and update LoggerInfo
            // members.
            //
            Status = WmipReadFile(LogFile,
                              (LPVOID) & LoggerBuffer,
                              ReadSize,
                              & nBytesRead,
                              NULL);
            if (nBytesRead < ReadSize) {
                NtClose(LogFile);
                if (FileNameBuffer != NULL) {
                    WmipFree(FileNameBuffer);
                }
                return WmipSetDosError(ERROR_BAD_PATHNAME);
            }
            if (  LoggerBuffer.LogFileHeader.LogFileMode
                & EVENT_TRACE_FILE_MODE_CIRCULAR) {
                NtClose(LogFile);
                if (FileNameBuffer != NULL) {
                    WmipFree(FileNameBuffer);
                }
                return WmipSetDosError(ERROR_BAD_PATHNAME);
            }
            LoggerInfo->BufferSize      =
                            LoggerBuffer.LogFileHeader.BufferSize / 1024;
            LoggerInfo->BuffersWritten  =
                            LoggerBuffer.LogFileHeader.BuffersWritten;
            LoggerInfo->MaximumFileSize =
                            LoggerBuffer.LogFileHeader.MaximumFileSize;

            // Write back logfile append mode so WmipFinalizeLogFile() correctly
            // update BuffersWritten field
            //
            FileInfo.CurrentByteOffset.QuadPart =
                            LOGFILE_FIELD_OFFSET(EndTime);
            Status = NtSetInformationFile(LogFile,
                                          & IoStatus,
                                          & FileInfo,
                                          sizeof(FILE_POSITION_INFORMATION),
                                          FilePositionInformation);
            if (!NT_SUCCESS(Status)) {
                NtClose(LogFile);
                if (FileNameBuffer != NULL) {
                    WmipFree(FileNameBuffer);
                }
                return WmipSetNtStatus(Status);
            }
            LoggerBuffer.LogFileHeader.EndTime.QuadPart = 0;
            Status = NtWriteFile(LogFile,
                                 NULL,
                                 NULL,
                                 NULL,
                                 & IoStatus,
                                 & LoggerBuffer.LogFileHeader.EndTime,
                                 sizeof(LARGE_INTEGER),
                                 NULL,
                                 NULL);
            if (! NT_SUCCESS(Status)) {
                NtClose(LogFile);
                if (FileNameBuffer != NULL) {
                    WmipFree(FileNameBuffer);
                }
                return WmipSetNtStatus(Status);
            }

            // build checksum structure
            //
            if (LoggerInfo->LogFileMode & EVENT_TRACE_PRIVATE_LOGGER_MODE) {
                LoggerInfo->Checksum = NULL;
            }
            else {
                LoggerInfo->Checksum = WmipAlloc(
                        sizeof(WNODE_HEADER) + sizeof(TRACE_LOGFILE_HEADER));
                if (LoggerInfo->Checksum != NULL) {
                    PBYTE ptrChecksum = LoggerInfo->Checksum;
                    RtlCopyMemory(ptrChecksum,
                                  & LoggerBuffer.BufferHeader,
                                  sizeof(WNODE_HEADER));
                    ptrChecksum += sizeof(WNODE_HEADER);
                    RtlCopyMemory(ptrChecksum,
                                  & LoggerBuffer.LogFileHeader,
                                  sizeof(TRACE_LOGFILE_HEADER));
                }
                else {
                    if (FileNameBuffer != NULL) {
                        WmipFree(FileNameBuffer);
                    }
                    return WmipSetDosError(ERROR_NOT_ENOUGH_MEMORY);
                }
            }
        }
    }

    // get the system parameters first

    LoggerInfo->LogFileHandle = NULL;

    Status = NtQuerySystemInformation(
                SystemBasicInformation,
                &SystemInfo, sizeof (SystemInfo), NULL);

    if (!NT_SUCCESS(Status)) {
        if (FileNameBuffer != NULL) {
            WmipFree(FileNameBuffer);
        }
        return WmipSetNtStatus(Status);
    }

    // choose some logical default value for buffer size if user
    // has not provided one

    MemorySize = SystemInfo.NumberOfPhysicalPages * SystemInfo.PageSize
                    / 1024 / 1024;
    if (MemorySize <= 32)
        BufferSize      = SystemInfo.PageSize;
    else if (MemorySize <= 64)
        BufferSize      = SystemInfo.PageSize;
    else if (MemorySize <= 128)
        BufferSize      = SystemInfo.PageSize * 2;
    else if (MemorySize <= 256)
        BufferSize      = SystemInfo.PageSize * 2;
    else if (MemorySize > 512)
        BufferSize      = SystemInfo.PageSize * 2;
    else if (MemorySize <= 256)
        BufferSize      = SystemInfo.PageSize * 2;
    else if (MemorySize > 512)
        BufferSize      = 64 * 1024;        // allocation size
    else       // > 256 && < 512
        BufferSize      = SystemInfo.PageSize * 2;

    if (LoggerInfo->BufferSize > 1024)      // limit to 1Mb
        BufferSize = 1024 * 1024;
    else if (LoggerInfo->BufferSize > 0)
        BufferSize = LoggerInfo->BufferSize * 1024;

    TraceKernel = IsEqualGUID(&LoggerInfo->Wnode.Guid, &SystemTraceControlGuid);
    if (!TraceKernel) {
        GUID guid;
        RtlZeroMemory(&guid, sizeof(GUID));
        if (IsEqualGUID(&LoggerInfo->Wnode.Guid, &guid)) {
            // Generate a Guid for this logger stream
            // This will ensure  buffer filtering at the WMI service
            // based on this GUID.
            UUID uid;
            WmipUuidCreate(&uid);
            LoggerInfo->Wnode.Guid = uid;
        }
    }

    if (!Update) {
        // don't want to update BufferSize information if the request is
        // to update logger session
        //
        LoggerInfo->BufferSize = BufferSize / 1024;
    }

    if (LoggerInfo->LogFileName.Length <= 0)
        return  ERROR_SUCCESS; //goto SendToKm;
    //
    // We assumed the exposed API has checked for either RealTime or FileName
    // is provided

    //
    // open the file for writing synchronously for the logger
    // others may want to read it as well.
    // For logfile append mode, logfile has been opened previously
    //
    if (! bLogFileAppend) {
/*        LogFile = WmipCreateFileW(
                       (PWCHAR) LoggerInfo->LogFileName.Buffer,
                       GENERIC_WRITE,
                       FILE_SHARE_READ,
                       NULL,
                       CREATE_ALWAYS,
                       FILE_ATTRIBUTE_NORMAL,
                       NULL
                       );  */
        LogFile = WmipCreateFile(
                    FileName,
                    FILE_GENERIC_WRITE,
                    FILE_SHARE_READ,
                    FILE_OVERWRITE_IF,
                    0);

        if (LogFile == INVALID_HANDLE_VALUE) {
            if (FileNameBuffer != NULL) {
                WmipFree(FileNameBuffer);
            }
            return WmipSetDosError(ERROR_BAD_PATHNAME);
        }
    }

    LoggerInfo->LogFileHandle = LogFile;


    //
    // If this is an Update call, then we need to pick up the original
    // buffer size for the LogFileHeader.
    //

    if (Update) {
        PWMI_LOGGER_INFORMATION pTempLoggerInfo;
        PWCHAR strLoggerName = NULL;
        PWCHAR strLogFileName = NULL;
        ULONG ErrCode;
        ULONG SizeNeeded = sizeof(WMI_LOGGER_INFORMATION) + MAXSTR * sizeof(WCHAR) * 2;
        SizeNeeded = (SizeNeeded +7) & ~7;
        pTempLoggerInfo = WmipAlloc(SizeNeeded);
        if (pTempLoggerInfo == NULL) {
            return WmipSetDosError(ERROR_NOT_ENOUGH_MEMORY);
        }
        RtlZeroMemory(pTempLoggerInfo, SizeNeeded);
        pTempLoggerInfo->Wnode.BufferSize = SizeNeeded;
        pTempLoggerInfo->Wnode.Flags |= WNODE_FLAG_TRACED_GUID;
        pTempLoggerInfo->Wnode.HistoricalContext = LoggerInfo->Wnode.HistoricalContext;
        pTempLoggerInfo->Wnode.Guid = LoggerInfo->Wnode.Guid;

        if (LoggerInfo->LogFileMode & EVENT_TRACE_PRIVATE_LOGGER_MODE) {
            pTempLoggerInfo->LogFileMode |= EVENT_TRACE_PRIVATE_LOGGER_MODE;
        }

        strLoggerName = (PWCHAR) ( ((PUCHAR) pTempLoggerInfo)
                                    + sizeof(WMI_LOGGER_INFORMATION));
        WmipInitString(&pTempLoggerInfo->LoggerName,
                       strLoggerName,
                       MAXSTR * sizeof(WCHAR));
        if (LoggerInfo->LoggerName.Length > 0) {
            RtlCopyUnicodeString( &pTempLoggerInfo->LoggerName,
                                  &LoggerInfo->LoggerName);
        }


        strLogFileName = (PWCHAR) ( ((PUCHAR) pTempLoggerInfo)
                                    + sizeof(WMI_LOGGER_INFORMATION)
                                    + MAXSTR * sizeof(WCHAR) );
        WmipInitString(&pTempLoggerInfo->LogFileName,
                       strLogFileName,
                       MAXSTR * sizeof(WCHAR) );

        //
        // Call QueryLogger
        //
        ErrCode = WmipQueryLogger(pTempLoggerInfo, FALSE);

        BufferSize = pTempLoggerInfo->BufferSize * 1024;
        WmipFree(pTempLoggerInfo);

        if (ErrCode != ERROR_SUCCESS) {
            return ErrCode;
        }
    }
    //
    // Before Allocating the Buffer for Logfile Header make
    // sure the buffer size is atleast as large as the LogFileHeader
    //

    HeaderSize =  sizeof(LoggerBuffer)
                        + LoggerInfo->LoggerName.Length + sizeof(WCHAR)
                        + LoggerInfo->LogFileName.Length + sizeof(WCHAR);

    if (HeaderSize > BufferSize) {
        //
        //  Round it to the nearest power of 2 and check for max size 1 MB
        //
        double dTemp = log (HeaderSize / 1024.0) / log (2.0);
        ULONG lTemp = (ULONG) (dTemp + 0.99);
        HeaderSize = (1 << lTemp);
        if (HeaderSize > 1024) {
            NtClose(LogFile);
            if (FileNameBuffer != NULL) {
                WmipFree(FileNameBuffer);
            }
            return WmipSetDosError(ERROR_NOT_ENOUGH_MEMORY);
        }
        LoggerInfo->BufferSize = HeaderSize;
        BufferSize = HeaderSize * 1024;
    }



    // allocate a buffer to write logger header and process/thread
    // rundown information
    //
    Logger.LogFileHandle   = LogFile;
    Logger.BufferSize      = BufferSize;
    Logger.TimerResolution = SystemInfo.TimerResolution;
    Logger.BufferSpace          = WmipAlloc(BufferSize);
    if (Logger.BufferSpace == NULL) {
        NtClose(LogFile);
        if (FileNameBuffer != NULL) {
            WmipFree(FileNameBuffer);
        }
        return WmipSetDosError(ERROR_NOT_ENOUGH_MEMORY);
    }
    if (LoggerInfo->Wnode.ClientContext != EVENT_TRACE_CLOCK_SYSTEMTIME) {
        LoggerInfo->Wnode.ClientContext = EVENT_TRACE_CLOCK_PERFCOUNTER;
    }
    Logger.UsePerfClock = LoggerInfo->Wnode.ClientContext;

    // initialize buffer first
    RtlZeroMemory(Logger.BufferSpace, BufferSize);
    Buffer         = (PWMI_BUFFER_HEADER) Logger.BufferSpace;
    Buffer->Offset = sizeof(WMI_BUFFER_HEADER);
    if (TraceKernel) {
        Buffer->Wnode.Guid   = SystemTraceControlGuid;
    }
    else {
        Buffer->Wnode.Guid   = LoggerInfo->Wnode.Guid;
    }
    Buffer->Wnode.BufferSize = BufferSize;
    Buffer->ClientContext.Alignment = (UCHAR)WmiTraceAlignment;
    Buffer->Wnode.Flags      = WNODE_FLAG_TRACED_GUID;

    if (bLogFileAppend) {
        Logger.BuffersWritten  = LoggerBuffer.LogFileHeader.BuffersWritten;
        WmipSetFilePointer(LogFile, 0, NULL, FILE_END);
    }
    else {
        PTRACE_LOGFILE_HEADER LogfileHeader;
        OSVERSIONINFO sVersionInfo;

        Logger.BuffersWritten  = 0;
        HeaderSize =  sizeof(TRACE_LOGFILE_HEADER)
                        + LoggerInfo->LoggerName.Length + sizeof(WCHAR)
                        + LoggerInfo->LogFileName.Length + sizeof(WCHAR);



        LogfileHeader = (PTRACE_LOGFILE_HEADER)
                        WmipGetTraceBuffer(
                            &Logger,
                            NULL,
                            EVENT_TRACE_GROUP_HEADER + EVENT_TRACE_TYPE_INFO,
                            HeaderSize
                            );
        if (LogfileHeader == NULL) {
            NtClose(LogFile);
            WmipFree(Logger.BufferSpace);
            if (FileNameBuffer != NULL) {
                WmipFree(FileNameBuffer);
            }
            return WmipSetDosError(ERROR_NOT_ENOUGH_MEMORY);
        }

        if (Logger.UsePerfClock == EVENT_TRACE_CLOCK_PERFCOUNTER) {
            LARGE_INTEGER CurrentTime;
            LARGE_INTEGER Frequency;
            Status = NtQueryPerformanceCounter(&CurrentTime, &Frequency);
            LogfileHeader->PerfFreq = Frequency;

            LogfileHeader->ReservedFlags = EVENT_TRACE_CLOCK_PERFCOUNTER;
        }

        //
        // Start and End Times are wall clock time
        //
        if (RefClock != NULL) {
            PSYSTEM_TRACE_HEADER Header;
            LogfileHeader->StartTime = RefClock->StartTime;
            Header = (PSYSTEM_TRACE_HEADER) ( (char *) LogfileHeader - sizeof(SYSTEM_TRACE_HEADER) );
            Header->SystemTime = RefClock->StartPerfClock;
        }
        else {
            LogfileHeader->StartTime.QuadPart = WmipGetSystemTime();
        }

        RtlZeroMemory(&sVersionInfo, sizeof(OSVERSIONINFO));
        sVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
        WmipGetVersionExA(&sVersionInfo);

        LogfileHeader->BufferSize = BufferSize;
        LogfileHeader->VersionDetail.MajorVersion =
                                         (UCHAR)sVersionInfo.dwMajorVersion;
        LogfileHeader->VersionDetail.MinorVersion =
                                         (UCHAR)sVersionInfo.dwMinorVersion;
        LogfileHeader->VersionDetail.SubVersion = TRACE_VERSION_MAJOR;
        LogfileHeader->VersionDetail.SubMinorVersion = TRACE_VERSION_MINOR;
        LogfileHeader->ProviderVersion = sVersionInfo.dwBuildNumber;
        LogfileHeader->StartBuffers = 1;
        LogfileHeader->LogFileMode
                = LoggerInfo->LogFileMode & (~(EVENT_TRACE_REAL_TIME_MODE));
        LogfileHeader->NumberOfProcessors = SystemInfo.NumberOfProcessors;
        if (LoggerInfo->LogFileMode & EVENT_TRACE_PRIVATE_LOGGER_MODE)
        {
            if ( (LoggerInfo->NumberOfProcessors > 0) &&
                 (LoggerInfo->LogFileMode & EVENT_TRACE_RELOG_MODE) ) {
                LogfileHeader->NumberOfProcessors = LoggerInfo->NumberOfProcessors;
            }
            else {
                LoggerInfo->NumberOfProcessors = SystemInfo.NumberOfProcessors;
            }
        }

        LogfileHeader->MaximumFileSize = LoggerInfo->MaximumFileSize;

        LogfileHeader->TimerResolution = SystemInfo.TimerResolution;
        LogfileHeader->LoggerName = (PWCHAR) ( (PUCHAR) LogfileHeader
                                    + sizeof(TRACE_LOGFILE_HEADER) );
        LogfileHeader->LogFileName = (PWCHAR) ((PUCHAR)LogfileHeader->LoggerName
                                    + LoggerInfo->LoggerName.Length
                                    + sizeof (WCHAR));
        RtlCopyMemory(LogfileHeader->LoggerName,
                    LoggerInfo->LoggerName.Buffer,
                    LoggerInfo->LoggerName.Length + sizeof(WCHAR));
        RtlCopyMemory(LogfileHeader->LogFileName,
                    LoggerInfo->LogFileName.Buffer,
                    LoggerInfo->LogFileName.Length + sizeof(WCHAR));
        WmipGetTimeZoneInformation(&LogfileHeader->TimeZone);
        LogfileHeader->PointerSize = sizeof(PVOID);

        if (LoggerInfo->LogFileMode & EVENT_TRACE_PRIVATE_LOGGER_MODE) {
            LoggerInfo->Checksum = NULL;
        }
        else {
            LoggerInfo->Checksum = WmipAlloc(
                            sizeof(WNODE_HEADER)
                          + sizeof(TRACE_LOGFILE_HEADER));
            if (LoggerInfo->Checksum != NULL) {
                PBYTE ptrChecksum = LoggerInfo->Checksum;
                RtlCopyMemory(ptrChecksum, Buffer, sizeof(WNODE_HEADER));
                ptrChecksum += sizeof(WNODE_HEADER);
                RtlCopyMemory(
                    ptrChecksum, LogfileHeader, sizeof(TRACE_LOGFILE_HEADER));
            }
            else {
                NtClose(LogFile);
                WmipFree(Logger.BufferSpace);
                if (FileNameBuffer != NULL) {
                    WmipFree(FileNameBuffer);
                }
                return WmipSetDosError(ERROR_NOT_ENOUGH_MEMORY);
            }
        }
    }

    // Dump the GuidMaps to File at the Start
    //
    if (!Update) {
        if (TraceKernel) {
            ULONG EnableFlags = LoggerInfo->EnableFlags;

            if (EnableFlags & EVENT_TRACE_FLAG_EXTENSION) {
                PTRACE_ENABLE_FLAG_EXTENSION tFlagExt;

                tFlagExt = (PTRACE_ENABLE_FLAG_EXTENSION)
                           &LoggerInfo->EnableFlags;
                EnableFlags = *(PULONG)((PCHAR)LoggerInfo + tFlagExt->Offset);
            }

            WmipDumpHardwareConfig(&Logger);


            WmipProcessRunDown( &Logger, TRUE, EnableFlags );
        }
        else {
            WmipDumpGuidMaps(&Logger, NULL, FALSE);
        }
    }

    Buffer = (PWMI_BUFFER_HEADER) Logger.BufferSpace;
    // flush the last buffer
    if (Buffer->Offset < Logger.BufferSize) {
        RtlFillMemory(
                (char *) Buffer + Buffer->Offset,
                Logger.BufferSize - Buffer->Offset,
                0xFF);
    }
    Status = NtWriteFile(
            LogFile,
            NULL,
            NULL,
            NULL,
            &IoStatus,
            Logger.BufferSpace,
            BufferSize,
            NULL,
            NULL);

    Logger.BuffersWritten++;

    if ((LoggerInfo->LogFileMode == EVENT_TRACE_FILE_MODE_CIRCULAR) ) {
        //
        // We need to write the number of StartBuffers in the
        // Circular Logfile header to process it properly.

        FileInfo.CurrentByteOffset.QuadPart =
                            LOGFILE_FIELD_OFFSET(StartBuffers);

        Status = NtSetInformationFile(
                             LogFile,
                             &IoStatus,
                             &FileInfo,
                             sizeof(FILE_POSITION_INFORMATION),
                             FilePositionInformation
                             );
        if (!NT_SUCCESS(Status)) {
            NtClose(LogFile);
            if (FileNameBuffer != NULL) {
                WmipFree(FileNameBuffer);
            }
            return WmipSetNtStatus(Status);
        }

        Status = NtWriteFile(
                            LogFile,
                            NULL,
                            NULL,
                            NULL,
                            &IoStatus,
                            &Logger.BuffersWritten,
                            sizeof(ULONG),
                            NULL,
                            NULL
                            );
        if (NT_SUCCESS(Status)) {
            PTRACE_LOGFILE_HEADER pLogFileHeader;

            NtFlushBuffersFile(Logger.LogFileHandle, &IoStatus);

            // update StartBuffers in Checksum
            //
            pLogFileHeader = (PTRACE_LOGFILE_HEADER)
                     (((PUCHAR) LoggerInfo->Checksum) + sizeof(WNODE_HEADER));
            pLogFileHeader->StartBuffers = Logger.BuffersWritten;
        }
    }

    //
    // As a last thing update the Number of BuffersWritten so far
    // in the header and also update the checksum. This is to prevent
    // Logger failing Update calls under high load.
    //

    FileInfo.CurrentByteOffset.QuadPart =
                    LOGFILE_FIELD_OFFSET(BuffersWritten);

    Status = NtSetInformationFile(
                             LogFile,
                             &IoStatus,
                             &FileInfo,
                             sizeof(FILE_POSITION_INFORMATION),
                             FilePositionInformation
                             );
    if (!NT_SUCCESS(Status)) {
        NtClose(LogFile);
        return WmipSetNtStatus(Status);
    }

    Status = NtWriteFile(
                        LogFile,
                        NULL,
                        NULL,
                        NULL,
                        &IoStatus,
                        &Logger.BuffersWritten,
                        sizeof(ULONG),
                        NULL,
                        NULL
                        );
    if (NT_SUCCESS(Status)) {
        PTRACE_LOGFILE_HEADER pLogFileHeader;

        NtFlushBuffersFile(Logger.LogFileHandle, &IoStatus);

        // update StartBuffers in Checksum
        //
        if ( !(LoggerInfo->LogFileMode & EVENT_TRACE_PRIVATE_LOGGER_MODE) ) {
            pLogFileHeader = (PTRACE_LOGFILE_HEADER)
                     (((PUCHAR) LoggerInfo->Checksum) + sizeof(WNODE_HEADER));
            pLogFileHeader->BuffersWritten = Logger.BuffersWritten;
        }
    }

    NtClose(LogFile);

    LogFile = WmipCreateFileW(
                 FileName,
                 GENERIC_WRITE,
                 FILE_SHARE_READ,
                 NULL,
                 OPEN_EXISTING,
                 FILE_FLAG_NO_BUFFERING,
                 NULL
                 );
/*    LogFile = WmipCreateFile(
                FileName,
                FILE_GENERIC_WRITE,
                FILE_SHARE_READ,
                FILE_OPEN,
                FILE_NO_INTERMEDIATE_BUFFERING);
*/
    if (FileNameBuffer != NULL) {
        WmipFree(FileNameBuffer);
    }

    if (LogFile == INVALID_HANDLE_VALUE) {
        return WmipGetLastError();
    }
    LoggerInfo->LogFileHandle = LogFile;
    LoggerInfo->BuffersWritten = Logger.BuffersWritten;
    WmipFree(Logger.BufferSpace);
    return ERROR_SUCCESS;
}

ULONG
WmipDumpGuidMaps(
    IN PWMI_LOGGER_CONTEXT Logger,
    IN PLIST_ENTRY GuidMapListHeadPtr,
    IN ULONG RealTimeFlag
    )
/*++

Routine Description:

    This routine communicates with the WMI Service and obtains all the
    Trace Guids that are currently registered and those guids that unregistered
    in the middle of a Logging session.
    The GuidMaps are dumped to the logfile or added to the current GuidMapList
    for real time processing.

Arguments:

    Logger          Logger Context.
    RealTimeFlag    Flag to denote real time processing.

Return Value:

    Returns the number of Trace GUIDS obtained from the WMI service.

--*/
{
#if 0	
    ULONG Status;
    PTRACEGUIDMAP GuidMapList = NULL;
    ULONG MaxGuidCount;
    ULONG TotalGuidCount;
    ULONG ReturnGuidCount;
    ULONG BusyRetries;

    // Ensure that wmi service is around and willing to send us notifications
    Status = WmipEnableNotifications();
    if (Status != ERROR_SUCCESS)
    {
        WmipSetLastError(Status);
        return(0);
    }
    MaxGuidCount = 10;
retry:
    TotalGuidCount = 0;
    ReturnGuidCount = 0;
    GuidMapList = WmipAlloc(MaxGuidCount * sizeof(TRACEGUIDMAP));
    if (GuidMapList == NULL)
    {
        WmipSetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return (0);
    }
    RtlZeroMemory(GuidMapList, MaxGuidCount * sizeof(TRACEGUIDMAP));

    BusyRetries = 0;
    do
    {
       try
       {
#if 0	
           Status = EnumerateTraceGuidMap(WmipDcCtxHandle,
                                      MaxGuidCount,
                                      &TotalGuidCount,
                                      &ReturnGuidCount,
                                      GuidMapList);
#endif			
       } except(EXCEPTION_EXECUTE_HANDLER) {
           Status = GetExceptionCode();
           if (Status == STATUS_ACCESS_VIOLATION)
           {
                Status = ERROR_NOACCESS;
                WmipFree(GuidMapList);
                WmipSetLastError(Status);
                return (0);
           }
       }
        if (Status == RPC_S_SERVER_TOO_BUSY)
        {
            WmipDebugPrint(("WMI: EnumerateTraceGuidMap too busy for the %d time\n",
                                      BusyRetries));
            WmipSleep(RPC_BUSY_WAIT_TIMER);
            BusyRetries++;
        }
    } while ((Status == RPC_S_SERVER_TOO_BUSY) &&
             (BusyRetries < RPC_BUSY_WAIT_RETRIES));

    //
    // If RPC was successful, then write out these events to logfile.
    //

    if (Status == ERROR_MORE_DATA) {
        MaxGuidCount = TotalGuidCount;
        WmipFree(GuidMapList);
        goto retry;
    }
    else if (Status == ERROR_SUCCESS) {
       ULONG GroupType;
       ULONG i;
       ULONG Size;
       PULONG AuxInfo;
       PTRACEGUIDMAP pTraceGuidMap = GuidMapList;


       GroupType = EVENT_TRACE_GROUP_HEADER + EVENT_TRACE_TYPE_GUIDMAP;
       Size = sizeof(TRACEGUIDMAP);

       for (i=0; i < ReturnGuidCount; i++) {
            if (RealTimeFlag) {
                WmipAddGuidHandleToGuidMapList(GuidMapListHeadPtr, pTraceGuidMap->GuidMapHandle,
                                               &pTraceGuidMap->Guid);
            }
            else {
                AuxInfo = (PULONG) WmipGetTraceBuffer(
                               Logger,
                               NULL,
                               GroupType,
                               Size);
               if (AuxInfo == NULL) {
                    WmipFree(GuidMapList);
                    WmipSetLastError(ERROR_NOT_ENOUGH_MEMORY);
                   return(0);
               }
               RtlCopyMemory(AuxInfo, pTraceGuidMap, sizeof(TRACEGUIDMAP));
            }
           pTraceGuidMap++;
       }
    }
    WmipFree(GuidMapList);
    WmipSetLastError(Status);
    return(ReturnGuidCount);
#else
    WmipSetLastError(ERROR_INVALID_PARAMETER);
    return(0);
#endif
}

ULONG
WmiUnregisterGuids(
    IN WMIHANDLE WMIHandle,
    IN LPGUID    Guid,
    OUT ULONG64  *LoggerContext
)
/*++

Routine Description:

    This routine informs WMI that a data provider is no longer available
    to receive requests for the guids previously registered. WMI will
    unregister any guids registered with this handle.

Arguments:

    WMIHandle - Handle returned from WMIRegisterGuids that represents
                the guids whose data is not longer available.
    Guid -      Pointer to the control Guid which is unregistering

    LoggerContext - Returned value of the LoggerContext

Return Value:

    Returns status code

--*/
{
    ULONG Status;
    ULONG ReturnSize;
    WMIUNREGGUIDS UnregGuids;

    UnregGuids.RequestHandle.Handle = WMIHandle;
    UnregGuids.Guid = *Guid;

    Status = WmipSendWmiKMRequest(NULL,
                                         IOCTL_WMI_UNREGISTER_GUIDS,
                                         &UnregGuids,
                                         sizeof(WMIUNREGGUIDS),
                                         &UnregGuids,
                                         sizeof(WMIUNREGGUIDS),
                                         &ReturnSize,
                                         NULL);
    								


    WmipSetLastError(Status);
    return(Status);
}

ULONG
WmipFlushLogger(
    IN OUT PWMI_LOGGER_INFORMATION LoggerInfo
    )
/*++

Routine Description:

    This is the actual routine to communicate with the kernel to start
    the logger. All the required parameters must be in LoggerInfo.

Arguments:

    LoggerInfo      The actual parameters to be passed to and return from
                    kernel.

Return Value:

    The status of performing the action requested.

--*/
{
    ULONG Status;
    ULONG BufferSize;
    PTRACE_ENABLE_CONTEXT pContext;

    if (   (LoggerInfo == NULL)
        || (LoggerInfo->Wnode.BufferSize < sizeof(WMI_LOGGER_INFORMATION))
        || (! (LoggerInfo->Wnode.Flags & WNODE_FLAG_TRACED_GUID))) {
        return WmipSetDosError(ERROR_INVALID_PARAMETER);
    }

    pContext = (PTRACE_ENABLE_CONTEXT) & LoggerInfo->Wnode.HistoricalContext;
    if (   (pContext->InternalFlag != 0)
        && (pContext->InternalFlag != EVENT_TRACE_INTERNAL_FLAG_PRIVATE)) {
        // Currently only one possible InternalFlag value. This will filter
        // out some bogus LoggerHandle
        //
        return WmipSetDosError(ERROR_INVALID_HANDLE);
    }

    if (LoggerInfo->LogFileMode & EVENT_TRACE_PRIVATE_LOGGER_MODE) {
        Status = WmipSendUmLogRequest(
                    WmiFlushLoggerCode,
                    LoggerInfo
                    );
    }
    else {

        Status = WmipSendWmiKMRequest(       // actually start the logger here
                    NULL,
                    IOCTL_WMI_FLUSH_LOGGER,
                    LoggerInfo,
                    LoggerInfo->Wnode.BufferSize,
                    LoggerInfo,
                    LoggerInfo->Wnode.BufferSize,
                    &BufferSize,
                    NULL
                    );
    }

    return WmipSetDosError(Status);
}
#endif