/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

    minirdr.c

Abstract:

    This module implements minirdr registration functions.

Author:

    Joe Linn (JoeLinn)    2-2-95

Revision History:

--*/

#include "precomp.h"
#pragma hdrstop

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, RxRegisterMinirdr)
#pragma alloc_text(PAGE, RxMakeLateDeviceAvailable)
#pragma alloc_text(PAGE, RxpUnregisterMinirdr)
#endif

//
// The debug trace level
//

#define Dbg                              (0)

#ifdef ALLOC_PRAGMA
#endif

// #define BBT_UPDATE 1

#ifdef BBT_UPDATE
extern VOID RxUpdate(PVOID pContext);

HANDLE   RxHandle  = INVALID_HANDLE_VALUE;
PETHREAD RxPointer = NULL;
#endif 


NTSTATUS
NTAPI
RxRegisterMinirdr(
    OUT PRDBSS_DEVICE_OBJECT *DeviceObject,
    IN OUT PDRIVER_OBJECT     DriverObject,    // the minirdr driver object
    IN  PMINIRDR_DISPATCH     MrdrDispatch,    // the mini rdr dispatch vector
    IN  ULONG                 Controls,
    IN  PUNICODE_STRING       DeviceName,
    IN  ULONG                 DeviceExtensionSize,
    IN  DEVICE_TYPE           DeviceType,
    IN  ULONG                 DeviceCharacteristics
    )
/*++

Routine Description:

    The routine adds the registration information to the minirdr registration table. As well, it builds
    a device object; the MUP registration is at start time. Also, we fill in the deviceobject so that we are catching
    all the calls.

Arguments:

    DeviceObject   - where the created device object is to be stored
    ProtocolMarker - a 4byte marker denoting the FileLevel Protocol
                             ('LCL ', 'SMB ', 'NCP ', and 'NFS ') are used
    MrdrDispatch   - the dispatch table for finding the server/netroot discovery routines
    Context        - whatever PVOID the underlying guy wants
    MupAction      - whether/how the MUP registration is done
    DeviceName,DeviceExtensionSize,DeviceType,DeviceCharacteristics
                   - the params for the device object that is to be built these are adjusted a bit
                     bfore they're passed to Io

Return Value:

    --

--*/
{
    NTSTATUS Status;
    PRDBSS_DEVICE_OBJECT RxDeviceObject;

    PAGED_CODE();

    RxDbgTrace( 0, (DEBUG_TRACE_ALWAYS), (" RxRegisterMinirdr  Name = %wZ", DeviceName));

    if (DeviceObject==NULL) {
        return STATUS_INVALID_PARAMETER;
    }

    // Create the device object.
    Status = IoCreateDevice(DriverObject,
                            sizeof(RDBSS_DEVICE_OBJECT) - sizeof(DEVICE_OBJECT) + DeviceExtensionSize,
                            DeviceName,
                            DeviceType,
                            DeviceCharacteristics,
                            FALSE,
                            (PDEVICE_OBJECT *)(&RxDeviceObject));
    
    if (Status != STATUS_SUCCESS) {
        return(Status);
    }

    if (RxData.DriverObject == NULL) {
        return STATUS_UNSUCCESSFUL;
    }

    //
    // If the Mini-Redir is being built in a monolithic fashion, then the 
    // device object "RxFileSystemDeviceObject" would not have been created
    // in the RxDriverEntry function. Hence we set RxDeviceObject->RDBSSDeviceObject
    // to NULL. When the "monolithic" Mini-Redir gets unloaded, a check is done
    // to see if RxDeviceObject->RDBSSDeviceObject is NULL. If it is not NULL,
    // then the device object is dereferenced. This happens in the function
    // RxUnregisterMinirdr.
    //
    // Don't allow myself to be unloaded.

#ifndef MONOLITHIC_MINIRDR
    RxDeviceObject->RDBSSDeviceObject = (PDEVICE_OBJECT)RxFileSystemDeviceObject;
    ObReferenceObject((PDEVICE_OBJECT)RxFileSystemDeviceObject);

    // Reset the Unload routine. This prevents rdbss from being unloaded individually
    RxData.DriverObject->DriverUnload = NULL;
#else
    RxDeviceObject->RDBSSDeviceObject = NULL;
    RxFileSystemDeviceObject->ReferenceCount++;
#endif

    *DeviceObject = RxDeviceObject;
    RxDeviceObject->RdbssExports = &RxExports;
    RxDeviceObject->Dispatch = MrdrDispatch;
    RxDeviceObject->RegistrationControls = Controls;
    RxDeviceObject->DeviceName = *DeviceName;
    RxDeviceObject->RegisterUncProvider =
             !BooleanFlagOn(Controls,RX_REGISTERMINI_FLAG_DONT_PROVIDE_UNCS);
    RxDeviceObject->RegisterMailSlotProvider =
             !BooleanFlagOn(Controls,RX_REGISTERMINI_FLAG_DONT_PROVIDE_MAILSLOTS);

    {
        LONG Index;

        for (Index = 0; Index < MaximumWorkQueue; Index++) {
            InitializeListHead( &RxDeviceObject->OverflowQueue[Index] );
        }
    }

    KeInitializeSpinLock( &RxDeviceObject->OverflowQueueSpinLock );

    RxDeviceObject->NetworkProviderPriority = RxGetNetworkProviderPriority(DeviceName);
    RxLog(("RegMini %x %wZ\n",RxDeviceObject->NetworkProviderPriority,DeviceName));
    RxWmiLog(LOG,
             RxRegisterMinirdr,
             LOGULONG(RxDeviceObject->NetworkProviderPriority)
             LOGUSTR(*DeviceName));

    ExAcquireFastMutexUnsafe(&RxData.MinirdrRegistrationMutex);

    InsertTailList(&RxData.RegisteredMiniRdrs,&RxDeviceObject->MiniRdrListLinks);
    //no need for interlock.....we're inside the mutex
    //InterlockedIncrement(&RxData.NumberOfMinirdrsRegistered);
    RxData.NumberOfMinirdrsRegistered++;

    ExReleaseFastMutexUnsafe(&RxData.MinirdrRegistrationMutex);

    if (!FlagOn(Controls,RX_REGISTERMINI_FLAG_DONT_INIT_DRIVER_DISPATCH)) {
        RxInitializeMinirdrDispatchTable(DriverObject);
    }

    if (!FlagOn(Controls,RX_REGISTERMINI_FLAG_DONT_INIT_PREFIX_N_SCAVENGER)) {
        //  Initialize the netname table
        RxDeviceObject->pRxNetNameTable = &RxDeviceObject->RxNetNameTableInDeviceObject;
        RxInitializePrefixTable( RxDeviceObject->pRxNetNameTable, 0, FALSE);
        RxDeviceObject->RxNetNameTableInDeviceObject.IsNetNameTable = TRUE;

        // Initialize the scavenger data structures
        RxDeviceObject->pRdbssScavenger = &RxDeviceObject->RdbssScavengerInDeviceObject;
        RxInitializeRdbssScavenger(RxDeviceObject->pRdbssScavenger);
    }

    RxDeviceObject->pAsynchronousRequestsCompletionEvent = NULL;

#ifdef BBT_UPDATE
    if (RxHandle == INVALID_HANDLE_VALUE) {
        NTSTATUS Status;

        Status = PsCreateSystemThread(
                     &RxHandle,
                     PROCESS_ALL_ACCESS,
                     NULL,
                     NULL,
                     NULL,
                     RxUpdate,
                     NULL);

        if (Status == STATUS_SUCCESS) {
            Status = ObReferenceObjectByHandle(
                         RxHandle,
                         THREAD_ALL_ACCESS,
                         NULL,
                         KernelMode,
                         &RxPointer,
                         NULL);

            if (Status != STATUS_SUCCESS) {
                RxPointer = NULL;
            }

            ZwClose(RxHandle);
        } else {
            RxHandle = INVALID_HANDLE_VALUE;
        }
    }
#endif 

    return((STATUS_SUCCESS));
}


VOID
NTAPI
RxMakeLateDeviceAvailable(
    IN PRDBSS_DEVICE_OBJECT RxDeviceObject
    )
/*++

Routine Description:

    The routine diddles the device object to make a "late device" available.
    A late device is one that is not created in the driver's load routine.
    Non-late devices are diddled by the driverload code in the io subsystem; but
    for late devices we have to do this by hand. This is a routine instead of a
    macro in order that other stuff might have to be done here....it's only
    executed once per device object.

Arguments:

    DeviceObject   - where the created device object is to be stored

Return Value:

    --

--*/
{
    PAGED_CODE();
    RxDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
    return;
}

VOID
RxpUnregisterMinirdr(
    IN PRDBSS_DEVICE_OBJECT RxDeviceObject
    )

/*++

Routine Description:

Arguments:

Return Value:

    --

--*/
{
    PAGED_CODE();

    RxDbgTrace( 0, (DEBUG_TRACE_ALWAYS), (" RxUnregisterMinirdr  Name = %wZ\n",&RxDeviceObject->DeviceName));
    ExAcquireFastMutexUnsafe(&RxData.MinirdrRegistrationMutex);
    RemoveEntryList(&RxDeviceObject->MiniRdrListLinks);
    //no need for interlock.....we're inside the mutex
    //InterlockedDecrement(&RxData.NumberOfMinirdrsRegistered);
    RxData.NumberOfMinirdrsRegistered--;

    if (RxData.NumberOfMinirdrsRegistered == 0) {
        // Allow rdbss being unloaded after mini rdr driver is unregistered
        RxData.DriverObject->DriverUnload = RxUnload;
    }

    ExReleaseFastMutexUnsafe(&RxData.MinirdrRegistrationMutex);

    if (!FlagOn(RxDeviceObject->RegistrationControls,
        RX_REGISTERMINI_FLAG_DONT_INIT_PREFIX_N_SCAVENGER)) {

        RxForceNetTableFinalization(RxDeviceObject);
        RxFinalizePrefixTable(&RxDeviceObject->RxNetNameTableInDeviceObject);
        //no finalization is defined for scavenger structure
    }

    RxSpinDownOutstandingAsynchronousRequests(RxDeviceObject);

    // Spin down any worker threads associated with this minirdr
    RxSpinDownMRxDispatcher(RxDeviceObject);

    IoDeleteDevice(&RxDeviceObject->DeviceObject);

#ifdef BBT_UPDATE
    if (RxPointer != NULL) {
       RxHandle = INVALID_HANDLE_VALUE;
       KeWaitForSingleObject(
            RxPointer,
            Executive,
            KernelMode,
            FALSE,
            NULL);
        
        ASSERT(PsIsThreadTerminating(RxPointer));
        ObDereferenceObject(RxPointer);

        RxPointer = NULL;
    }
#endif 
}


VOID
RxSpinDownOutstandingAsynchronousRequests(
    PRDBSS_DEVICE_OBJECT RxDeviceObject)
/*++

Routine Description:

    This routine spins down all the outstanding requests associated with a
    mini redirector before it can be unloaded

Arguments:

    RxDeviceObject -- the mini redirector's device object


--*/
{
    BOOLEAN WaitForSpinDown = FALSE;
    KEVENT SpinDownEvent;

    KeInitializeEvent(
        &SpinDownEvent,
        NotificationEvent,
        FALSE);

    RxAcquireSerializationMutex();

    ASSERT(RxDeviceObject->pAsynchronousRequestsCompletionEvent == NULL);

    WaitForSpinDown = (RxDeviceObject->AsynchronousRequestsPending != 0);

    RxDeviceObject->pAsynchronousRequestsCompletionEvent = &SpinDownEvent;

    RxReleaseSerializationMutex();

    if (WaitForSpinDown) {
        KeWaitForSingleObject(
            &SpinDownEvent,
            Executive,
            KernelMode,
            FALSE,
            NULL);
    }
}


NTSTATUS
RxRegisterAsynchronousRequest(
    PRDBSS_DEVICE_OBJECT RxDeviceObject)
/*++

Routine Description:

    This routine registers an asynchronous request. On successful completion
    the mini redirector cannot be unloaded till the request completes

Arguments:

    RxDeviceObject - the mini redirector device object

Return Value:

    STATUS_SUCCESS if successful

--*/
{
    NTSTATUS Status = STATUS_INVALID_DEVICE_REQUEST;

    RxAcquireSerializationMutex();

    if (RxDeviceObject->pAsynchronousRequestsCompletionEvent == NULL) {
        RxDeviceObject->AsynchronousRequestsPending++;
        Status = STATUS_SUCCESS;
    }

    RxReleaseSerializationMutex();

    return Status;
}

VOID
RxDeregisterAsynchronousRequest(
    PRDBSS_DEVICE_OBJECT RxDeviceObject)
/*++

Routine Description:

    This routine signals the completion of an asynchronous request. It resumes
    unloading if required.

Arguments:

    RxDeviceObject - the mini redirector device object

--*/
{
    PKEVENT pEvent = NULL;

    RxAcquireSerializationMutex();

    RxDeviceObject->AsynchronousRequestsPending--;

    if ((RxDeviceObject->AsynchronousRequestsPending == 0) &&
        (RxDeviceObject->pAsynchronousRequestsCompletionEvent != NULL)) {
        pEvent = RxDeviceObject->pAsynchronousRequestsCompletionEvent;
    }

    RxReleaseSerializationMutex();

    if (pEvent != NULL) {
        KeSetEvent(
            pEvent,
            IO_NO_INCREMENT,
            FALSE);
    }
}

#ifdef BBT_UPDATE
WCHAR   Request_Name[] = L"\\??\\UNC\\landyw-bear\\bbt\\bbt.txt";

VOID
RxUpdate(PVOID pContext)
{
    NTSTATUS          Status;
    OBJECT_ATTRIBUTES ObjectAttributes;
    IO_STATUS_BLOCK   IoStatusBlock;
    UNICODE_STRING    RequestName;

    RequestName.Buffer = Request_Name;
    RequestName.MaximumLength = wcslen(Request_Name) * sizeof(WCHAR);
    RequestName.Length = RequestName.MaximumLength;

    InitializeObjectAttributes(
        &ObjectAttributes,
        &RequestName,
        OBJ_CASE_INSENSITIVE,
        NULL,
        NULL);

    for (;;) {
        PHYSICAL_ADDRESS StartAddress;
        LARGE_INTEGER    NumberOfBytes;
        HANDLE           FileHandle;

        struct {
            LIST_ENTRY Link;
            SIZE_T Size;
            CHAR Data[];
        } *Request;

        NumberOfBytes.QuadPart = 0x2;
        StartAddress.QuadPart = 0;

        MmAddPhysicalMemory(
            &StartAddress,
            &NumberOfBytes);

        Request = (PVOID)(StartAddress.QuadPart);

        if (Request != NULL) {
            Status = ZwCreateFile(
                         &FileHandle,
                         (FILE_APPEND_DATA | SYNCHRONIZE),
                         &ObjectAttributes,
                         &IoStatusBlock,
                         NULL,
                         FILE_ATTRIBUTE_NORMAL,
                         (FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE),
                         FILE_OPEN,
                         FILE_NO_INTERMEDIATE_BUFFERING,
                         NULL,
                         0);


            if (Status == STATUS_SUCCESS) {
                LARGE_INTEGER ByteOffset;

                ByteOffset.QuadPart = -1;

                Status = ZwWriteFile(
                            FileHandle,
                            NULL,
                            NULL,
                            NULL,
                            &IoStatusBlock,
                            Request->Data,
                            (ULONG)Request->Size,
                            &ByteOffset,
                            NULL);                           


                Status = ZwClose(FileHandle);
            }

            ExFreePool(Request);
        }


        if (RxHandle == INVALID_HANDLE_VALUE) {
            break;
        }
    }

    PsTerminateSystemThread(STATUS_SUCCESS);
}
#endif