/*++

Copyright (c) 1991 Microsoft Corporation

Module Name:

    bowutils.c

Abstract:

    This module implements various useful routines for the NT datagram
receiver (bowser).


Author:

    Larry Osterman (larryo) 6-May-1991

Revision History:

    24-Sep-1991 larryo

        Created

--*/

#include "precomp.h"
#pragma hdrstop

#ifdef  ALLOC_PRAGMA
#pragma alloc_text(PAGE, BowserMapUsersBuffer)
#pragma alloc_text(PAGE, BowserLockUsersBuffer)
#pragma alloc_text(PAGE, BowserConvertType3IoControlToType2IoControl)
#pragma alloc_text(PAGE, BowserPackNtString)
#pragma alloc_text(PAGE, BowserPackUnicodeString)
#pragma alloc_text(PAGE, BowserRandom)
#pragma alloc_text(PAGE, BowserTimeUp)
#pragma alloc_text(PAGE, BowserReferenceDiscardableCode)
#pragma alloc_text(PAGE, BowserDereferenceDiscardableCode)
#pragma alloc_text(PAGE, BowserUninitializeDiscardableCode)
#pragma alloc_text(INIT, BowserInitializeDiscardableCode)

#if DBG
#ifndef PRODUCT1
#pragma alloc_text(PAGE, BowserTrace)
#endif
#pragma alloc_text(PAGE, BowserInitializeTraceLog)
#pragma alloc_text(PAGE, BowserOpenTraceLogFile)
#pragma alloc_text(PAGE, BowserUninitializeTraceLog)
#pragma alloc_text(PAGE, BowserDebugCall)
#endif

#endif

BOOLEAN
BowserMapUsersBuffer (
    IN PIRP Irp,
    OUT PVOID *UserBuffer,
    IN ULONG Length
    )

/*++

Routine Description:

    This routine will probe and lock the buffer described by the
    provided Irp.

Arguments:

    IN PIRP Irp - Supplies the IRP that is to be mapped.
    OUT PVOID *Buffer - Returns a buffer that maps the user's buffer in the IRP

Return Value:

    TRUE - The buffer was mapped into the current address space.
    FALSE - The buffer was NOT mapped in, it was already mappable.


--*/

{
    PAGED_CODE();

    if (Irp->MdlAddress) {
        *UserBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, LowPagePriority);
        return FALSE;
    } else {
        if (Irp->AssociatedIrp.SystemBuffer != NULL) {
            *UserBuffer = Irp->AssociatedIrp.SystemBuffer;

        } else if (Irp->RequestorMode != KernelMode) {
            PIO_STACK_LOCATION IrpSp;

            IrpSp = IoGetCurrentIrpStackLocation( Irp );

            if ((Length != 0) && (Irp->UserBuffer != 0)) {

                if ((IrpSp->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) ||
                    (IrpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL)) {
                    ULONG ControlCode = IrpSp->Parameters.DeviceIoControl.IoControlCode;

                    if ((ControlCode & 3) == METHOD_NEITHER) {
                        ProbeForWrite( Irp->UserBuffer,
                                        Length,
                                        sizeof(UCHAR) );
                    } else {
                        ASSERT ((ControlCode & 3) != METHOD_BUFFERED);
                        ASSERT ((ControlCode & 3) != METHOD_IN_DIRECT);
                        ASSERT ((ControlCode & 3) != METHOD_OUT_DIRECT);
                    }

                } else if ((IrpSp->MajorFunction == IRP_MJ_READ) ||
                    (IrpSp->MajorFunction == IRP_MJ_QUERY_INFORMATION) ||
                    (IrpSp->MajorFunction == IRP_MJ_QUERY_VOLUME_INFORMATION) ||
                    (IrpSp->MajorFunction == IRP_MJ_QUERY_SECURITY) ||
                    (IrpSp->MajorFunction == IRP_MJ_DIRECTORY_CONTROL)) {

                    ProbeForWrite( Irp->UserBuffer,
                           Length,
                           sizeof(UCHAR) );
                } else {
                    ProbeForRead( Irp->UserBuffer,
                          Length,
                          sizeof(UCHAR) );
                }
            }

            *UserBuffer = Irp->UserBuffer;
        }

        return FALSE;
    }

}

NTSTATUS
BowserLockUsersBuffer (
    IN PIRP Irp,
    IN LOCK_OPERATION Operation,
    IN ULONG BufferLength
    )

/*++

Routine Description:

    This routine will probe and lock the buffer described by the
    provided Irp.

Arguments:

    IN PIRP Irp - Supplies the IRP that is to be locked.
    IN LOCK_OPERATION Operation - Supplies the operation type to probe.

Return Value:

    None.


--*/

{
    NTSTATUS Status = STATUS_SUCCESS;

    PAGED_CODE();
    if ((Irp->MdlAddress == NULL)) {

        try {

            Irp->MdlAddress = IoAllocateMdl(Irp->UserBuffer,
                     BufferLength,
                     FALSE,
                     TRUE,
                     NULL);

            if (Irp->MdlAddress == NULL) {
               return(STATUS_INSUFFICIENT_RESOURCES);
            }


            //
            //  Now probe and lock down the user's data buffer.
            //

            MmProbeAndLockPages(Irp->MdlAddress,
                            Irp->RequestorMode,
                            Operation);

        } except (BR_EXCEPTION) {
            Status =  GetExceptionCode();

            if (Irp->MdlAddress != NULL) {
                //
                //  We blew up in the probe and lock, free up the MDL
                //  and set the IRP to have a null MDL pointer - we are failing the
                //  request
                //

                IoFreeMdl(Irp->MdlAddress);
                Irp->MdlAddress = NULL;
            }

        }

    }

    return Status;

}

NTSTATUS
BowserConvertType3IoControlToType2IoControl (
    IN PIRP Irp,
    IN PIO_STACK_LOCATION IrpSp
    )

/*++

Routine Description:

    This routine does the work necessary to convert a type 3 IoCtl to a
    type 2 IoCtl.  We do this when we have to pass a user IRP to the FSP.


Arguments:

    IN PIRP Irp - Supplies an IRP to convert
    IN PIO_STACK_LOCATION IrpSp - Supplies an Irp Stack location for convenience

Return Value:

    NTSTATUS - Status of operation

Note: This must be called in the FSD.

--*/

{
    NTSTATUS Status;

    PAGED_CODE();

    if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength != 0) {
        Status = BowserLockUsersBuffer(Irp, IoWriteAccess, IrpSp->Parameters.DeviceIoControl.OutputBufferLength);

        //
        //  If we were unable to lock the users output buffer, return now.
        //

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

    }

    ASSERT (Irp->AssociatedIrp.SystemBuffer == NULL);

    try {
        if (IrpSp->Parameters.DeviceIoControl.InputBufferLength != 0) {
            PCHAR InputBuffer = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
            ULONG InputBufferLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;

            Irp->AssociatedIrp.SystemBuffer = ExAllocatePoolWithQuotaTag(PagedPool,
                                                            InputBufferLength, '  GD');

            if (Irp->AssociatedIrp.SystemBuffer == NULL) {
                return STATUS_INSUFFICIENT_RESOURCES;
            }

            //
            // If called from a user process,
            //  probe the buffer to ensure it is in the callers address space.
            //
            if (Irp->RequestorMode != KernelMode) {
                ProbeForRead( InputBuffer,
                              InputBufferLength,
                              sizeof(UCHAR));
            }

            RtlCopyMemory( Irp->AssociatedIrp.SystemBuffer,
                       InputBuffer,
                       InputBufferLength);

            Irp->Flags |= (IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER);

        } else {
            Irp->AssociatedIrp.SystemBuffer = NULL;
        }

    } except (BR_EXCEPTION) {

        if (Irp->AssociatedIrp.SystemBuffer != NULL) {
           ExFreePool(Irp->AssociatedIrp.SystemBuffer);
        }
        return GetExceptionCode();

    }

    return STATUS_SUCCESS;
}

ULONG
BowserPackNtString(
    PUNICODE_STRING string,
    ULONG_PTR BufferDisplacement,
    PCHAR dataend,
    PCHAR * laststring
    )
/**     BowserPackNtString
 *
 *  BowserPackNtString is used to stuff variable-length data, which
 *  is pointed to by (surpise!) a pointer.  The data is assumed
 *  to be a nul-terminated string (ASCIIZ).  Repeated calls to
 *  this function are used to pack data from an entire structure.
 *
 *  Upon first call, the laststring pointer should point to just
 *  past the end of the buffer.  Data will be copied into the buffer from
 *  the end, working towards the beginning.  If a data item cannot
 *  fit, the pointer will be set to NULL, else the pointer will be
 *  set to the new data location.
 *
 *  Pointers which are passed in as NULL will be set to be pointer
 *  to and empty string, as the NULL-pointer is reserved for
 *  data which could not fit as opposed to data not available.
 *
 *  Returns:  0 if could not fit data into buffer
 *    else size of data stuffed (guaranteed non-zero)
 *
 *  See the test case for sample usage.  (tst/packtest.c)
 */

{
    LONG size;

    PAGED_CODE();

    dlog(DPRT_PACK, ("BowserPackNtString:\n"));
    dlog(DPRT_PACK, ("  string=%Fp, *string=%Fp, **string=\"%us\"\n",
                                                    string, *string, *string));
    dlog(DPRT_PACK, ("  end=%Fp\n", dataend));
    dlog(DPRT_PACK, ("  last=%Fp, *last=%Fp, **last=\"%us\"\n",
                                        laststring, *laststring, *laststring));

    ASSERT (dataend < *laststring);

    //
    //  is there room for the string?
    //

    size = string->Length;

    if ((*laststring - dataend) < size) {
        string->Length = 0;
        return(0);
    } else {
        *laststring -= size;
        RtlCopyMemory(*laststring, string->Buffer, size);
        string->Buffer = (PWSTR)((*laststring) - BufferDisplacement);
        return(size);
    }
}

ULONG
BowserPackUnicodeString(
    IN OUT PWCHAR * string,     // pointer by reference: string to be copied.
    IN ULONG StringLength,      // Length of this string (in bytes) (w/o trailing zero)
    IN ULONG_PTR OutputBufferDisplacement,  // Amount to subtract from output buffer
    IN PVOID dataend,          // pointer to end of fixed size data.
    IN OUT PVOID * laststring  // pointer by reference: top of string data.
    )

/*++

Routine Description:

    BowserPackUnicodeString is used to stuff variable-length data, which
    is pointed to by (surpise!) a pointer.  The data is assumed
    to be a nul-terminated string (ASCIIZ).  Repeated calls to
    this function are used to pack data from an entire structure.

    Upon first call, the laststring pointer should point to just
    past the end of the buffer.  Data will be copied into the buffer from
    the end, working towards the beginning.  If a data item cannot
    fit, the pointer will be set to NULL, else the pointer will be
    set to the new data location.

    Pointers which are passed in as NULL will be set to be pointer
    to and empty string, as the NULL-pointer is reserved for
    data which could not fit as opposed to data not available.

    See the test case for sample usage.  (tst/packtest.c)


Arguments:

    string - pointer by reference:  string to be copied.

    dataend - pointer to end of fixed size data.

    laststring - pointer by reference:  top of string data.

Return Value:

    0  - if it could not fit data into the buffer.  Or...

    sizeOfData - the size of data stuffed (guaranteed non-zero)

--*/

{
    DWORD  size;
    DWORD  Available       = (DWORD)((PCHAR)*laststring - (PCHAR)dataend);
    WCHAR  StringBuffer[1] = L"";

    PAGED_CODE();

    //
    //  Verify that there is room left for the string.  If a NULL string
    //    is input, there must be at least room for a UNICODE NULL, so set
    //    size to sizeof(WCHAR) in this case.
    //

    if (*string == NULL) {
        StringLength = 0;
        *string      = StringBuffer;
    }

    size = StringLength + sizeof(WCHAR);

    if (*laststring < dataend || size > Available) {
       *string = UNICODE_NULL;
       return(0);
    }

    *((PCHAR *)laststring) -= size;
    RtlCopyMemory(*laststring, *string, size-sizeof(WCHAR));
    *string = *laststring;
    (*string)[StringLength/2] = L'\0';
    *(PCHAR*)string -=OutputBufferDisplacement;
    return(size);

} // BowserUnicodePackString


ULONG
BowserTimeUp(
    VOID
    )
/*++

Routine Description:

    BowserTimeUp is used to return the number of seconds the browser has been
    running.


Arguments:

    None

Return Value:

    Number of seconds the browser has been up.

--*/
{
    LARGE_INTEGER CurrentTime;
    LARGE_INTEGER TimeDelta;
    LARGE_INTEGER TimeUp;

    //
    //  These are the magic numbers needed to do our extended division.  The
    //  only numbers we ever need to divide by are
    //
    //      10,000 = convert 100ns tics to millisecond tics
    //
    //
    //  These values were stolen from ntos\rtl\time.c
    //

    LARGE_INTEGER Magic10000 = {0xe219652c, 0xd1b71758};
#define SHIFT10000                       13


    PAGED_CODE();

    KeQuerySystemTime(&CurrentTime);

    TimeDelta.QuadPart = CurrentTime.QuadPart - BowserStartTime.QuadPart;

    //
    //  TimeDelta is the number of 100ns units the bowser has been up.  Convert
    //  it to milliseconds using the magic routine.
    //

    TimeUp = RtlExtendedMagicDivide(TimeDelta, Magic10000, SHIFT10000);

    //
    //  Please note that TimeUp.LowPart wraps after about 49 days,
    //  this means that if a machine has been up for more than 49 days,
    //  we peg at 0xffffffff.
    //

    if (TimeUp.HighPart != 0) {
        return(0xffffffff);
    }

    return(TimeUp.LowPart);
}

ULONG
BowserRandom(
    IN ULONG MaxValue
    )
/*++

Routine Description:

    BowserRandom is used to return a random number between 0 and MaxValue

Arguments:

    MaxValue - The maximum value to return.

Return Value:

    Random # between 0 and MaxValue

--*/
{
    PAGED_CODE();

    return RtlRandom(&BowserRandomSeed) % MaxValue;
}


VOID
BowserReferenceDiscardableCode(
    DISCARDABLE_SECTION_NAME SectionName
    )
/*++

Routine Description:

    BowserReferenceDiscardableCode is called to reference the browsers
    discardable code section.

    If the section is not present in memory, MmLockPagableCodeSection is
    called to fault the section into memory.

Arguments:

    None.

Return Value:

    None.

--*/

{
    PAGED_CODE();

    RdrReferenceDiscardableCode(SectionName);


}

VOID
BowserDereferenceDiscardableCode(
    DISCARDABLE_SECTION_NAME SectionName
    )
/*++

Routine Description:

    BowserDereferenceDiscardableCode is called to dereference the browsers
    discardable code section.

    When the reference count drops to 0, a timer is set that will fire in <n>
    seconds, after which time the section will be unlocked.

Arguments:

    None.

Return Value:

    None.

--*/

{
    PAGED_CODE();
    RdrDereferenceDiscardableCode(SectionName);
}

VOID
BowserInitializeDiscardableCode(
    VOID
    )
{
}

VOID
BowserUninitializeDiscardableCode(
    VOID
    )
{
    PAGED_CODE();
}

#if BOWSERPOOLDBG
typedef struct {
    ULONG Count;
    ULONG Size;
    PCHAR FileName;
    ULONG LineNumber;
} POOL_STATS, *PPOOL_STATS;


typedef struct _POOL_HEADER {
//    LIST_ENTRY ListEntry;
    ULONG NumberOfBytes;
    PPOOL_STATS Stats;
} POOL_HEADER, *PPOOL_HEADER;

ULONG CurrentAllocationCount;
ULONG CurrentAllocationSize;

ULONG NextFreeEntry = 0;

POOL_STATS PoolStats[POOL_MAXTYPE+1];

PVOID
BowserAllocatePool (
    IN POOL_TYPE PoolType,
    IN ULONG NumberOfBytes,
    IN PCHAR FileName,
    IN ULONG LineNumber,
    IN ULONG Tag
    )
{
    PPOOL_HEADER header;
    KIRQL oldIrql;
#if 1
    ULONG i;
#endif

#if  POOL_TAGGING
    header = ExAllocatePoolWithTag( PoolType, sizeof(POOL_HEADER) + NumberOfBytes, Tag );
#else
    header = ExAllocatePool( PoolType, sizeof(POOL_HEADER) + NumberOfBytes );

#endif
    if ( header == NULL ) {
        return NULL;
    }
    header->NumberOfBytes = NumberOfBytes;

//    DbgPrint( "BOWSER: allocated type %d, size %d at %x\n", AllocationType, NumberOfBytes, header );

    ACQUIRE_SPIN_LOCK( &BowserTimeSpinLock, &oldIrql );

    CurrentAllocationCount++;
    CurrentAllocationSize += NumberOfBytes;
#if 1
    //
    //  Lets see if we've already allocated one of these guys.
    //


    for (i = 0;i < POOL_MAXTYPE ; i+= 1 ) {
        if ((PoolStats[i].LineNumber == LineNumber) &&
            (PoolStats[i].FileName == FileName)) {

            //
            //  Yup, remember this allocation and return.
            //

            header->Stats = &PoolStats[i];
            PoolStats[i].Count++;
            PoolStats[i].Size += NumberOfBytes;

            RELEASE_SPIN_LOCK( &BowserTimeSpinLock, oldIrql );

            return header + 1;
        }
    }

    for (i = NextFreeEntry; i < POOL_MAXTYPE ; i+= 1 ) {
        if ((PoolStats[i].LineNumber == 0) &&
            (PoolStats[i].FileName == NULL)) {

            PoolStats[i].Count++;
            PoolStats[i].Size += NumberOfBytes;
            PoolStats[i].FileName = FileName;
            PoolStats[i].LineNumber = LineNumber;
            header->Stats = &PoolStats[i];

            NextFreeEntry = i+1;

            RELEASE_SPIN_LOCK( &BowserTimeSpinLock, oldIrql );

            return header + 1;
        }
    }

    header->Stats = &PoolStats[i];
    PoolStats[POOL_MAXTYPE].Count++;
    PoolStats[POOL_MAXTYPE].Size += NumberOfBytes;
#endif

    RELEASE_SPIN_LOCK( &BowserTimeSpinLock, oldIrql );

    return header + 1;
}

PVOID
BowserAllocatePoolWithQuota (
    IN POOL_TYPE PoolType,
    IN ULONG NumberOfBytes,
    IN PCHAR FileName,
    IN ULONG LineNumber,
    IN ULONG Tag
    )
{
    PPOOL_HEADER header;
    KIRQL oldIrql;
#if 1
    ULONG i;
#endif

#if POOL_TAGGING
    header = ExAllocatePoolWithTagQuota( PoolType, sizeof(POOL_HEADER) + NumberOfBytes, Tag );
#else
    header = ExAllocatePoolWithQuota( PoolType, sizeof(POOL_HEADER) + NumberOfBytes );
#endif
    if ( header == NULL ) {
        return NULL;
    }
    header->NumberOfBytes = NumberOfBytes;

//    DbgPrint( "BOWSER: allocated type %d, size %d at %x\n", AllocationType, NumberOfBytes, header );

    ACQUIRE_SPIN_LOCK( &BowserTimeSpinLock, &oldIrql );

    CurrentAllocationCount++;
    CurrentAllocationSize += NumberOfBytes;
#if 1
    //
    //  Lets see if we've already allocated one of these guys.
    //


    for (i = 0;i < POOL_MAXTYPE ; i+= 1 ) {
        if ((PoolStats[i].LineNumber == LineNumber) &&
            (PoolStats[i].FileName == FileName)) {

            //
            //  Yup, remember this allocation and return.
            //

            header->Stats = &PoolStats[i];
            PoolStats[i].Count++;
            PoolStats[i].Size += NumberOfBytes;

            RELEASE_SPIN_LOCK( &BowserTimeSpinLock, oldIrql );

            return header + 1;
        }
    }

    for (i = NextFreeEntry; i < POOL_MAXTYPE ; i+= 1 ) {
        if ((PoolStats[i].LineNumber == 0) &&
            (PoolStats[i].FileName == NULL)) {

            PoolStats[i].Count++;
            PoolStats[i].Size += NumberOfBytes;
            PoolStats[i].FileName = FileName;
            PoolStats[i].LineNumber = LineNumber;
            header->Stats = &PoolStats[i];

            NextFreeEntry = i+1;

            RELEASE_SPIN_LOCK( &BowserTimeSpinLock, oldIrql );

            return header + 1;
        }
    }

    header->Stats = &PoolStats[i];
    PoolStats[POOL_MAXTYPE].Count++;
    PoolStats[POOL_MAXTYPE].Size += NumberOfBytes;

#endif

    RELEASE_SPIN_LOCK( &BowserTimeSpinLock, oldIrql );

    return header + 1;
}

VOID
BowserFreePool (
    IN PVOID P
    )
{
    PPOOL_HEADER header;
    KIRQL oldIrql;
    PPOOL_STATS stats;
    ULONG size;

    header = (PPOOL_HEADER)P - 1;

    size = header->NumberOfBytes;
    stats = header->Stats;

//    if ( allocationType > POOL_MAXTYPE ) allocationType = POOL_MAXTYPE;
//    DbgPrint( "BOWSER: freed type %d, size %d at %x\n", allocationType, size, header );

    ACQUIRE_SPIN_LOCK( &BowserTimeSpinLock, &oldIrql );

    CurrentAllocationCount--;
    CurrentAllocationSize -= size;
#if 1
    stats->Count--;
    stats->Size -= size;
#endif

    RELEASE_SPIN_LOCK( &BowserTimeSpinLock, oldIrql );

    ExFreePool( header );

    return;
}
#endif // BOWSERPOOLDBG

#if DBG

ERESOURCE
BrowserTraceLock;

HANDLE
BrowserTraceLogHandle = NULL;
UCHAR LastCharacter = '\n';

#ifndef PRODUCT1

VOID
BowserTrace(
    PCHAR FormatString,
    ...
    )

#define LAST_NAMED_ARGUMENT FormatString

{
    CHAR OutputString[1024];
    IO_STATUS_BLOCK IoStatus;
    BOOLEAN ProcessAttached = FALSE;
    BOOLEAN ReleaseResource = FALSE;
    va_list ParmPtr;                    // Pointer to stack parms.
    KAPC_STATE ApcState;
    NTSTATUS Status;

    PAGED_CODE();


    if (BrowserTraceLogHandle == NULL) {

        // Attach to FSP when using handle
        if (IoGetCurrentProcess() != BowserFspProcess) {
            KeStackAttachProcess(BowserFspProcess, &ApcState );

            ProcessAttached = TRUE;
        }

        if (!NT_SUCCESS(BowserOpenTraceLogFile(L"\\SystemRoot\\Bowser.Log"))) {

            BrowserTraceLogHandle = (HANDLE) -1;

            if (ProcessAttached) {
                KeUnstackDetachProcess( &ApcState );
            }

            return;
        }

    } else if (BrowserTraceLogHandle == (HANDLE) -1) {

        if (ProcessAttached) {
            KeUnstackDetachProcess( &ApcState );
        }

        return;
    }


    try {
        LARGE_INTEGER EndOfFile;

        ExAcquireResourceExclusive(&BrowserTraceLock, TRUE);
        ReleaseResource = TRUE;

        // re-verify we should be tracing (under lock).
        if (BrowserTraceLogHandle == NULL) {
            try_return(Status);
        }

        EndOfFile.HighPart = 0xffffffff;
        EndOfFile.LowPart = FILE_WRITE_TO_END_OF_FILE;

        if (LastCharacter == '\n') {
            LARGE_INTEGER SystemTime;
            TIME_FIELDS TimeFields;

            KeQuerySystemTime(&SystemTime);

            ExSystemTimeToLocalTime(&SystemTime, &SystemTime);

            RtlTimeToTimeFields(&SystemTime, &TimeFields);

            //
            //  The last character written was a newline character.  We should
            //  timestamp this record in the file.
            //
            sprintf(OutputString, "%2.2d/%2.2d %2.2d:%2.2d:%2.2d.%3.3d: ",
                                                            TimeFields.Month,
                                                            TimeFields.Day,
                                                            TimeFields.Hour,
                                                            TimeFields.Minute,
                                                            TimeFields.Second,
                                                            TimeFields.Milliseconds);
            // Attach to FSP when using handle
            if (IoGetCurrentProcess() != BowserFspProcess) {
                KeStackAttachProcess(BowserFspProcess, &ApcState );

                ProcessAttached = TRUE;
            }

            if (!NT_SUCCESS(Status = ZwWriteFile(BrowserTraceLogHandle, NULL, NULL, NULL, &IoStatus, OutputString, strlen(OutputString), &EndOfFile, NULL))) {
                KdPrint(("Error writing time to Browser log file: %lX\n", Status));
                try_return(Status);
            }

            if (!NT_SUCCESS(IoStatus.Status)) {
                KdPrint(("Error writing time to Browser log file: %lX\n", IoStatus.Status));
                try_return(Status);
            }

            if (IoStatus.Information != strlen(OutputString)) {
                KdPrint(("Error writing time to Browser log file: %lX\n", IoStatus.Status));
                try_return(Status);
            }

        }

        va_start(ParmPtr, LAST_NAMED_ARGUMENT);

        // Be in caller's process when referencing parameters.
        if (ProcessAttached) {
            KeUnstackDetachProcess( &ApcState );
            ProcessAttached = FALSE;
        }

        //
        //  Format the parameters to the string.
        //

        vsprintf(OutputString, FormatString, ParmPtr);

        // Attach to FSP when using handle
        if (IoGetCurrentProcess() != BowserFspProcess) {
            KeStackAttachProcess(BowserFspProcess, &ApcState );

            ProcessAttached = TRUE;
        }

        if (!NT_SUCCESS(Status = ZwWriteFile(BrowserTraceLogHandle, NULL, NULL, NULL, &IoStatus, OutputString, strlen(OutputString), &EndOfFile, NULL))) {
            KdPrint(("Error writing string to Browser log file: %ld\n", Status));
            try_return(Status);
        }

        if (!NT_SUCCESS(IoStatus.Status)) {
            KdPrint(("Error writing string to Browser log file: %lX\n", IoStatus.Status));
            try_return(Status);
        }

        if (IoStatus.Information != strlen(OutputString)) {
            KdPrint(("Error writing string to Browser log file: %ld\n", IoStatus.Status));
            try_return(Status);
        }

        //
        //  Remember the last character output to the log.
        //

        LastCharacter = OutputString[strlen(OutputString)-1];

try_exit:NOTHING;
    } finally {
        if (ReleaseResource) {
            ExReleaseResource(&BrowserTraceLock);
        }
        if (ProcessAttached) {
            KeUnstackDetachProcess( &ApcState );
        }
    }
}

#endif

VOID
BowserInitializeTraceLog()
{

    PAGED_CODE();
    ExInitializeResource(&BrowserTraceLock);

}

NTSTATUS
BowserOpenTraceLogFile(
    IN PWCHAR TraceFile
    )
{
    UNICODE_STRING TraceFileName;
    OBJECT_ATTRIBUTES ObjA;
    NTSTATUS Status;
    IO_STATUS_BLOCK IoStatusBlock;

    PAGED_CODE();

    RtlInitUnicodeString(&TraceFileName, TraceFile);

    InitializeObjectAttributes(&ObjA, &TraceFileName, OBJ_CASE_INSENSITIVE, NULL, NULL);

    Status = IoCreateFile(&BrowserTraceLogHandle,
                                        FILE_APPEND_DATA|SYNCHRONIZE,
                                        &ObjA,
                                        &IoStatusBlock,
                                        NULL,
                                        FILE_ATTRIBUTE_NORMAL,
                                        FILE_SHARE_READ,
                                        FILE_OPEN_IF,
                                        FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE | FILE_SEQUENTIAL_ONLY,
                                        NULL,
                                        0,
                                        CreateFileTypeNone,
                                        NULL,
                                        IO_FORCE_ACCESS_CHECK |         // Ensure the user has access to the file
                                            IO_NO_PARAMETER_CHECKING |  // All of the buffers are kernel buffers
                                            IO_CHECK_CREATE_PARAMETERS  // But double check parameter consistancy
                                        );


    if (!NT_SUCCESS(Status)) {
        KdPrint(("Bowser: Error creating trace file %ws %lX\n", TraceFile, Status));

        return Status;
    }

    return Status;
}

VOID
BowserUninitializeTraceLog()
{
    BOOLEAN ProcessAttached = FALSE;
    KAPC_STATE ApcState;

    PAGED_CODE();

    ExDeleteResource(&BrowserTraceLock);

    if (BrowserTraceLogHandle != NULL) {
        if (IoGetCurrentProcess() != BowserFspProcess) {
            KeStackAttachProcess(BowserFspProcess, &ApcState );

            ProcessAttached = TRUE;
        }

        ZwClose(BrowserTraceLogHandle);

        if (ProcessAttached) {
            KeUnstackDetachProcess( &ApcState );
        }
    }

    BrowserTraceLogHandle = NULL;
}

NTSTATUS
BowserDebugCall(
    IN PLMDR_REQUEST_PACKET InputBuffer,
    IN ULONG InputBufferLength
    )
{
    NTSTATUS Status;
    BOOLEAN ProcessAttached = FALSE;
    KAPC_STATE ApcState;


    PAGED_CODE();

    if (IoGetCurrentProcess() != BowserFspProcess) {
        KeStackAttachProcess(BowserFspProcess, &ApcState );

        ProcessAttached = TRUE;
    }


    try {
        if (InputBufferLength < sizeof(LMDR_REQUEST_PACKET)) {
            try_return(Status=STATUS_BUFFER_TOO_SMALL);
        }

        if ( InputBuffer->Version != LMDR_REQUEST_PACKET_VERSION_DOM ) {
            try_return(Status=STATUS_INVALID_PARAMETER);
        }

        if (InputBuffer->Parameters.Debug.OpenLog && InputBuffer->Parameters.Debug.CloseLog) {
            try_return(Status=STATUS_INVALID_PARAMETER);
        }

        if (InputBuffer->Parameters.Debug.OpenLog) {

            ENSURE_IN_INPUT_BUFFER_STR( InputBuffer->Parameters.Debug.TraceFileName);

            Status = BowserOpenTraceLogFile(InputBuffer->Parameters.Debug.TraceFileName);

        } else if (InputBuffer->Parameters.Debug.CloseLog) {
            Status = ZwClose(BrowserTraceLogHandle);

            if (NT_SUCCESS(Status)) {
                BrowserTraceLogHandle = NULL;
            }

        } else if (InputBuffer->Parameters.Debug.TruncateLog) {
            FILE_END_OF_FILE_INFORMATION EndOfFileInformation;
            IO_STATUS_BLOCK IoStatus;

            if (BrowserTraceLogHandle == NULL) {
                try_return(Status=STATUS_INVALID_HANDLE);
            }

            EndOfFileInformation.EndOfFile.HighPart = 0;
            EndOfFileInformation.EndOfFile.LowPart = 0;

            Status = NtSetInformationFile(BrowserTraceLogHandle,
                                            &IoStatus,
                                            &EndOfFileInformation,
                                            sizeof(EndOfFileInformation),
                                            FileEndOfFileInformation);

        } else {
            BowserDebugLogLevel = InputBuffer->Parameters.Debug.DebugTraceBits;
            KdPrint(("Setting Browser Debug Trace Bits to %lx\n", BowserDebugLogLevel));
            Status = STATUS_SUCCESS;
        }

        try_return(Status);

    try_exit:NOTHING;
    } finally {

        if (ProcessAttached) {
            KeUnstackDetachProcess( &ApcState );
        }

    }

    return Status;
}

#endif