/*
 * Copyright (c) 2008, 2009, 2010, 2011 Kernel Drivers, LLC.
 * Copyright (c) 2009, 2010, 2011 Your File System, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright
 *   notice,
 *   this list of conditions and the following disclaimer in the
 *   documentation
 *   and/or other materials provided with the distribution.
 * - Neither the names of Kernel Drivers, LLC and Your File System, Inc.
 *   nor the names of their contributors may be used to endorse or promote
 *   products derived from this software without specific prior written
 *   permission from Kernel Drivers, LLC and Your File System, Inc.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

//
// File: AFSLibrarySupport.cpp
//

#include "AFSCommon.h"

NTSTATUS
AFSLoadLibrary( IN ULONG Flags,
                IN UNICODE_STRING *ServicePath)
{

    NTSTATUS ntStatus = STATUS_SUCCESS;
    AFSDeviceExt       *pDevExt = (AFSDeviceExt *)AFSDeviceObject->DeviceExtension;
    UNICODE_STRING uniLibraryName;
    AFSDeviceExt *pLibDevExt = NULL;
    PFILE_OBJECT pLibraryFileObject = NULL;
    PDEVICE_OBJECT pLibraryDeviceObject = NULL;

    __Enter
    {

        //
        // Wait on the load library event so we don't race with any
        // other requests coming through
        //

        AFSDbgLogMsg( AFS_SUBSYSTEM_LOAD_LIBRARY,
                      AFS_TRACE_LEVEL_VERBOSE,
                      "%s Start load library\n",
                      __FUNCTION__);

        ntStatus = KeWaitForSingleObject( &pDevExt->Specific.Control.LoadLibraryEvent,
                                          Executive,
                                          KernelMode,
                                          FALSE,
                                          NULL);

        if( !NT_SUCCESS( ntStatus))
        {

            AFSDbgLogMsg( AFS_SUBSYSTEM_LOAD_LIBRARY | AFS_SUBSYSTEM_INIT_PROCESSING,
                          AFS_TRACE_LEVEL_ERROR,
                          "AFSLoadLibrary Wait for LoadLibraryEvent failure %08lX\n",
                          ntStatus);

            try_return( ntStatus);
        }

        //
        // Check our current state to ensure we currently do not have a library loaded
        //

        if( BooleanFlagOn( pDevExt->Specific.Control.LibraryState, AFS_LIBRARY_LOADED))
        {

            AFSDbgLogMsg( AFS_SUBSYSTEM_LOAD_LIBRARY | AFS_SUBSYSTEM_INIT_PROCESSING,
                          AFS_TRACE_LEVEL_VERBOSE,
                          "%s Library already loaded\n",
                          __FUNCTION__);

            try_return( ntStatus = STATUS_DEVICE_NOT_READY);
        }

        pDevExt->Specific.Control.LibraryServicePath.Buffer = (WCHAR *)AFSExAllocatePoolWithTag( PagedPool,
                                                                                                 ServicePath->Length,
                                                                                                 AFS_GENERIC_MEMORY_25_TAG);

        if( pDevExt->Specific.Control.LibraryServicePath.Buffer == NULL)
        {

            AFSDbgLogMsg( AFS_SUBSYSTEM_LOAD_LIBRARY | AFS_SUBSYSTEM_INIT_PROCESSING,
                          AFS_TRACE_LEVEL_ERROR,
                          "AFSLoadLibrary AFS_GENERIC_MEMORY_25_TAG allocation error\n");

            try_return( ntStatus = STATUS_INSUFFICIENT_RESOURCES);
        }

        RtlZeroMemory( pDevExt->Specific.Control.LibraryServicePath.Buffer,
                       ServicePath->Length);

        pDevExt->Specific.Control.LibraryServicePath.Length = ServicePath->Length;
        pDevExt->Specific.Control.LibraryServicePath.MaximumLength = pDevExt->Specific.Control.LibraryServicePath.Length;

        RtlCopyMemory( pDevExt->Specific.Control.LibraryServicePath.Buffer,
                       ServicePath->Buffer,
                       pDevExt->Specific.Control.LibraryServicePath.Length);

        //
        // Load the library
        //

        ntStatus = ZwLoadDriver( ServicePath);

        if( !NT_SUCCESS( ntStatus))
        {

            AFSDbgLogMsg( AFS_SUBSYSTEM_LOAD_LIBRARY | AFS_SUBSYSTEM_INIT_PROCESSING,
                          AFS_TRACE_LEVEL_ERROR,
                          "%s Failed to load library Status %08lX\n",
                          __FUNCTION__,
                          ntStatus);

            try_return( ntStatus);
        }

        //
        // Open up the control device and grab teh entry points for the library
        //

        RtlInitUnicodeString( &uniLibraryName,
                              AFS_LIBRARY_CONTROL_DEVICE_NAME);

        ntStatus = IoGetDeviceObjectPointer( &uniLibraryName,
                                             FILE_ALL_ACCESS,
                                             &pLibraryFileObject,
                                             &pLibraryDeviceObject);

        if( !NT_SUCCESS( ntStatus))
        {
            AFSDbgLogMsg( AFS_SUBSYSTEM_LOAD_LIBRARY | AFS_SUBSYSTEM_INIT_PROCESSING,
                          AFS_TRACE_LEVEL_ERROR,
                          "AFSLoadLibrary IoGetDeviceObjectPointer failure %08lX\n",
                          ntStatus);

            try_return( ntStatus);
        }

        //
        // We have our reference to the library device object. Grab the
        // device extension and setup our callbacks
        //

        pLibDevExt = (AFSDeviceExt *)pLibraryDeviceObject->DeviceExtension;

        //
        // Save off our references
        //

        pDevExt->Specific.Control.LibraryFileObject = pLibraryFileObject;

        pDevExt->Specific.Control.LibraryDeviceObject = pLibraryDeviceObject;

        //
        // Reset the state for our library
        //

        AFSAcquireExcl( &pDevExt->Specific.Control.LibraryStateLock,
                        TRUE);

        SetFlag( pDevExt->Specific.Control.LibraryState, AFS_LIBRARY_LOADED);

        ClearFlag( pDevExt->Specific.Control.LibraryState, AFS_LIBRARY_QUEUE_CANCELLED);

        AFSDbgLogMsg( AFS_SUBSYSTEM_LOAD_LIBRARY | AFS_SUBSYSTEM_INIT_PROCESSING,
                      AFS_TRACE_LEVEL_VERBOSE,
                      "%s Completed load library, processing queued requests\n",
                      __FUNCTION__);

        AFSReleaseResource( &pDevExt->Specific.Control.LibraryStateLock);

        //
        // Process the queued requests
        //

        AFSProcessQueuedResults( FALSE);

try_exit:

        AFSDbgLogMsg( AFS_SUBSYSTEM_LOAD_LIBRARY | AFS_SUBSYSTEM_INIT_PROCESSING,
                      AFS_TRACE_LEVEL_VERBOSE,
                      "%s Library load complete Status %08lX\n",
                      __FUNCTION__,
                      ntStatus);

        if( !NT_SUCCESS( ntStatus))
        {

            if( pDevExt->Specific.Control.LibraryServicePath.Buffer != NULL)
            {

                ZwUnloadDriver( &pDevExt->Specific.Control.LibraryServicePath);

                ExFreePool( pDevExt->Specific.Control.LibraryServicePath.Buffer);

                pDevExt->Specific.Control.LibraryServicePath.Buffer = NULL;
                pDevExt->Specific.Control.LibraryServicePath.Length = 0;
                pDevExt->Specific.Control.LibraryServicePath.MaximumLength = 0;
            }
        }

        KeSetEvent( &pDevExt->Specific.Control.LoadLibraryEvent,
                    0,
                    FALSE);
    }

    return ntStatus;
}

NTSTATUS
AFSUnloadLibrary( IN BOOLEAN CancelQueue)
{

    NTSTATUS ntStatus = STATUS_SUCCESS;
    AFSDeviceExt       *pDevExt = (AFSDeviceExt *)AFSDeviceObject->DeviceExtension;
    LARGE_INTEGER       liTimeout;

    __Enter
    {

        //
        // Wait on the load library event so we don't race with any
        // other requests coming through
        //

        AFSDbgLogMsg( AFS_SUBSYSTEM_LOAD_LIBRARY,
                      AFS_TRACE_LEVEL_VERBOSE,
                      "%s Start unload library\n",
                      __FUNCTION__);

        ntStatus = KeWaitForSingleObject( &pDevExt->Specific.Control.LoadLibraryEvent,
                                          Executive,
                                          KernelMode,
                                          FALSE,
                                          NULL);

        if( !NT_SUCCESS( ntStatus))
        {
            try_return( ntStatus);
        }

        if( !BooleanFlagOn( pDevExt->Specific.Control.LibraryState, AFS_LIBRARY_LOADED))
        {
            try_return( ntStatus = STATUS_DEVICE_NOT_READY);
        }

        //
        // Clear all outstanding requests
        //

        AFSAcquireExcl( &pDevExt->Specific.Control.LibraryStateLock,
                        TRUE);

        ClearFlag( pDevExt->Specific.Control.LibraryState, AFS_LIBRARY_LOADED);

        if( CancelQueue)
        {
            SetFlag( pDevExt->Specific.Control.LibraryState, AFS_LIBRARY_QUEUE_CANCELLED);
        }

        //
        // We'll wait on the inflight event to be set, checking for the inflight
        // request count to reach zero
        //

        while( pDevExt->Specific.Control.InflightLibraryRequests > 0)
        {

            liTimeout.QuadPart = -(AFS_ONE_SECOND);

            //
            // If the count is non-zero make sure the event is cleared
            //

            KeClearEvent( &pDevExt->Specific.Control.InflightLibraryEvent);

            AFSReleaseResource( &pDevExt->Specific.Control.LibraryStateLock);

            ntStatus = KeWaitForSingleObject( &pDevExt->Specific.Control.InflightLibraryEvent,
                                              Executive,
                                              KernelMode,
                                              FALSE,
                                              &liTimeout);

            AFSAcquireExcl( &pDevExt->Specific.Control.LibraryStateLock,
                            TRUE);

            if( ntStatus != STATUS_TIMEOUT &&
                ntStatus != STATUS_SUCCESS)
            {

                AFSDbgLogMsg( AFS_SUBSYSTEM_LOAD_LIBRARY,
                              AFS_TRACE_LEVEL_VERBOSE,
                              "%s Failed request event Status %08lX\n",
                              __FUNCTION__,
                              ntStatus);

                SetFlag( pDevExt->Specific.Control.LibraryState, AFS_LIBRARY_LOADED);

                AFSReleaseResource( &pDevExt->Specific.Control.LibraryStateLock);

                AFSProcessQueuedResults( TRUE);

                try_return( ntStatus);
            }

            AFSDbgLogMsg( AFS_SUBSYSTEM_LOAD_LIBRARY,
                          AFS_TRACE_LEVEL_VERBOSE,
                          "%s Wait for inflight requests to complete %08lX\n",
                          __FUNCTION__,
                          pDevExt->Specific.Control.InflightLibraryRequests);
        }

        AFSReleaseResource( &pDevExt->Specific.Control.LibraryStateLock);

        AFSDbgLogMsg( AFS_SUBSYSTEM_LOAD_LIBRARY,
                      AFS_TRACE_LEVEL_VERBOSE,
                      "%s Processing queued results\n",
                      __FUNCTION__);

        AFSProcessQueuedResults( TRUE);

        //
        // Unload the current library implementation
        //

        if( pDevExt->Specific.Control.LibraryFileObject != NULL)
        {
            ObDereferenceObject( pDevExt->Specific.Control.LibraryFileObject);
        }

        pDevExt->Specific.Control.LibraryFileObject = NULL;

        pDevExt->Specific.Control.LibraryDeviceObject = NULL;

        ZwUnloadDriver( &pDevExt->Specific.Control.LibraryServicePath);

        ExFreePool( pDevExt->Specific.Control.LibraryServicePath.Buffer);

        pDevExt->Specific.Control.LibraryServicePath.Length = 0;

        pDevExt->Specific.Control.LibraryServicePath.MaximumLength = 0;

        pDevExt->Specific.Control.LibraryServicePath.Buffer = NULL;

try_exit:

        AFSDbgLogMsg( AFS_SUBSYSTEM_LOAD_LIBRARY,
                      AFS_TRACE_LEVEL_VERBOSE,
                      "%s Library unload complete Status %08lX\n",
                      __FUNCTION__,
                      ntStatus);

        KeSetEvent( &pDevExt->Specific.Control.LoadLibraryEvent,
                    0,
                    FALSE);
    }

    return ntStatus;
}

NTSTATUS
AFSCheckLibraryState( IN PIRP Irp)
{

    NTSTATUS ntStatus = STATUS_SUCCESS;
    AFSDeviceExt *pDevExt = (AFSDeviceExt *)AFSDeviceObject->DeviceExtension;
    AFSDeviceExt *pRDRDevExt = (AFSDeviceExt *)AFSRDRDeviceObject->DeviceExtension;
    PIO_STACK_LOCATION pIrpSp = IoGetCurrentIrpStackLocation( Irp);

    __Enter
    {

        AFSAcquireShared( &pDevExt->Specific.Control.LibraryStateLock,
                          TRUE);

        AFSDbgLogMsg( AFS_SUBSYSTEM_LOAD_LIBRARY,
                      AFS_TRACE_LEVEL_VERBOSE,
                      "%s Entry State %08lX Irp %p Function %08lX\n",
                      __FUNCTION__,
                      pRDRDevExt->DeviceFlags,
                      Irp,
                      pIrpSp->MajorFunction);

        if( BooleanFlagOn( pRDRDevExt->DeviceFlags, AFS_DEVICE_FLAG_REDIRECTOR_SHUTDOWN))
        {

            try_return( ntStatus = STATUS_DEVICE_NOT_READY);
        }

        if( !BooleanFlagOn( pDevExt->Specific.Control.LibraryState, AFS_LIBRARY_LOADED))
        {

            if( Irp != NULL)
            {

                AFSDbgLogMsg( AFS_SUBSYSTEM_LOAD_LIBRARY,
                              AFS_TRACE_LEVEL_VERBOSE,
                              "%s Queuing request %p\n",
                              __FUNCTION__,
                              Irp);

                ntStatus = AFSQueueLibraryRequest( Irp);

                AFSDbgLogMsg( AFS_SUBSYSTEM_LOAD_LIBRARY,
                              AFS_TRACE_LEVEL_VERBOSE,
                              "%s Queued request %p Status %08lX\n",
                              __FUNCTION__,
                              Irp,
                              ntStatus);
            }
            else
            {

                ntStatus = STATUS_TOO_LATE;

                AFSDbgLogMsg( AFS_SUBSYSTEM_LOAD_LIBRARY,
                              AFS_TRACE_LEVEL_VERBOSE,
                              "%s Failing request %p\n",
                              __FUNCTION__,
                              Irp);
            }

            try_return( ntStatus);
        }

        if( InterlockedIncrement( &pDevExt->Specific.Control.InflightLibraryRequests) == 1)
        {
            KeClearEvent( &pDevExt->Specific.Control.InflightLibraryEvent);
        }

try_exit:

        AFSDbgLogMsg( AFS_SUBSYSTEM_LOAD_LIBRARY,
                      AFS_TRACE_LEVEL_VERBOSE,
                      "%s Completed Irp %p Status %08lX Inflight Count %08lX\n",
                      __FUNCTION__,
                      Irp,
                      ntStatus,
                      pDevExt->Specific.Control.InflightLibraryRequests);

        AFSReleaseResource( &pDevExt->Specific.Control.LibraryStateLock);
    }

    return ntStatus;
}

NTSTATUS
AFSClearLibraryRequest()
{

    NTSTATUS ntStatus = STATUS_SUCCESS;
    AFSDeviceExt       *pDevExt = (AFSDeviceExt *)AFSDeviceObject->DeviceExtension;

    __Enter
    {
        AFSDbgLogMsg( AFS_SUBSYSTEM_LOAD_LIBRARY,
                      AFS_TRACE_LEVEL_VERBOSE,
                      "%s Inflight Count %08lX\n",
                      __FUNCTION__,
                      pDevExt->Specific.Control.InflightLibraryRequests);

        if( InterlockedDecrement( &pDevExt->Specific.Control.InflightLibraryRequests) == 0)
        {

            KeSetEvent( &pDevExt->Specific.Control.InflightLibraryEvent,
                        0,
                        FALSE);
        }

    }

    return ntStatus;
}

NTSTATUS
AFSQueueLibraryRequest( IN PIRP Irp)
{

    NTSTATUS ntStatus = STATUS_SUCCESS;
    AFSDeviceExt *pDevExt = (AFSDeviceExt *)AFSDeviceObject->DeviceExtension;
    AFSDeviceExt *pRDRDevExt = (AFSDeviceExt *)AFSRDRDeviceObject->DeviceExtension;
    AFSLibraryQueueRequestCB *pRequest = NULL;
    PIO_STACK_LOCATION pIrpSp = IoGetCurrentIrpStackLocation( Irp);

    __Enter
    {

        AFSAcquireExcl( &pDevExt->Specific.Control.LibraryQueueLock,
                        TRUE);

        AFSDbgLogMsg( AFS_SUBSYSTEM_LOAD_LIBRARY,
                      AFS_TRACE_LEVEL_VERBOSE,
                      "%s Entry for Irp %p Function %08lX\n",
                      __FUNCTION__,
                      Irp,
                      pIrpSp->MajorFunction);

        //
        // Has the load processing timed out and we are no longer
        // queuing requests?
        //

        if( BooleanFlagOn( pDevExt->Specific.Control.LibraryState, AFS_LIBRARY_QUEUE_CANCELLED) ||
            BooleanFlagOn( pRDRDevExt->DeviceFlags, AFS_DEVICE_FLAG_REDIRECTOR_SHUTDOWN))
        {

            AFSDbgLogMsg( AFS_SUBSYSTEM_LOAD_LIBRARY,
                          AFS_TRACE_LEVEL_ERROR,
                          "%s Library not loaded for Irp %p\n",
                          __FUNCTION__,
                          Irp);

            try_return( ntStatus = STATUS_DEVICE_NOT_READY);
        }

        pRequest = (AFSLibraryQueueRequestCB *)AFSExAllocatePoolWithTag( PagedPool,
                                                                         sizeof( AFSLibraryQueueRequestCB),
                                                                         AFS_LIBRARY_QUEUE_TAG);

        if( pRequest == NULL)
        {
            try_return( ntStatus = STATUS_INSUFFICIENT_RESOURCES);
        }

        RtlZeroMemory( pRequest,
                       sizeof( AFSLibraryQueueRequestCB));

        pRequest->Irp = Irp;

        if( pDevExt->Specific.Control.LibraryQueueHead == NULL)
        {
            pDevExt->Specific.Control.LibraryQueueHead = pRequest;
        }
        else
        {
            pDevExt->Specific.Control.LibraryQueueTail->fLink = pRequest;
        }

        pDevExt->Specific.Control.LibraryQueueTail = pRequest;

        IoMarkIrpPending( Irp);

        ntStatus = STATUS_PENDING;

try_exit:

        AFSDbgLogMsg( AFS_SUBSYSTEM_LOAD_LIBRARY,
                      AFS_TRACE_LEVEL_VERBOSE,
                      "%s Completed for Irp %p Status %08lX\n",
                      __FUNCTION__,
                      Irp,
                      ntStatus);

        AFSReleaseResource( &pDevExt->Specific.Control.LibraryQueueLock);
    }

    return ntStatus;
}

NTSTATUS
AFSProcessQueuedResults( IN BOOLEAN CancelRequest)
{

    NTSTATUS ntStatus = STATUS_SUCCESS;
    AFSLibraryQueueRequestCB *pRequest = NULL;
    AFSDeviceExt       *pDevExt = (AFSDeviceExt *)AFSDeviceObject->DeviceExtension;

    __Enter
    {

        AFSDbgLogMsg( AFS_SUBSYSTEM_LOAD_LIBRARY,
                      AFS_TRACE_LEVEL_VERBOSE,
                      "%s Entry\n",
                      __FUNCTION__);

        //
        // Loop through the queue either resubmitting requests or cancelling them
        //

        while( TRUE)
        {

            AFSAcquireExcl( &pDevExt->Specific.Control.LibraryQueueLock,
                            TRUE);

            if( pDevExt->Specific.Control.LibraryQueueHead == NULL)
            {

                AFSReleaseResource( &pDevExt->Specific.Control.LibraryQueueLock);

                break;
            }

            pRequest = pDevExt->Specific.Control.LibraryQueueHead;

            pDevExt->Specific.Control.LibraryQueueHead = pRequest->fLink;

            if( pDevExt->Specific.Control.LibraryQueueHead == NULL)
            {

                pDevExt->Specific.Control.LibraryQueueTail = NULL;
            }

            AFSReleaseResource( &pDevExt->Specific.Control.LibraryQueueLock);

            if( CancelRequest)
            {

                pRequest->Irp->IoStatus.Status = STATUS_CANCELLED;

                AFSDbgLogMsg( AFS_SUBSYSTEM_LOAD_LIBRARY,
                              AFS_TRACE_LEVEL_VERBOSE,
                              "%s Cancelling request Irp %p\n",
                              __FUNCTION__,
                              pRequest->Irp);

                IoCompleteRequest( pRequest->Irp,
                                   IO_NO_INCREMENT);
            }
            else
            {

                AFSDbgLogMsg( AFS_SUBSYSTEM_LOAD_LIBRARY,
                              AFS_TRACE_LEVEL_VERBOSE,
                              "%s Resubmitting request Irp %p\n",
                              __FUNCTION__,
                              pRequest->Irp);

                AFSSubmitLibraryRequest( pRequest->Irp);
            }

            ExFreePool( pRequest);
        }

        AFSDbgLogMsg( AFS_SUBSYSTEM_LOAD_LIBRARY,
                      AFS_TRACE_LEVEL_VERBOSE,
                      "%s Completed\n",
                      __FUNCTION__);
    }

    return ntStatus;
}

NTSTATUS
AFSSubmitLibraryRequest( IN PIRP Irp)
{

    NTSTATUS ntStatus = STATUS_SUCCESS;
    IO_STACK_LOCATION *pIrpSp = IoGetCurrentIrpStackLocation( Irp);

    __Enter
    {

        AFSDbgLogMsg( AFS_SUBSYSTEM_LOAD_LIBRARY,
                      AFS_TRACE_LEVEL_VERBOSE,
                      "%s Submitting Irp %p Function %08lX\n",
                      __FUNCTION__,
                      Irp,
                      pIrpSp->MajorFunction);

        switch( pIrpSp->MajorFunction)
        {

            case IRP_MJ_CREATE:
            {
                AFSCreate( AFSRDRDeviceObject,
                           Irp);
                break;
            }

            case IRP_MJ_CLOSE:
            {
                AFSClose( AFSRDRDeviceObject,
                          Irp);
                break;
            }

            case IRP_MJ_READ:
            {
                AFSRead( AFSRDRDeviceObject,
                         Irp);
                break;
            }

            case IRP_MJ_WRITE:
            {
                AFSWrite( AFSRDRDeviceObject,
                          Irp);
                break;
            }

            case IRP_MJ_QUERY_INFORMATION:
            {
                AFSQueryFileInfo( AFSRDRDeviceObject,
                                  Irp);
                break;
            }

            case IRP_MJ_SET_INFORMATION:
            {
                AFSSetFileInfo( AFSRDRDeviceObject,
                                Irp);
                break;
            }

            case IRP_MJ_QUERY_EA:
            {
                AFSQueryEA( AFSRDRDeviceObject,
                            Irp);
                break;
            }

            case IRP_MJ_SET_EA:
            {
                AFSSetEA( AFSRDRDeviceObject,
                          Irp);
                break;
            }

            case IRP_MJ_FLUSH_BUFFERS:
            {
                AFSFlushBuffers( AFSRDRDeviceObject,
                                 Irp);
                break;
            }

            case IRP_MJ_QUERY_VOLUME_INFORMATION:
            {
                AFSQueryVolumeInfo( AFSRDRDeviceObject,
                                    Irp);
                break;
            }

            case IRP_MJ_SET_VOLUME_INFORMATION:
            {
                AFSSetVolumeInfo( AFSRDRDeviceObject,
                                  Irp);
                break;
            }

            case IRP_MJ_DIRECTORY_CONTROL:
            {
                AFSDirControl( AFSRDRDeviceObject,
                               Irp);
                break;
            }

            case IRP_MJ_FILE_SYSTEM_CONTROL:
            {
                AFSFSControl( AFSRDRDeviceObject,
                              Irp);
                break;
            }

            case IRP_MJ_DEVICE_CONTROL:
            {
                AFSDevControl( AFSRDRDeviceObject,
                               Irp);
                break;
            }

            case IRP_MJ_INTERNAL_DEVICE_CONTROL:
            {
                AFSInternalDevControl( AFSRDRDeviceObject,
                                       Irp);
                break;
            }

            case IRP_MJ_SHUTDOWN:
            {
                AFSShutdown( AFSRDRDeviceObject,
                             Irp);
                break;
            }

            case IRP_MJ_LOCK_CONTROL:
            {
                AFSLockControl( AFSRDRDeviceObject,
                                Irp);
                break;
            }

            case IRP_MJ_CLEANUP:
            {
                AFSCleanup( AFSRDRDeviceObject,
                            Irp);
                break;
            }

            case IRP_MJ_QUERY_SECURITY:
            {
                AFSQuerySecurity( AFSRDRDeviceObject,
                                  Irp);
                break;
            }

            case IRP_MJ_SET_SECURITY:
            {
                AFSSetSecurity( AFSRDRDeviceObject,
                                Irp);
                break;
            }

            case IRP_MJ_SYSTEM_CONTROL:
            {
                AFSSystemControl( AFSRDRDeviceObject,
                                  Irp);
                break;
            }

            default:
            {
                AFSDefaultDispatch( AFSRDRDeviceObject,
                                    Irp);
                break;
            }
        }
    }

    return ntStatus;
}

NTSTATUS
AFSInitializeLibrary( IN AFSFileID *GlobalRootFid,
                      IN BOOLEAN QueueRootEnumeration)
{

    NTSTATUS ntStatus = STATUS_SUCCESS;
    AFSLibraryInitCB    stInitLib;
    AFSDeviceExt       *pDevExt = (AFSDeviceExt *)AFSDeviceObject->DeviceExtension;
    AFSDeviceExt       *pRDRDevExt = (AFSDeviceExt *)AFSRDRDeviceObject->DeviceExtension;

    __Enter
    {

        RtlZeroMemory( &stInitLib,
                       sizeof( AFSLibraryInitCB));

        //
        // Initialize the parameters to pass to the library
        //

        stInitLib.AFSControlDeviceObject = AFSDeviceObject;

        stInitLib.AFSRDRDeviceObject = AFSRDRDeviceObject;

        stInitLib.AFSServerName = AFSServerName;

        stInitLib.AFSDebugFlags = AFSDebugFlags;

        if( GlobalRootFid != NULL)
        {
            stInitLib.GlobalRootFid = *GlobalRootFid;
        }

        stInitLib.AFSCacheManagerCallbacks = &AFSCacheManagerCallbacks;

        stInitLib.AFSCacheBaseAddress = pRDRDevExt->Specific.RDR.CacheBaseAddress;

        stInitLib.AFSCacheLength = pRDRDevExt->Specific.RDR.CacheLength;

        //
        // Initialize the callback functions for the library
        //

        stInitLib.AFSProcessRequest = AFSProcessRequest;

        stInitLib.AFSDbgLogMsg = AFSDbgLogMsg;

        stInitLib.AFSAddConnectionEx = AFSAddConnectionEx;

        stInitLib.AFSExAllocatePoolWithTag = AFSExAllocatePoolWithTag;

        stInitLib.AFSExFreePool = AFSExFreePool;

        stInitLib.AFSDumpTraceFiles = AFSDumpTraceFiles;

        stInitLib.AFSRetrieveAuthGroup = AFSRetrieveAuthGroup;

        ntStatus = AFSSendDeviceIoControl( pDevExt->Specific.Control.LibraryDeviceObject,
                                           IOCTL_AFS_INITIALIZE_LIBRARY_DEVICE,
                                           &stInitLib,
                                           sizeof( AFSLibraryInitCB),
                                           NULL,
                                           0,
                                           NULL);

        if ( !NT_SUCCESS( ntStatus))
        {

            AFSDbgLogMsg( AFS_SUBSYSTEM_INIT_PROCESSING,
                          AFS_TRACE_LEVEL_ERROR,
                          "AFSInitializeLibrary AFSSendDeviceIoControl failure %08lX\n",
                          ntStatus);
        }
    }

    return ntStatus;
}
