
/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

    RxInit.c

Abstract:

    This module implements the DRIVER_INITIALIZATION routine for the RDBSS.

    Also, the routines for pagingio resource selection/allocation are here; since
    we have to delete the resources when we unload, having them here simply centralizes
    all the pagingio resource stuff.

    Finally, the routines that are here that implement the wrapper's version of
    network provider order. Basically, the wrapper MUST implement the same concept of
    network provider order as the MUP so that the UI will work as expected. So, we
    read the provider order from the registry at init time and memorize the order. Then,
    we can assign the correct order when minirdrs register. Obviously, provider order is
    not an issue in MONOLITHIC mode.

Author:

    Joe Linn [JoeLinn]    20-jul-1994

Revision History:


--*/

#include "precomp.h"
#pragma hdrstop
#include "ntverp.h"
#include "NtDdNfs2.h"
#include "netevent.h"

//
//  The local debug trace level
//

#define Dbg                              (0)

ULONG RxBuildNumber = VER_PRODUCTBUILD;
#ifdef RX_PRIVATE_BUILD
ULONG RxPrivateBuild = 1;
#else
ULONG RxPrivateBuild = 0;
#endif

#ifdef MONOLITHIC_MINIRDR
RDBSS_DEVICE_OBJECT RxSpaceForTheWrappersDeviceObject;
#endif

#define LANMAN_WORKSTATION_PARAMETERS \
    L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\LanmanWorkStation\\Parameters"

BOOLEAN DisableByteRangeLockingOnReadOnlyFiles = FALSE;

VOID
RxReadRegistryParameters();
      
NPAGED_LOOKASIDE_LIST RxContextLookasideList;

VOID
RxGetRegistryParameters(
    IN PUNICODE_STRING RegistryPath
    );

NTSTATUS
RxInitializeRegistrationStructures(
    void
    );
VOID
RxUninitializeRegistrationStructures(
    void
    );

VOID
RxAbbreviatedWriteLogEntry__ (
    IN NTSTATUS Status,
    IN ULONG    OneExtraUlong
    );
#define RxAbbreviatedWriteLogEntry(STATUS__) RxAbbreviatedWriteLogEntry__(STATUS__,__LINE__)


NTSTATUS
RxGetStringRegistryParameter(
    HANDLE ParametersHandle,
    PWCHAR ParameterName,
    PUNICODE_STRING ParamString,
    PKEY_VALUE_PARTIAL_INFORMATION Value,
    ULONG ValueSize,
    BOOLEAN LogFailure
    );

NTSTATUS
RxGetUlongRegistryParameter(
    HANDLE ParametersHandle,
    PWCHAR ParameterName,
    PULONG ParamUlong,
    PKEY_VALUE_PARTIAL_INFORMATION Value,
    ULONG ValueSize,
    BOOLEAN LogFailure
    );

//this type and variable are used for unwinding the initialization so that stuff doesn't go thru the cracks
// the way that this works is that stuff is done in the reverse order of the enumeration. that way i can just
// use a switch-no-break to unwind.
typedef enum _RX_INIT_STATES {
    RXINIT_ALL_INITIALIZATION_COMPLETED,
    RXINIT_CONSTRUCTED_PROVIDERORDER,
    RXINIT_CREATED_LOG,
    RXINIT_CREATED_DEVICE_OBJECT,
    RXINIT_CREATED_FIRST_LINK,
    RXINIT_START
} RX_INIT_STATES;

VOID
RxInitUnwind(
    IN PDRIVER_OBJECT DriverObject,
    IN RX_INIT_STATES RxInitState
    );

#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, RxDriverEntry)
#pragma alloc_text(INIT, RxGetRegistryParameters)
#pragma alloc_text(INIT, RxGetStringRegistryParameter)
#pragma alloc_text(INIT, RxGetUlongRegistryParameter)
#pragma alloc_text(INIT, RxAbbreviatedWriteLogEntry__)
#pragma alloc_text(PAGE, RxUnload)
#pragma alloc_text(PAGE, RxInitUnwind)
#pragma alloc_text(PAGE, RxGetNetworkProviderPriority)
#pragma alloc_text(PAGE, RxInitializeRegistrationStructures)
#pragma alloc_text(PAGE, RxUninitializeRegistrationStructures)
#pragma alloc_text(PAGE, RxInitializeMinirdrDispatchTable)
#pragma alloc_text(PAGE, __RxFillAndInstallFastIoDispatch)
#pragma alloc_text(PAGE, RxReadRegistryParameters)
#endif

#define RX_SYMLINK_NAME L"\\??\\fsWrap"

BOOLEAN EnableWmiLog = FALSE;

NTSTATUS
RxDriverEntry(
    IN PDRIVER_OBJECT DriverObject,
    IN PUNICODE_STRING RegistryPath
    )

/*++

Routine Description:

    This is the initialization routine for the Rx file system
    device driver.  This routine creates the device object for the FileSystem
    device and performs all other driver initialization.

Arguments:

    DriverObject - Pointer to driver object created by the system.

Return Value:

    RXSTATUS - The function value is the final status from the initialization
        operation.

--*/

{
    NTSTATUS Status;
    RX_INIT_STATES RxInitState = 0;
#ifndef MONOLITHIC_MINIRDR
    UNICODE_STRING UnicodeString,LinkName;
#endif

    //Structure checks
    RxCheckFcbStructuresForAlignment(); //this will bugcheck if things are bad

    //
    //  Initialize the global data structures


    ZeroAndInitializeNodeType( &RxData, RDBSS_NTC_DATA_HEADER, sizeof(RDBSS_DATA));
    RxData.DriverObject = DriverObject;

    ZeroAndInitializeNodeType( &RxDeviceFCB, RDBSS_NTC_DEVICE_FCB, sizeof(FCB));

    KeInitializeSpinLock( &RxStrucSupSpinLock );
    RxExports.pRxStrucSupSpinLock = &RxStrucSupSpinLock;

    RxInitializeDebugSupport();

    try {

        Status = RXINIT_START;

#ifndef MONOLITHIC_MINIRDR
        //
        // Create a symbolic link from \\dosdevices\fswrap to the rdbss device object name

        RtlInitUnicodeString(&LinkName, RX_SYMLINK_NAME);
        RtlInitUnicodeString(&UnicodeString, DD_NFS2_DEVICE_NAME_U);

        IoDeleteSymbolicLink(&LinkName);
        Status = IoCreateSymbolicLink(&LinkName, &UnicodeString);

        if (!NT_SUCCESS( Status )) {
            try_return( Status );
        }
        RxInitState = RXINIT_CREATED_FIRST_LINK;

        //
        // Create the device object.

        Status = IoCreateDevice(DriverObject,
                                sizeof(RDBSS_DEVICE_OBJECT) - sizeof(DEVICE_OBJECT),
                                &UnicodeString,
                                FILE_DEVICE_NETWORK_FILE_SYSTEM,
                                FILE_REMOTE_DEVICE,
                                FALSE,
                                (PDEVICE_OBJECT *)(&RxFileSystemDeviceObject));

        if (!NT_SUCCESS( Status )) {
            try_return( Status );
        }
        RxInitState = RXINIT_CREATED_DEVICE_OBJECT;

#else

// in monolithic mode, the wrapper doesn't really need a device object but
//   we allocate stuff in the wrapper's device object in order to appropriately throttle
//   thread usage per device object.
        RxFileSystemDeviceObject = &RxSpaceForTheWrappersDeviceObject;
        RtlZeroMemory(RxFileSystemDeviceObject,sizeof(RxSpaceForTheWrappersDeviceObject));

#endif
        //
        // Initialize the trace and logging facilities. loginit is a big allocation.

        RxInitializeDebugTrace();

        RxInitializeLog();

        RxInitState = RXINIT_CREATED_LOG;

        RxGetRegistryParameters(RegistryPath);
        RxDbgTrace(0, (DEBUG_TRACE_ALWAYS), ("Constants %08lx %08lx\n",
                   RX_CONTEXT_FLAG_WAIT,
                   RX_CONTEXT_CREATE_FLAG_ADDEDBACKSLASH
                   ));

        RxReadRegistryParameters();

        //
        // Initialize the minirdr registration facilities.

        Status = RxInitializeRegistrationStructures();

        if (!NT_SUCCESS( Status )) {
            try_return( Status );
        }
        RxInitState = RXINIT_CONSTRUCTED_PROVIDERORDER;

  try_exit: NOTHING;
    } finally {
        if (Status != STATUS_SUCCESS) {
            RxLogFailure (
                RxFileSystemDeviceObject,
                NULL,
                EVENT_RDR_UNEXPECTED_ERROR,
                Status);

            RxInitUnwind(DriverObject,RxInitState);
        }
    }

    if (Status != STATUS_SUCCESS) {
        return(Status);
    }

        //
    //
    //
    //
    //       #####     ##    #    #   ####   ######  #####
    //       #    #   #  #   ##   #  #    #  #       #    #
    //       #    #  #    #  # #  #  #       #####   #    #
    //       #    #  ######  #  # #  #  ###  #       #####
    //       #    #  #    #  #   ##  #    #  #       #   #
    //       #####   #    #  #    #   ####   ######  #    #
    //
    //
    //
    //  EVERYTHING FROM HERE DOWN BETTER WORK BECAUSE THERE IS NO MORE UNWINDING!!!
    //
    //


    RxInitializeDispatcher();
    RxInitializeBackoffPackage();

    // Initialize the look aside list for RxContext allocation
    ExInitializeNPagedLookasideList(
        &RxContextLookasideList,
        ExAllocatePoolWithTag,
        ExFreePool,
        0,
        sizeof(RX_CONTEXT),
        RX_IRPC_POOLTAG,
        4);

    // Initialize the list of transport Irps to an empty list
    InitializeListHead(&RxIrpsList);
    KeInitializeSpinLock(&RxIrpsListSpinLock);
    
    // Initialize the list of active contexts to an empty list
    InitializeListHead(&RxActiveContexts);

    // Initialize the list of srv call downs active
    InitializeListHead(&RxSrvCalldownList);

    // a fastmutex is used to serialize access to the Qs that serialize blocking pipe operations
    ExInitializeFastMutex(&RxContextPerFileSerializationMutex);

    // and to serialize access to the Qs that serialize some pagingio operations
    ExInitializeFastMutex(&RxLowIoPagingIoSyncMutex);

    // Initialize the scavenger mutex
    KeInitializeMutex(&RxScavengerMutex,1);

    // Initialize the global serialization Mutex.
    KeInitializeMutex(&RxSerializationMutex,1);

    //
    //  Initialize the wrapper's overflow queue
    //

    {
        PRDBSS_DEVICE_OBJECT MyDo = (PRDBSS_DEVICE_OBJECT)RxFileSystemDeviceObject;
        LONG Index;

        for (Index = 0; Index < MaximumWorkQueue; Index++) {
            MyDo->OverflowQueueCount[Index] = 0;

            InitializeListHead( &MyDo->OverflowQueue[Index] );

            MyDo->PostedRequestCount[Index] = 0;
        }

        KeInitializeSpinLock( &MyDo->OverflowQueueSpinLock );
    }

    //
    //      Initialize dispatch vector for driver object AND ALSO FOR the devicefcb

    RxInitializeDispatchVectors(DriverObject);
    ExInitializeResourceLite( &RxData.Resource );

    //
    //  Set up global pointer to our process.

    RxData.OurProcess = PsGetCurrentProcess();

    //
    //  Put in a bunch of sanity checks about various structures...hey, it's init code!

    IF_DEBUG {
        ULONG FcbStateBufferingMask = FCB_STATE_BUFFERING_STATE_MASK;
        ULONG MinirdrBufStateCommandMask = MINIRDR_BUFSTATE_COMMAND_MASK;
        USHORT EightBitsPerChar = 8;

        //we could put in defines for the ULONG/USHORTS here...but they don't change often
        ASSERT ( MRDRBUFSTCMD_MAXXX == (sizeof(ULONG)*EightBitsPerChar) );

        ASSERT (!(FcbStateBufferingMask&MinirdrBufStateCommandMask));

    }

    //
    //  Setup the timer subsystem

    RxInitializeRxTimer();

#ifndef MONOLITHIC_MINIRDR
    Status = IoWMIRegistrationControl ((PDEVICE_OBJECT)RxFileSystemDeviceObject, WMIREG_ACTION_REGISTER);
    
    if (Status != STATUS_SUCCESS) {
        DbgPrint("Rdbss fails to register WMI %lx\n",Status);
    } else {
        EnableWmiLog = TRUE;
    }
#endif

    return( STATUS_SUCCESS );
}


//
//  Unload routine
//

VOID
RxUnload(
    IN PDRIVER_OBJECT DriverObject
    )
/*++

Routine Description:

     This is the unload routine for the RDBSS.

Arguments:

     DriverObject - pointer to the driver object for the RDBSS

Return Value:

     None

--*/

{
    //UNICODE_STRING LinkName;

    PAGED_CODE();

    RxTearDownRxTimer();

//remember this
//    RdrUnloadSecurity();

    RxDbgTrace( 0, (DEBUG_TRACE_ALWAYS), ("RxUnload: DriverObject =%08lx\n", DriverObject ));

    ExDeleteResourceLite(&RxData.Resource);

    RxUninitializeBackoffPackage();

    RxTearDownDispatcher();

    RxTearDownDebugSupport();

    ExDeleteNPagedLookasideList(
        &RxContextLookasideList);

    RxInitUnwind(DriverObject, RXINIT_ALL_INITIALIZATION_COMPLETED);
    
    if (EnableWmiLog) {
        NTSTATUS Status;

        Status = IoWMIRegistrationControl ((PDEVICE_OBJECT)RxFileSystemDeviceObject, WMIREG_ACTION_DEREGISTER);
        if (Status != STATUS_SUCCESS) {
            DbgPrint("Rdbss fails to deregister WMI %lx\n",Status);
        }
    }
}

#if DBG
PCHAR RxUnwindFollower = NULL;
#define UNWIND_CASE(x) case x: RxUnwindFollower = #x;
#else
#define UNWIND_CASE(x) case x:
#endif



VOID
RxInitUnwind(
    IN PDRIVER_OBJECT DriverObject,
    IN RX_INIT_STATES RxInitState
    )
/*++

Routine Description:

     This routine does the common uninit work for unwinding from a bad driver entry or for unloading.

Arguments:

     RxInitState - tells how far we got into the intialization

Return Value:

     None

--*/

{
#ifndef MONOLITHIC_MINIRDR
    UNICODE_STRING LinkName;
#endif

    PAGED_CODE();

    switch (RxInitState) {
    UNWIND_CASE(RXINIT_ALL_INITIALIZATION_COMPLETED)
        //Nothing extra to do...this is just so that the constant in RxUnload doesn't change.......
        //lack of break intentional

    UNWIND_CASE(RXINIT_CONSTRUCTED_PROVIDERORDER)
        RxUninitializeRegistrationStructures();
        //lack of break intentional

    UNWIND_CASE(RXINIT_CREATED_LOG)
        RxUninitializeLog();
        //lack of break intentional

#ifndef MONOLITHIC_MINIRDR
    UNWIND_CASE(RXINIT_CREATED_DEVICE_OBJECT)
        IoDeleteDevice((PDEVICE_OBJECT)RxFileSystemDeviceObject);
        //lack of break intentional

    UNWIND_CASE(RXINIT_CREATED_FIRST_LINK)
        RtlInitUnicodeString(&LinkName, RX_SYMLINK_NAME);
        IoDeleteSymbolicLink(&LinkName);
        // lack of break intentional
#endif

    UNWIND_CASE(RXINIT_START)
        //RdrUninitializeDiscardableCode();
        //DbgBreakPoint();
        break;
    }

}


VOID
RxAbbreviatedWriteLogEntry__ (
    IN NTSTATUS Status,
    IN ULONG    OneExtraUlong
    )
/*++

Routine Description:

     This routine write a log entry with most of the fields set to fixed values.

Arguments:

     Status - the status to be stored in the log record
     OneExtraUlong - a key for finding out what happened

Return Value:

     none.

--*/
{
    //RxLogFailureWithBuffer (
    //    RxFileSystemDeviceObject,
    //    NULL,
    //    EVENT_RDR_CANT_READ_REGISTRY,
    //    Status,
    //    &OneExtraUlong,
    //    sizeof(ULONG)
    //    );
}


VOID
RxGetRegistryParameters(
    PUNICODE_STRING RegistryPath
    )
{
    ULONG Storage[256];
    UNICODE_STRING UnicodeString;
    HANDLE ConfigHandle;
    HANDLE ParametersHandle;
    NTSTATUS Status;
    //ULONG BytesRead;
    OBJECT_ATTRIBUTES ObjectAttributes;
    PKEY_VALUE_FULL_INFORMATION Value = (PKEY_VALUE_FULL_INFORMATION)Storage;

    PAGED_CODE(); //INIT

    InitializeObjectAttributes(
        &ObjectAttributes,
        RegistryPath,               // name
        OBJ_CASE_INSENSITIVE,       // attributes
        NULL,                       // root
        NULL                        // security descriptor
        );

    Status = ZwOpenKey (&ConfigHandle, KEY_READ, &ObjectAttributes);

    if (!NT_SUCCESS(Status)) {

        //RxLogFailure (
        //    RxFileSystemDeviceObject,
        //    NULL,
        //    EVENT_RDR_CANT_READ_REGISTRY,
        //    Status);
        return;
    }

    RtlInitUnicodeString(&UnicodeString, L"Parameters");

    InitializeObjectAttributes(
        &ObjectAttributes,
        &UnicodeString,
        OBJ_CASE_INSENSITIVE,
        ConfigHandle,
        NULL
        );


    Status = ZwOpenKey (&ParametersHandle, KEY_READ, &ObjectAttributes);

    if (!NT_SUCCESS(Status)) {

        //RxLogFailure (
        //    RxFileSystemDeviceObject,
        //    NULL,
        //    EVENT_RDR_CANT_READ_REGISTRY,
        //    Status);

        ZwClose(ConfigHandle);

        return;
    }

#ifdef RDBSSLOG
    RxGetStringRegistryParameter(ParametersHandle,
                              L"InitialDebugString",
                              &UnicodeString,
                              (PKEY_VALUE_PARTIAL_INFORMATION) Storage,
                              sizeof(Storage),
                              FALSE
                              );


    //DbgPrint("String From Registry %wZ, buffer = %08lx\n",&UnicodeString,UnicodeString.Buffer);
    if (UnicodeString.Length && UnicodeString.Length<320) {
        PWCH u = UnicodeString.Buffer;
        ULONG l;
        PCH p = (PCH)u;

        for (l=0;l<UnicodeString.Length;l++) {
            *p++ = (CHAR)*u++;
            *p = 0;
        }

        DbgPrint("InitialDebugString From Registry as singlebytestring: <%s>\n",UnicodeString.Buffer);
        RxDebugControlCommand((PCH)UnicodeString.Buffer);
    }
#endif //RDBSSLOG

    ZwClose(ParametersHandle);

    ZwClose(ConfigHandle);

}


NTSTATUS
RxGetStringRegistryParameter(
    HANDLE ParametersHandle,
    PWCHAR ParameterName,
    PUNICODE_STRING ParamString,
    PKEY_VALUE_PARTIAL_INFORMATION Value,
    ULONG ValueSize,
    BOOLEAN LogFailure
    )
{
    UNICODE_STRING UnicodeString;
    NTSTATUS Status;
    ULONG BytesRead;

    PAGED_CODE(); //INIT

    RtlInitUnicodeString(&UnicodeString, ParameterName);

    Status = ZwQueryValueKey(ParametersHandle,
                        &UnicodeString,
                        KeyValuePartialInformation,
                        Value,
                        ValueSize,
                        &BytesRead);

    ParamString->Length = 0;
    ParamString->Buffer = NULL;
    if (NT_SUCCESS(Status)) {
        ParamString->Buffer = (PWCH)(&Value->Data[0]);
        //the datalength actually accounts for the trailing null
        ParamString->Length = ((USHORT)Value->DataLength) - sizeof(WCHAR);
        ParamString->MaximumLength = ParamString->Length;
        return STATUS_SUCCESS;
    }

    if (!LogFailure) { return Status; }


    RxLogFailure (
        RxFileSystemDeviceObject,
        NULL,
        EVENT_RDR_CANT_READ_REGISTRY,
        Status);

    return Status;
}


NTSTATUS
RxGetUlongRegistryParameter(
    HANDLE ParametersHandle,
    PWCHAR ParameterName,
    PULONG ParamUlong,
    PKEY_VALUE_PARTIAL_INFORMATION Value,
    ULONG ValueSize,
    BOOLEAN LogFailure
    )
{
    UNICODE_STRING UnicodeString;
    NTSTATUS Status;
    ULONG BytesRead;

    PAGED_CODE(); //INIT

    RtlInitUnicodeString(&UnicodeString, ParameterName);

    Status = ZwQueryValueKey(ParametersHandle,
                        &UnicodeString,
                        KeyValuePartialInformation,
                        Value,
                        ValueSize,
                        &BytesRead);


    if (NT_SUCCESS(Status)) {
        if (Value->Type == REG_DWORD) {
            PULONG ConfigValue = (PULONG)&Value->Data[0];
            *ParamUlong = *((PULONG)ConfigValue);
            DbgPrint("readRegistryvalue %wZ = %08lx\n",&UnicodeString,*ParamUlong);
            return(STATUS_SUCCESS);
        } else {
            Status = STATUS_INVALID_PARAMETER;
        }
     }

     if (!LogFailure) { return Status; }

     RxLogFailureWithBuffer (
         RxFileSystemDeviceObject,
         NULL,
         EVENT_RDR_CANT_READ_REGISTRY,
         Status,
         ParameterName,
         (USHORT)(wcslen(ParameterName)*sizeof(WCHAR))
         );

     return Status;
}


/*-------------------------------

This set of routines implements the network provider order in the wrapper.
The way that this works is somewhat complicated. First, we go to the registry
to get the provider order; it is stored at key=PROVIDERORDER_REGISTRY_KEY and
value=L"ProviderOrder". This is a list of service providers whereas what we need
are the device names. So, for each ServiceProverName, we go to the registry to
get the device name at key=SERVICE_REGISTRY_KEY, subkey=ServiceProverName,
subsubkey=NETWORK_PROVIDER_SUBKEY, and value=L"Devicename".

We build a linked list of these guys. Later when a minirdr registers, we look
on this list for the corresponding device name and that gives us the priority.

simple? i think NOT.


----------------------------------*/

#ifndef MONOLITHIC_MINIRDR

NTSTATUS
RxAccrueProviderFromServiceName (
    HANDLE           ServicesHandle,
    PUNICODE_STRING  ServiceName,
    ULONG            Priority,
    PWCHAR           ProviderInfoNameBuffer,
    ULONG            ProviderInfoNameBufferLength
    );

NTSTATUS
RxConstructProviderOrder (
    void
    );

VOID
RxDestructProviderOrder (
    void
    );

#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, RxAccrueProviderFromServiceName)
#pragma alloc_text(INIT, RxConstructProviderOrder)
#pragma alloc_text(PAGE, RxDestructProviderOrder)
#endif


#define PROVIDERORDER_REGISTRY_KEY L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\NetworkProvider\\Order"
#define SERVICE_REGISTRY_KEY L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\"
#define NETWORK_PROVIDER_SUBKEY L"\\networkprovider"


typedef struct _RX_UNC_PROVIDER_HEADER {
        union {
            LIST_ENTRY;
            LIST_ENTRY Links;
        };
        ULONG Priority;
        union {
            UNICODE_STRING;
            UNICODE_STRING DeviceName;
        };
} RX_UNC_PROVIDER_HEADER;

typedef struct _RX_UNC_PROVIDER {
    RX_UNC_PROVIDER_HEADER;
    KEY_VALUE_PARTIAL_INFORMATION Info;
} RX_UNC_PROVIDER, *PRX_UNC_PROVIDER;

LIST_ENTRY RxUncProviders;

ULONG
RxGetNetworkProviderPriority(
    IN PUNICODE_STRING DeviceName
    )
/*++

Routine Description:

     This routine is called at minirdr registration time to find out the priority
     of the provider with the given DeviceName. It simply looks it up on a list.

Arguments:

     DeviceName - name of the device whose priority is to be found

Return Value:

     the network provider priority that the MUP will use.

--*/
{
    PLIST_ENTRY Entry;

    PAGED_CODE();

    RxLog(("FindUncProvider %wZ \n",DeviceName));
    RxWmiLog(LOG,
             RxGetNetworkProviderPriority,
             LOGUSTR(*DeviceName));

    for (Entry = RxUncProviders.Flink;
         Entry != &RxUncProviders;
        ) {

        PRX_UNC_PROVIDER UncProvider = (PRX_UNC_PROVIDER)Entry;
        Entry = Entry->Flink;
        if ( RtlEqualUnicodeString( DeviceName, &UncProvider->DeviceName, TRUE )) {
            return(UncProvider->Priority);
        }

    }

    // no corresponding entry was found

    return(0x7effffff); //got this constant from the MUP........
}


NTSTATUS
RxAccrueProviderFromServiceName (
    HANDLE           ServicesHandle,
    PUNICODE_STRING  ServiceName,
    ULONG            Priority,
    PWCHAR           ProviderInfoNameBuffer,
    ULONG            ProviderInfoNameBufferLength
    )
/*++

Routine Description:

     This routine has the responsibility to look up the device name corresponding
     to a particular provider name; if successful, the device name and the corresponding
     priority are recorded on the UncProvider List.

Arguments:

    HANDLE           ServicesHandle  - a handle to the services root in the registry
    PUNICODE_STRING  ServiceName     - the name of the service relative to the servicehandle
    ULONG            Priority,       - the priority of this provider
    PWCHAR           ProviderInfoNameBuffer,    - a buffer that can be used to accrue the subkey name
    ULONG            ProviderInfoNameBufferLength   - and the length

Return Value:

     STATUS_SUCCESS if everything worked elsewise an error status.

--*/
{
    NTSTATUS Status;
    OBJECT_ATTRIBUTES ObjectAttributes;
    UNICODE_STRING ProviderInfoName,ProviderInfoKey,ParameterDeviceName;
    HANDLE NetworkProviderInfoHandle = INVALID_HANDLE_VALUE;
    KEY_VALUE_PARTIAL_INFORMATION InitialValuePartialInformation;
    ULONG DummyBytesRead,ProviderLength;
    PRX_UNC_PROVIDER UncProvider = NULL;

    PAGED_CODE();

    RxLog(("SvcNm %wZ",ServiceName));
    RxWmiLog(LOG,
             RxAccrueProviderFromServiceName_1,
             LOGUSTR(*ServiceName));
    //
    // Form the correct keyname using the bufferspace provided

    ProviderInfoName.Buffer = ProviderInfoNameBuffer;
    ProviderInfoName.Length = 0;
    ProviderInfoName.MaximumLength = (USHORT)ProviderInfoNameBufferLength;

    Status = RtlAppendUnicodeStringToString(&ProviderInfoName,ServiceName);
    if (Status != STATUS_SUCCESS) {
        DbgPrint("Could append1: %08lx %wZ\n",Status,&ProviderInfoName);
        //RxAbbreviatedWriteLogEntry(Status);
        goto FINALLY;
    }
    RtlInitUnicodeString(&ProviderInfoKey, NETWORK_PROVIDER_SUBKEY);
    Status = RtlAppendUnicodeStringToString(&ProviderInfoName,&ProviderInfoKey);
    if (Status != STATUS_SUCCESS) {
        DbgPrint("Could append2: %08lx %wZ\n",Status,&ProviderInfoName);
        RxAbbreviatedWriteLogEntry(Status);
        goto FINALLY;
    }

    //
    // Open the key in preparation for reeading the devicename value

    //DbgPrint("RxAccrueProviderFromServiceName providerinfoname %wZ\n",&ProviderInfoName);
    InitializeObjectAttributes(
        &ObjectAttributes,
        &ProviderInfoName,      // name
        OBJ_CASE_INSENSITIVE,   // attributes
        ServicesHandle,         // root
        NULL                    // security descriptor
        );

    Status = ZwOpenKey (&NetworkProviderInfoHandle, KEY_READ, &ObjectAttributes);

    if (!NT_SUCCESS(Status)) {
        DbgPrint("NetWorkProviderInfoFailed: %08lx %wZ\n",Status,&ProviderInfoName);
        RxAbbreviatedWriteLogEntry(Status);
        goto FINALLY;
    }

    //
    //  Read the devicename. we do this in two steps. first, we do a partial read to find out
    //  how big the name really is. Then, we allocate a UncProviderEntry of the correctsize and make
    //  a second call to fill it in.


    //DbgPrint("RxAccrueProviderFromServiceName ServiceName %wZ; worked\n",ServiceName);
    RtlInitUnicodeString(&ParameterDeviceName, L"DeviceName");

    Status = ZwQueryValueKey(NetworkProviderInfoHandle,
                        &ParameterDeviceName,
                        KeyValuePartialInformation,
                        &InitialValuePartialInformation,
                        sizeof(InitialValuePartialInformation),
                        &DummyBytesRead);
    if (Status==STATUS_BUFFER_OVERFLOW) {
        Status=STATUS_SUCCESS;
    }
    if (Status!=STATUS_SUCCESS) {
        RxAbbreviatedWriteLogEntry(Status);
        goto FINALLY;
    }
    //DbgPrint("RxAccrueProviderFromServiceName ServiceName %wZ; worked; devicenamesize=%08lx\n",
    //                                    ServiceName,InitialValuePartialInformation.DataLength);

    ProviderLength = sizeof(RX_UNC_PROVIDER)+InitialValuePartialInformation.DataLength;
    UncProvider = RxAllocatePoolWithTag(PagedPool | POOL_COLD_ALLOCATION ,ProviderLength,RX_MRX_POOLTAG);
    if (UncProvider==NULL) {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        DbgPrint("UncProviderAllocationFailed: %08lx %wZ\n",Status,&ProviderInfoName);
        RxAbbreviatedWriteLogEntry(Status);
        goto FINALLY;
    }

    Status = ZwQueryValueKey(NetworkProviderInfoHandle,
                        &ParameterDeviceName,
                        KeyValuePartialInformation,
                        &UncProvider->Info,
                        ProviderLength,
                        &DummyBytesRead);
    if (Status!=STATUS_SUCCESS) {
        RxAbbreviatedWriteLogEntry(Status);
        goto FINALLY;
    }

    //
    // Finish filling in the UncProviderEntry and link it in

    UncProvider->Buffer = (PWCHAR)(&UncProvider->Info.Data[0]);
    UncProvider->Length = (USHORT)(UncProvider->Info.DataLength-sizeof(WCHAR)); //dont include trailing NULL
    UncProvider->MaximumLength = UncProvider->Length;
    UncProvider->Priority = Priority;

    InsertTailList(&RxUncProviders,&UncProvider->Links);

    RxLog(("Dvc p=%lx Nm %wZ",UncProvider->Priority,&UncProvider->DeviceName));
    RxWmiLog(LOG,
             RxAccrueProviderFromServiceName_2,
             LOGULONG(UncProvider->Priority)
             LOGUSTR(UncProvider->DeviceName));
    UncProvider = NULL;

FINALLY:

    //
    // if we obtained a handle to ...\\services\<sevicename>\providerinfo then close it

    if (NetworkProviderInfoHandle!=INVALID_HANDLE_VALUE) ZwClose(NetworkProviderInfoHandle);

    if (UncProvider != NULL) {
        RxFreePool(UncProvider);
    }

    return Status;
}

NTSTATUS
RxConstructProviderOrder(
    void
    )
/*++

Routine Description:

     This routine has the responsibility to build the list of network providers
     that is used to look up provider priority at minirdr registration time. It does this
     by first reading the providerorder string fron the registry;  then for each provider
     listed in the string, a helper routine is called to lookup the corresponding device
     name and insert an entry on the provider list.

Arguments:

     none.

Return Value:

     STATUS_SUCCESS if everything worked elsewise an error status.


--*/
{
    KEY_VALUE_PARTIAL_INFORMATION InitialValuePartialInformation;
    UNICODE_STRING ProviderOrderValueName;
    ULONG DummyBytesRead;
    PBYTE ProviderOrderStringBuffer;
    PBYTE ServiceNameStringBuffer = NULL;
    ULONG  ProviderOrderStringLength,ServiceNameStringLength,AllocationLength;

    UNICODE_STRING UnicodeString;
    UNICODE_STRING ProviderOrder;
    PWCHAR ScanPtr,FinalScanPtr;
    HANDLE NPOrderHandle = INVALID_HANDLE_VALUE;
    HANDLE ServiceRootHandle = INVALID_HANDLE_VALUE;
    NTSTATUS Status;
    OBJECT_ATTRIBUTES ObjectAttributes;
    ULONG Priority = 0;

    PAGED_CODE();

    RxLog(("RxConstructProviderOrder"));
    RxWmiLog(LOG,
             RxConstructProviderOrder_1,
             LOGULONG(Priority));
    InitializeListHead(&RxUncProviders);

    //
    // Start by opening the service registry key. This is the root key of all services
    // and is used for relative opens by the helper routine so that string manipulation
    // is reduced.

    RtlInitUnicodeString(&UnicodeString, SERVICE_REGISTRY_KEY);

    InitializeObjectAttributes(
        &ObjectAttributes,
        &UnicodeString,             // name
        OBJ_CASE_INSENSITIVE,       // attributes
        NULL,                       // root
        NULL                        // security descriptor
        );

    Status = ZwOpenKey (&ServiceRootHandle, KEY_READ, &ObjectAttributes);

    if (!NT_SUCCESS(Status)) {
        DbgPrint("ServiceRootOpenFailed: %08lx %wZ\n",Status,&UnicodeString);
        RxAbbreviatedWriteLogEntry(Status);
        goto FINALLY;
    }

    //
    // Now open up the key where we find the provider order string

    RtlInitUnicodeString(&UnicodeString, PROVIDERORDER_REGISTRY_KEY);

    InitializeObjectAttributes(
        &ObjectAttributes,
        &UnicodeString,             // name
        OBJ_CASE_INSENSITIVE,       // attributes
        NULL,                       // root
        NULL                        // security descriptor
        );

    Status = ZwOpenKey (&NPOrderHandle, KEY_READ, &ObjectAttributes);

    if (!NT_SUCCESS(Status)) {
        DbgPrint("NetProviderOpenFailed: %08lx %wZ\n",Status,&UnicodeString);
        RxAbbreviatedWriteLogEntry(Status);
        goto FINALLY;
    }

    //
    //Find out how long the provider order string is

    RtlInitUnicodeString(&ProviderOrderValueName, L"ProviderOrder");

    Status = ZwQueryValueKey(NPOrderHandle,
                        &ProviderOrderValueName,
                        KeyValuePartialInformation,
                        &InitialValuePartialInformation,
                        sizeof(InitialValuePartialInformation),
                        &DummyBytesRead);
    if (Status==STATUS_BUFFER_OVERFLOW) {
        Status=STATUS_SUCCESS;
    }
    if (Status!=STATUS_SUCCESS) {
        DbgPrint("ProviderOrderStringPartialInfoFailed: %08lx %wZ\n",Status,&ProviderOrderValueName);
        RxAbbreviatedWriteLogEntry(Status);
        goto FINALLY;
    }

    //
    // allocate two buffers: one buffer will hold the provider string -- ProviderOrderStringBuffer.
    // it has to be as long as the providerorder string plus enough extra for the registry
    // structure used in the call. the second buffer is used to hold the servicename key--it has
    // to be as long as any element of the provider string plus enough extra to hold the suffix
    // NETWORK_PROVIDER_SUBKEY. in order to only parse the string once, we just allocate for a complete
    // additional copy of the provider string. we actually combine these into a single allocation.

    ProviderOrderStringLength = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + InitialValuePartialInformation.DataLength;
    ProviderOrderStringLength = QuadAlign(ProviderOrderStringLength +2*sizeof(WCHAR));   //chars added below

    ServiceNameStringLength = sizeof(NETWORK_PROVIDER_SUBKEY) + InitialValuePartialInformation.DataLength;
    ServiceNameStringLength = QuadAlign(ServiceNameStringLength);

    AllocationLength = ProviderOrderStringLength + ServiceNameStringLength;
    RxLog(("prov string=%lx,alloc=%lx\n",
                InitialValuePartialInformation.DataLength,
                AllocationLength));
    RxWmiLog(LOG,
             RxConstructProviderOrder_2,
             LOGULONG(InitialValuePartialInformation.DataLength)
             LOGULONG(AllocationLength));

    ServiceNameStringBuffer = RxAllocatePoolWithTag(PagedPool | POOL_COLD_ALLOCATION,AllocationLength,RX_MRX_POOLTAG);
    if (ServiceNameStringBuffer==NULL) {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        RxAbbreviatedWriteLogEntry(Status);
        goto FINALLY;
    }
    ProviderOrderStringBuffer = ServiceNameStringBuffer+ServiceNameStringLength;

    //
    // now do the final read to get the providerorder string

    RxGetStringRegistryParameter(NPOrderHandle,
                              L"ProviderOrder",
                              &ProviderOrder,
                              (PKEY_VALUE_PARTIAL_INFORMATION) ProviderOrderStringBuffer,
                              ProviderOrderStringLength,
                              FALSE
                              );
    if (ProviderOrder.Buffer == NULL) {
        Status = STATUS_UNSUCCESSFUL;
        RxAbbreviatedWriteLogEntry(Status);
        goto FINALLY;
    }

    //
    // comma-terminate the string for easier living. then scan down the string
    // looking for comma terminated entries. for each entry found, try to accrue
    // it to the list

    ProviderOrder.Buffer[ProviderOrder.Length/sizeof(WCHAR)] = L',';

    ScanPtr = ProviderOrder.Buffer;
    FinalScanPtr = ScanPtr+(ProviderOrder.Length/sizeof(WCHAR));
    for (;;) {

        UNICODE_STRING ServiceName;

        // check for loop termination

        if (ScanPtr >= FinalScanPtr) { break; }
        if (*ScanPtr==L',') { ScanPtr++; continue; }

        // parse for a servicename

        ServiceName.Buffer = ScanPtr;
        for (;*ScanPtr!=L',';ScanPtr++) {
            //DbgPrint("Considering %c\n",*ScanPtr);
        }
        ASSERT(*ScanPtr==L',');
        ServiceName.Length = (USHORT)(sizeof(WCHAR)*(ScanPtr - ServiceName.Buffer));
        //DbgPrint("Length %08lx\n",ServiceName.Length);
        //DbgPrint("ServiceName %wZ\n",&ServiceName);

        // accrue it to the list

        Priority++;
        Status = RxAccrueProviderFromServiceName(ServiceRootHandle,
                                                 &ServiceName,
                                                 Priority,
                                                 (PWCHAR)ServiceNameStringBuffer,
                                                 ServiceNameStringLength  );
        if (Status == STATUS_INSUFFICIENT_RESOURCES) {
            goto FINALLY; //a log entry has already been generated
        } else {
            Status = STATUS_SUCCESS;
        }
    }

FINALLY:

    //
    // give back anything that we got in this procedure

    if (NPOrderHandle!=INVALID_HANDLE_VALUE) ZwClose(NPOrderHandle);
    if (ServiceRootHandle!=INVALID_HANDLE_VALUE) ZwClose(ServiceRootHandle);
    if (ServiceNameStringBuffer!=NULL) RxFreePool(ServiceNameStringBuffer);

    //
    // if things didn't work, then we won't start....so give back
    // the stuff that we have

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


VOID
RxDestructProviderOrder(
    void
    )
{
    PLIST_ENTRY Entry;
    PAGED_CODE();

    //DbgPrint("RxDestructProviderOrder \n");
    for (Entry = RxUncProviders.Flink;
         Entry != &RxUncProviders;
        ) {
        PRX_UNC_PROVIDER UncProvider = (PRX_UNC_PROVIDER)Entry;
        Entry = Entry->Flink;
        //DbgPrint("RxDO UncP %wZ p=%x\n",&UncProvider->DeviceName,UncProvider->Priority);
        RxFreePool(UncProvider);
    }
    return;
}

#else
ULONG
RxGetNetworkProviderPriority(
    PUNICODE_STRING DeviceName
    )
{
    PAGED_CODE(); //DUPLICATE

    return(1); //this number is irrelevant for monolithic
}
#endif //#ifndef MONOLITHIC_MINIRDR

NTSTATUS
RxInitializeRegistrationStructures(
    void
    )
{
    NTSTATUS Status = STATUS_SUCCESS;
    PAGED_CODE();

    ExInitializeFastMutex(&RxData.MinirdrRegistrationMutex);
    RxData.NumberOfMinirdrsRegistered = 0;
    RxData.NumberOfMinirdrsStarted = 0;
    InitializeListHead( &RxData.RegisteredMiniRdrs );
#ifndef MONOLITHIC_MINIRDR
    Status = RxConstructProviderOrder();
#endif
    return(Status);
}

VOID
RxUninitializeRegistrationStructures(
    void
    )
{
    PAGED_CODE();

#ifndef MONOLITHIC_MINIRDR
    RxDestructProviderOrder();
#endif
}


VOID
RxInitializeMinirdrDispatchTable(
    IN PDRIVER_OBJECT DriverObject
    )
{
    PAGED_CODE();

#ifndef MONOLITHIC_MINIRDR
    {ULONG i;
    //finally, fill in the dispatch tables for normal guys.........
    for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) {
        DriverObject->MajorFunction[i] = RxData.DriverObject->MajorFunction[i];
    }}
    DriverObject->FastIoDispatch = RxData.DriverObject->FastIoDispatch;
#endif
}


VOID
NTAPI
__RxFillAndInstallFastIoDispatch(
    IN     PRDBSS_DEVICE_OBJECT RxDeviceObject,
    IN OUT PFAST_IO_DISPATCH FastIoDispatch,
    IN     ULONG             FastIoDispatchSize
    )
/*++

Routine Description:

    This routine fills out a fastiodispatch vector to be identical with
    that normal one and installs it into the driver object associated with
    the device object passed.

Arguments:

    RxDeviceObject - the device object that is to have its driver's fastiodispatch changed
    FastIoDispatch - the fastiodispatch table to fill in and use
    FastIoDispatchSize - the size of the table passed

Return Value:

    NONE

--*/
{
    ULONG TableSize = min(FastIoDispatchSize,
                          RxFastIoDispatch.SizeOfFastIoDispatch);
    PAGED_CODE();

#ifndef MONOLITHIC_MINIRDR
    RtlCopyMemory(FastIoDispatch,&RxFastIoDispatch,TableSize);
    FastIoDispatch->SizeOfFastIoDispatch = TableSize;
    RxDeviceObject->DriverObject->FastIoDispatch = FastIoDispatch;
    return;
#endif
}

VOID
RxReadRegistryParameters( 
    void
    )
{
    ULONG Storage[16];
    UNICODE_STRING UnicodeString;
    HANDLE ParametersHandle;
    NTSTATUS Status;
    OBJECT_ATTRIBUTES ObjectAttributes;
    UNICODE_STRING WorkStationParametersRegistryKeyName;
    PKEY_VALUE_PARTIAL_INFORMATION Value = (PKEY_VALUE_PARTIAL_INFORMATION)Storage;
    ULONG ValueSize;
    ULONG BytesRead;

    PAGED_CODE(); //INIT

    RtlInitUnicodeString(&WorkStationParametersRegistryKeyName,
        LANMAN_WORKSTATION_PARAMETERS);

    ValueSize = sizeof(Storage);

    InitializeObjectAttributes(
        &ObjectAttributes,
        &WorkStationParametersRegistryKeyName,  // name
        OBJ_CASE_INSENSITIVE,                   // attributes
        NULL,                                   // root
        NULL                                    // security descriptor
        );

    Status = ZwOpenKey (&ParametersHandle, KEY_READ, &ObjectAttributes);

    if (!NT_SUCCESS(Status)) {

        //RxLogFailure (
        //    RxFileSystemDeviceObject,
        //    NULL,
        //    EVENT_RDR_CANT_READ_REGISTRY,
        //    Status);
        return;
    }

    RtlInitUnicodeString(&UnicodeString, L"DisableByteRangeLockingOnReadOnlyFiles");

    Status = ZwQueryValueKey(ParametersHandle,
                            &UnicodeString,
                            KeyValuePartialInformation,
                            Value,
                            ValueSize,
                            &BytesRead);

    if (NT_SUCCESS(Status)) {
        if (Value->Type == REG_DWORD) {
            PULONG ConfigValue = (PULONG)&Value->Data[0];
            DisableByteRangeLockingOnReadOnlyFiles = 
                    (BOOLEAN) (*((PULONG)ConfigValue) != 0);
        }
    }

    ZwClose(ParametersHandle);
}

