/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

    LowIo.c

Abstract:

    This module implements buffer locking and mapping; also synchronous waiting for a lowlevelIO.

Author:

    JoeLinn     [JoeLinn]    12-Oct-94

Revision History:


--*/

#include "precomp.h"
#pragma hdrstop

//
//  Local debug trace level
//

#define Dbg                              (DEBUG_TRACE_LOWIO)

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, RxLockUserBuffer)
#pragma alloc_text(PAGE, RxNewMapUserBuffer)
#pragma alloc_text(PAGE, RxMapSystemBuffer)
#pragma alloc_text(PAGE, RxInitializeLowIoContext)
#pragma alloc_text(PAGE, RxLowIoGetBufferAddress)
#pragma alloc_text(PAGE, RxLowIoSubmitRETRY)
#pragma alloc_text(PAGE, RxLowIoCompletionTail)
#pragma alloc_text(PAGE, RxLowIoCompletion)
#pragma alloc_text(PAGE, RxLowIoPopulateFsctlInfo)
#pragma alloc_text(PAGE, RxLowIoSubmit)
#pragma alloc_text(PAGE, RxInitializeLowIoPerFcbInfo)
#pragma alloc_text(PAGE, RxInitializeLowIoPerFcbInfo)
#endif

//this is a crude implementation of the insertion, deletion, and coverup operations for wimp lowio
//we'll just use a linked list for now.........
#define RxInsertIntoOutStandingPagingOperationsList(RxContext,Operation) {     \
    PLIST_ENTRY WhichList = (Operation==LOWIO_OP_READ)                        \
                               ?&capFcb->Specific.Fcb.PagingIoReadsOutstanding  \
                               :&capFcb->Specific.Fcb.PagingIoWritesOutstanding;\
    InsertTailList(WhichList,&RxContext->RxContextSerializationQLinks);      \
}
#define RxRemoveFromOutStandingPagingOperationsList(RxContext) { \
    RemoveEntryList(&RxContext->RxContextSerializationQLinks);      \
    RxContext->RxContextSerializationQLinks.Flink = NULL;        \
    RxContext->RxContextSerializationQLinks.Blink = NULL;        \
}

#ifndef WIN9X

FAST_MUTEX RxLowIoPagingIoSyncMutex;

//here we hiding the IO access flags
#define RxLockAndMapUserBufferForLowIo(RXCONTEXT,LOWIOCONTEXT,OPERATION) {\
      RxLockUserBuffer( RXCONTEXT,                                            \
                        (OPERATION==LOWIO_OP_READ)?IoWriteAccess:IoReadAccess,\
                        LOWIOCONTEXT->ParamsFor.ReadWrite.ByteCount );        \
      if (RxNewMapUserBuffer(RXCONTEXT) == NULL) Status = STATUS_INSUFFICIENT_RESOURCES; \
      LOWIOCONTEXT->ParamsFor.ReadWrite.Buffer = RxUserBufferFromContext(RXCONTEXT); \
}




//these next macros are a bit strange. the macro parameter is the RxContext.....people who have
//    not defined appropriate capture macros can access out from there. on NT, we will just use the
//    captured entities but we hide the actual field designators

#define RxIsPagingIo(RXCONTEXT) (FlagOn(capReqPacket->Flags,IRP_PAGING_IO))

#define RxUserBufferFromContext(RXCONTEXT) (capReqPacket->MdlAddress)

#if 0
//if capFcb->NetRoot is NULL, then this must be the device FCB!
#define RxGetMiniRdrDispatchForLowIo(RXCONTEXT) {\
      if (capFcb->VNetRoot != NULL) {                                          \
         pMiniRdrDispatch = capFcb->MRxDispatch;                               \
      } else {                                                                 \
         PV_NET_ROOT pVirtualNetRoot = (PV_NET_ROOT)capFileObject->FsContext2; \
         pMiniRdrDispatch = pVirtualNetRoot->NetRoot->Dispatch;                \
      }                                                                        \
}
#endif 0
#define RxGetMiniRdrDispatchForLowIo(RXCONTEXT) {\
    pMiniRdrDispatch = RxContext->RxDeviceObject->Dispatch;    \
    }

#define RxMarkPendingForLowIo(RXCONTEXT) IoMarkIrpPending(capReqPacket)
#define RxUnmarkPendingForLowIo(RXCONTEXT) {\
    capPARAMS->Control &= ~SL_PENDING_RETURNED;  \
}


#define RxGetCurrentResourceThreadForLowIo(RXCONTEXT) \
    (ExGetCurrentResourceThread())


//NT specific routines

VOID
RxLockUserBuffer (
    IN PRX_CONTEXT RxContext,
    IN LOCK_OPERATION Operation,
    IN ULONG BufferLength
    )
/*++

Routine Description:

    This routine locks the specified buffer for the specified type of
    access.  The file system requires this routine since it does not
    ask the I/O system to lock its buffers for direct I/O.  This routine
    may only be called from the Fsd while still in the user context.

Arguments:

    RxContext - Pointer to the pointer Irp for which the buffer is to be locked.

    Operation - IoWriteAccess for read operations, or IoReadAccess for
                write operations.

    BufferLength - Length of user buffer.

Return Value:

    None

--*/
{
    RxCaptureRequestPacket;
    PMDL Mdl = NULL;

    PAGED_CODE();

    if (capReqPacket->MdlAddress == NULL) {

        ASSERT(!(capReqPacket->Flags & IRP_INPUT_OPERATION));

        // Allocate the Mdl, and Raise if we fail.
        if (BufferLength > 0) {
            Mdl = IoAllocateMdl(
                      capReqPacket->UserBuffer,
                      BufferLength,
                      FALSE,
                      FALSE,
                      capReqPacket );

            if (Mdl == NULL) {
                RxRaiseStatus(
                    RxContext,
                    STATUS_INSUFFICIENT_RESOURCES );
            } else {
                // Now probe the buffer described by the Irp.  If we get an exception,
                // deallocate the Mdl and return the appropriate "expected" status.

                try {
                    MmProbeAndLockPages(
                        Mdl,
                        capReqPacket->RequestorMode,
                        Operation );
                } except(EXCEPTION_EXECUTE_HANDLER) {
                    NTSTATUS Status;

                    Status = GetExceptionCode();

                    IoFreeMdl( Mdl );
                    capReqPacket->MdlAddress = NULL;

                    SetFlag(
                        RxContext->Flags,
                        RX_CONTEXT_FLAG_NO_EXCEPTION_BREAKPOINT);

                    RxRaiseStatus(
                        RxContext,
                        (FsRtlIsNtstatusExpected(Status) ?
                            Status :
                            STATUS_INVALID_USER_BUFFER));
                }
            }
        }
    } else {
        Mdl = capReqPacket->MdlAddress;
        ASSERT(RxLowIoIsMdlLocked(Mdl));
    }
}


NTSTATUS
RxLockBuffer (
    IN PRX_CONTEXT    RxContext,
    IN PVOID          pBuffer,
    IN ULONG          BufferLength,
    IN LOCK_OPERATION Operation,
    OUT PMDL          *pBufferMdlPtr
    )
/*++

Routine Description:

    This routine locks the input buffer for the specified type of
    access.  The file system requires this routine since it does not
    ask the I/O system to lock its buffers for direct I/O.

Arguments:

    RxContext - Pointer to the pointer Irp for which the buffer is to be locked.

    Operation - IoWriteAccess for read operations, or IoReadAccess for
                write operations.

    pBufferMdl - a placeholder for a pointer to the locked down MDL

Return Value:

    RxStatus(SUCCESS) - if the operation was successful

--*/
{
    NTSTATUS Status = STATUS_SUCCESS;

    RxCaptureRequestPacket;

    *pBufferMdlPtr = NULL;

    // Allocate the Mdl, and Raise if we fail.
    *pBufferMdlPtr = IoAllocateMdl(
                         pBuffer,
                         BufferLength,
                         FALSE,
                         FALSE,
                         capReqPacket);

    if (*pBufferMdlPtr == NULL) {
        Status = STATUS_INSUFFICIENT_RESOURCES;
    } else {
        // probe and lock down the buffer
        try {
            MmProbeAndLockPages(
                *pBufferMdlPtr,
                capReqPacket->RequestorMode,
                Operation );

            RxProtectMdlFromFree(*pBufferMdlPtr);
        } except(EXCEPTION_EXECUTE_HANDLER) {
            NTSTATUS Status;

            Status = GetExceptionCode();

            IoFreeMdl(*pBufferMdlPtr);

            Status = FsRtlIsNtstatusExpected(Status) ?
                     Status :
                     STATUS_INVALID_USER_BUFFER;
        }
    }

    return Status;
}

PVOID
RxMapSystemBuffer (
    IN PRX_CONTEXT RxContext
    )
/*++

Routine Description:

    This routine returns the system buffer address from the irp. the way that the code is written
    it may also decide to get the buffer address from the mdl. that is wrong because the systembuffer is
    always nonpaged so no locking/mapping is needed. thus, the mdl path now contains an assert.

Arguments:

    RxContext - Pointer to the IrpC for the request.

Return Value:

    Mapped address

--*/
{
    RxCaptureRequestPacket;

    PAGED_CODE();

    if (capReqPacket->MdlAddress == NULL) {
       return capReqPacket->AssociatedIrp.SystemBuffer;
    } else {
       ASSERT (!"there should not be an MDL in this irp!!!!!");
       return MmGetSystemAddressForMdlSafe(
                  capReqPacket->MdlAddress, NormalPagePriority );
    }
}

PVOID
RxNewMapUserBuffer (
    IN PRX_CONTEXT RxContext
    )
/*++

Routine Description:

    This routine returns the address of the userbuffer. if an MDL exists then the assumption is that
    the mdl describes the userbuffer and the system address for the mdl is returned. otherwise, the userbuffer
    is returned directly.

Arguments:

    RxContext - Pointer to the IrpC for the request.

Return Value:

    Mapped address

--*/
{
    RxCaptureRequestPacket;

    PAGED_CODE();

    if (capReqPacket->MdlAddress == NULL) {
       return capReqPacket->UserBuffer;
    } else {
       return MmGetSystemAddressForMdlSafe(
                  capReqPacket->MdlAddress,
                  NormalPagePriority );
    }
}
#else

#define RxMarkPendingForLowIo(RXCONTEXT)
#define RxUnmarkPendingForLowIo(RXCONTEXT)

//CIA figure out how paging io works on win9x

#define RxIsPagingIo(RXCONTEXT)     FALSE
#define RxGetCurrentResourceThreadForLowIo(RXCONTEXT) 11

#define RxLockAndMapUserBufferForLowIo(RXCONTEXT,LOWIOCONTEXT,OPERATION) {\
      if(((LOWIOCONTEXT)->ParamsFor.ReadWrite.Buffer = \
         RxAllocateMdl(capReqPacket->ir_data, capReqPacket->ir_length)) \
            != NULL) {\
              NTSTATUS __Status = _RxProbeAndLockPages((LOWIOCONTEXT)->ParamsFor.ReadWrite.Buffer, \
                        KernelMode,\
                        (OPERATION==LOWIO_OP_READ)?IoWriteAccess:IoReadAccess\
                        );    \
            if (__Status != STATUS_SUCCESS) {                               \
                IoFreeMdl((LOWIOCONTEXT)->ParamsFor.ReadWrite.Buffer);      \
                (LOWIOCONTEXT)->ParamsFor.ReadWrite.Buffer = NULL;          \
            }                                                               \
      }\
}

#define RxGetMiniRdrDispatchForLowIo(RXCONTEXT) {\
         PV_NET_ROOT pVirtualNetRoot = (PV_NET_ROOT)capReqPacket->ir_rh;       \
         pMiniRdrDispatch = pVirtualNetRoot->NetRoot->Dispatch;                \
}


#endif //#ifndef WIN9X

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//  from here down (except for fsctl buffer determination), everything is available for either wrapper. we may
//  decide that the fsctl stuff should be moved as well
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

VOID
RxInitializeLowIoContext(
    PLOWIO_CONTEXT LowIoContext,
    ULONG Operation
    )
/*++

Routine Description:

    This routine initializes the LowIO context in the RxContext.

Arguments:

    RxContext - context of irp being processed.

Return Value:

    none

--*/
{
    PRX_CONTEXT RxContext = CONTAINING_RECORD(LowIoContext,RX_CONTEXT,LowIoContext);
    RxCaptureRequestPacket; RxCaptureParamBlock;

    PAGED_CODE();

    ASSERT (LowIoContext = &RxContext->LowIoContext);

    KeInitializeEvent(
        &RxContext->SyncEvent,
        NotificationEvent,
        FALSE );

    //this ID is used to release the resource on behalf of another thread....
    // e.g. it is used when an async routine completes to release the thread
    //      acquired by the first acquirer.
    LowIoContext->ResourceThreadId = RxGetCurrentResourceThreadForLowIo(RxContext);

    LowIoContext->Operation = (USHORT)Operation;

    switch (Operation) {
    case LOWIO_OP_READ:
    case LOWIO_OP_WRITE:
        IF_DEBUG {
            LowIoContext->ParamsFor.ReadWrite.ByteOffset = 0xffffffee; //no operation should start there!
            LowIoContext->ParamsFor.ReadWrite.ByteCount  = 0xeeeeeeee; //no operation should start there!
        }
        ASSERT (&capPARAMS->Parameters.Read.Length == &capPARAMS->Parameters.Write.Length);
        ASSERT (&capPARAMS->Parameters.Read.Key == &capPARAMS->Parameters.Write.Key);
        LowIoContext->ParamsFor.ReadWrite.Key  = capPARAMS->Parameters.Read.Key;
        LowIoContext->ParamsFor.ReadWrite.Flags  = 0
                 | (RxIsPagingIo(RxContext)?LOWIO_READWRITEFLAG_PAGING_IO:0)
                 ;
        break;

    case LOWIO_OP_FSCTL:
    case LOWIO_OP_IOCTL:
        LowIoContext->ParamsFor.FsCtl.Flags              = 0;
        LowIoContext->ParamsFor.FsCtl.InputBufferLength  = 0;
        LowIoContext->ParamsFor.FsCtl.pInputBuffer       = NULL;
        LowIoContext->ParamsFor.FsCtl.OutputBufferLength = 0;
        LowIoContext->ParamsFor.FsCtl.pOutputBuffer      = NULL;
        LowIoContext->ParamsFor.FsCtl.MinorFunction      = 0;
        break;

    case LOWIO_OP_SHAREDLOCK:
    case LOWIO_OP_EXCLUSIVELOCK:
    case LOWIO_OP_UNLOCK:
    case LOWIO_OP_UNLOCK_MULTIPLE:
    case LOWIO_OP_CLEAROUT:
    case LOWIO_OP_NOTIFY_CHANGE_DIRECTORY:
        break;
    default:
        ASSERT(FALSE);
    }
}

PVOID
RxLowIoGetBufferAddress (
    IN PRX_CONTEXT RxContext
    )
/*++

Routine Description:

    This routine gets the buffer corresponding to the Mdl in the LowIoContext.

Arguments:

    RxContext - context for the request.

Return Value:

    Mapped address

--*/
{
    PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;

    PAGED_CODE();

    if (LowIoContext->ParamsFor.ReadWrite.ByteCount > 0) {
        ASSERT(LowIoContext->ParamsFor.ReadWrite.Buffer);
        return MmGetSystemAddressForMdlSafe(
                   LowIoContext->ParamsFor.ReadWrite.Buffer,
                   NormalPagePriority);
    } else {
        return NULL;
    }
}

NTSTATUS
RxLowIoSubmitRETRY (
    IN PRX_CONTEXT RxContext
    )
/*++

Routine Description:

    This routine just calls LowIoSubmit; the completion routine was previously
    stored so we just extract it and pass it in. This is called out of the Fsp
    dispatcher for retrying at the low level.


Arguments:

    RxContext - the usual

Return Value:

    whatever value supplied by the caller or RxStatus(MORE_PROCESSING_REQUIRED).

--*/
{
    PAGED_CODE();

    return(RxLowIoSubmit(RxContext,RxContext->LowIoContext.CompletionRoutine));
}

NTSTATUS
RxLowIoCompletionTail (
    IN PRX_CONTEXT RxContext
    )
/*++

Routine Description:

    This routine is called by lowio routines at the very end...i.e. after the individual completion
    routines are called.


Arguments:

    RxContext - the RDBSS context

Return Value:

    whatever value supplied by the caller.

--*/
{
    NTSTATUS Status;
    PLOWIO_CONTEXT LowIoContext  = &RxContext->LowIoContext;
    ULONG          Operation     = LowIoContext->Operation;
    BOOLEAN  SynchronousIo = !BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION);

    PAGED_CODE();

    RxDbgTrace(+1, Dbg, ("RxLowIoCompletionTail, Operation=%08lx\n",LowIoContext->Operation));

    if (( KeGetCurrentIrql() < DISPATCH_LEVEL )
          || (FlagOn(LowIoContext->Flags,LOWIO_CONTEXT_FLAG_CAN_COMPLETE_AT_DPC_LEVEL))  ) {
        Status = RxContext->LowIoContext.CompletionRoutine(RxContext);
    } else {
        Status = STATUS_MORE_PROCESSING_REQUIRED;
    }

    if ( (Status == STATUS_MORE_PROCESSING_REQUIRED) || (Status == STATUS_RETRY) ) {
        RxDbgTrace(-1, Dbg, ("RxLowIoCompletionTail wierdstatus, Status=%08lx\n",Status));
        return(Status);
    }

    switch (Operation) {
    case LOWIO_OP_READ:
    case LOWIO_OP_WRITE:
        if (FlagOn(LowIoContext->ParamsFor.ReadWrite.Flags,LOWIO_READWRITEFLAG_PAGING_IO)) {
            RxDbgTrace(0, Dbg, ("RxLowIoCompletionTail pagingio unblock\n"));

            ExAcquireFastMutexUnsafe(&RxLowIoPagingIoSyncMutex);
            RxRemoveFromOutStandingPagingOperationsList(RxContext);
            ExReleaseFastMutexUnsafe(&RxLowIoPagingIoSyncMutex);

            RxResumeBlockedOperations_ALL(RxContext);
        }
        break;

    case LOWIO_OP_SHAREDLOCK:
    case LOWIO_OP_EXCLUSIVELOCK:
    case LOWIO_OP_UNLOCK:
    case LOWIO_OP_UNLOCK_MULTIPLE:
    case LOWIO_OP_CLEAROUT:
        break;

    case LOWIO_OP_FSCTL:
    case LOWIO_OP_IOCTL:
    case LOWIO_OP_NOTIFY_CHANGE_DIRECTORY:
        break;

    default:
        ASSERT(!"Valid Low Io Op Code");
    }

    if (!FlagOn(LowIoContext->Flags,LOWIO_CONTEXT_FLAG_SYNCCALL)){

        //if we're being called from lowiosubmit then just get out otherwise...do the completion

        RxCompleteAsynchronousRequest( RxContext, Status );
    }

    RxDbgTrace(-1, Dbg, ("RxLowIoCompletionTail, Status=%08lx\n",Status));
    return(Status);
}

NTSTATUS
RxLowIoCompletion (
    IN PRX_CONTEXT RxContext
    )
/*++

Routine Description:

    This routine must be called by the minirdr lowio routines when they
    complete IF THEY HAVE INITIALLY RETURNED PENDING.

    It behaves a bit differently depending on whether it's sync or
    async IO. for sync, we just get back into the user's thread. for async,
    we first try the completion routine directly....if we get MORE_PROCESSING...
    then we flip to a thread and the routine will be recalled.


Arguments:

    RxContext - the RDBSS context

Return Value:

    whatever value supplied by the caller or RxStatus(MORE_PROCESSING_REQUIRED). the value M_P_R is very handy
    if this is being called for a Irp completion; M_P_R causes the Irp completion guy to stop processing....which
    is good since the called completion routine may complete the packet!

--*/
{
    RxCaptureParamBlock;

#ifndef WIN9X
    NTSTATUS Status;
#endif
    BOOLEAN  SynchronousIo = !BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION);

    PAGED_CODE();

    if (SynchronousIo) {
        RxSignalSynchronousWaiter(RxContext);
        return(STATUS_MORE_PROCESSING_REQUIRED);
    }

#ifdef WIN9X
    RxDbgTrace(0, Dbg, ("We SHOULD NEVER GET HERE\n"));
    ASSERT(FALSE);
#else
    RxDbgTrace(0, Dbg, ("RxLowIoCompletion ASYNC\n"));
    ASSERT (RxLowIoIsBufferLocked(&RxContext->LowIoContext));

    Status = RxLowIoCompletionTail(RxContext);

    //the called routine makes the decision as to whether it can continue.....many will ask for
    //a post if we're at DPC level.....some will not.
    if (Status == STATUS_MORE_PROCESSING_REQUIRED) {
        RxFsdPostRequestWithResume(RxContext,RxLowIoCompletion);
        return(STATUS_MORE_PROCESSING_REQUIRED);
    }

    //i'm not too sure about this
    if (Status == STATUS_RETRY) {
        RxFsdPostRequestWithResume(RxContext,RxLowIoSubmitRETRY);
        return(STATUS_MORE_PROCESSING_REQUIRED);
    }

    return(Status);
#endif
}
#if DBG
VOID
RxAssertFsctlIsLikeIoctl ()
{
    ASSERT(FIELD_OFFSET(IO_STACK_LOCATION,Parameters.FileSystemControl.OutputBufferLength)
            == FIELD_OFFSET(IO_STACK_LOCATION,Parameters.DeviceIoControl.OutputBufferLength) );
    ASSERT(FIELD_OFFSET(IO_STACK_LOCATION,Parameters.FileSystemControl.InputBufferLength)
            == FIELD_OFFSET(IO_STACK_LOCATION,Parameters.DeviceIoControl.InputBufferLength) );
    ASSERT(FIELD_OFFSET(IO_STACK_LOCATION,Parameters.FileSystemControl.FsControlCode)
            == FIELD_OFFSET(IO_STACK_LOCATION,Parameters.DeviceIoControl.IoControlCode) );
    ASSERT(FIELD_OFFSET(IO_STACK_LOCATION,Parameters.FileSystemControl.Type3InputBuffer)
            == FIELD_OFFSET(IO_STACK_LOCATION,Parameters.DeviceIoControl.Type3InputBuffer) );
}
#else
#define RxAssertFsctlIsLikeIoctl()
#endif //if DBG


NTSTATUS
NTAPI
RxLowIoPopulateFsctlInfo (
    IN PRX_CONTEXT RxContext
    )
{
    NTSTATUS Status = STATUS_SUCCESS;
    RxCaptureRequestPacket;
    RxCaptureParamBlock;
    PLOWIO_CONTEXT LowIoContext  = &RxContext->LowIoContext;

    PAGED_CODE();

    RxAssertFsctlIsLikeIoctl();

    LowIoContext->ParamsFor.FsCtl.FsControlCode =
             capPARAMS->Parameters.FileSystemControl.FsControlCode;

    LowIoContext->ParamsFor.FsCtl.InputBufferLength =
             capPARAMS->Parameters.FileSystemControl.InputBufferLength;

    LowIoContext->ParamsFor.FsCtl.OutputBufferLength =
             capPARAMS->Parameters.FileSystemControl.OutputBufferLength;


    LowIoContext->ParamsFor.FsCtl.MinorFunction = capPARAMS->MinorFunction;

    switch (LowIoContext->ParamsFor.FsCtl.FsControlCode & 3) {
    case METHOD_BUFFERED:
        {
           LowIoContext->ParamsFor.FsCtl.pInputBuffer  = capReqPacket->AssociatedIrp.SystemBuffer;
           LowIoContext->ParamsFor.FsCtl.pOutputBuffer = capReqPacket->AssociatedIrp.SystemBuffer;
        }
        break;

    case METHOD_IN_DIRECT:
    case METHOD_OUT_DIRECT:
        {
           LowIoContext->ParamsFor.FsCtl.pInputBuffer  = capReqPacket->AssociatedIrp.SystemBuffer;
           if (capReqPacket->MdlAddress!=NULL) {
               LowIoContext->ParamsFor.FsCtl.pOutputBuffer =
                   MmGetSystemAddressForMdlSafe(
                       capReqPacket->MdlAddress,
                       NormalPagePriority);

               if (LowIoContext->ParamsFor.FsCtl.pOutputBuffer == NULL) {
                   Status = STATUS_INSUFFICIENT_RESOURCES;
               }
           } else {
               LowIoContext->ParamsFor.FsCtl.pOutputBuffer = NULL;
           }
        }
        break;

    case METHOD_NEITHER:
        {
           LowIoContext->ParamsFor.FsCtl.pInputBuffer  = capPARAMS->Parameters.FileSystemControl.Type3InputBuffer;
           LowIoContext->ParamsFor.FsCtl.pOutputBuffer = capReqPacket->UserBuffer;
        }
        break;

    default:
        ASSERT(!"Valid Method for Fs Control");
        break;
    }

    return Status;
}

NTSTATUS
RxLowIoSubmit (
    IN PRX_CONTEXT RxContext,
    PLOWIO_COMPLETION_ROUTINE CompletionRoutine
    )
/*++

Routine Description:

    This routine passes the request to the minirdr after setting up for completion. it then waits
    or pends as appropriate.

Arguments:

    RxContext - the usual

Return Value:

    whatever value is returned by a callout....or by LowIoCompletion.

--*/
{
    RxCaptureRequestPacket;
    RxCaptureFcb;
    RxCaptureFobx;
    RxCaptureParamBlock;
    RxCaptureFileObject;

    NTSTATUS       Status        = STATUS_SUCCESS;
    PLOWIO_CONTEXT LowIoContext  = &RxContext->LowIoContext;
    ULONG          Operation     = LowIoContext->Operation;
    BOOLEAN        SynchronousIo = !BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION);

    PAGED_CODE();

    LowIoContext->CompletionRoutine = CompletionRoutine;

    RxDbgTrace(+1, Dbg, ("RxLowIoSubmit, Operation=%08lx\n",LowIoContext->Operation));

    switch (Operation) {
    case LOWIO_OP_READ:
    case LOWIO_OP_WRITE:
        ASSERT(LowIoContext->ParamsFor.ReadWrite.ByteOffset != 0xffffffee);
        ASSERT(LowIoContext->ParamsFor.ReadWrite.ByteCount  != 0xeeeeeeee);
        RxLockAndMapUserBufferForLowIo(RxContext,LowIoContext,Operation);
    #ifndef WIN9X
    // NT paging IO is different from WIN9X so this may be different
        if (FlagOn(LowIoContext->ParamsFor.ReadWrite.Flags,LOWIO_READWRITEFLAG_PAGING_IO)) {
            ExAcquireFastMutexUnsafe(&RxLowIoPagingIoSyncMutex);
            RxContext->BlockedOpsMutex = &RxLowIoPagingIoSyncMutex;
            RxInsertIntoOutStandingPagingOperationsList(RxContext,Operation);
            ExReleaseFastMutexUnsafe(&RxLowIoPagingIoSyncMutex);
        }
    #else
        if (!LowIoContext->ParamsFor.ReadWrite.Buffer) {
            //
            // Couldn't get mdl for data!
            //

            return(STATUS_INSUFFICIENT_RESOURCES);
        }
    #endif //ifndef WIN9X
            break;

    //can't do much to make this OS independent
    case LOWIO_OP_FSCTL:
    case LOWIO_OP_IOCTL:

        Status = RxLowIoPopulateFsctlInfo(RxContext);

        if (Status == STATUS_SUCCESS) {
            if ((LowIoContext->ParamsFor.FsCtl.InputBufferLength > 0) &&
                (LowIoContext->ParamsFor.FsCtl.pInputBuffer == NULL)) {
                Status = STATUS_INVALID_PARAMETER;
            }
    
            if ((LowIoContext->ParamsFor.FsCtl.OutputBufferLength > 0) &&
                (LowIoContext->ParamsFor.FsCtl.pOutputBuffer == NULL)) {
                Status = STATUS_INVALID_PARAMETER;
            }
        }

        break;

    case LOWIO_OP_NOTIFY_CHANGE_DIRECTORY:
    case LOWIO_OP_SHAREDLOCK:
    case LOWIO_OP_EXCLUSIVELOCK:
    case LOWIO_OP_UNLOCK:
    case LOWIO_OP_UNLOCK_MULTIPLE:
    case LOWIO_OP_CLEAROUT:
        break;

    default:
        ASSERT(!"Valid Low Io Op Code");
        Status = STATUS_INVALID_PARAMETER;
    }

    SetFlag(RxContext->Flags,RX_CONTEXT_FLAG_NO_PREPOSTING_NEEDED);

    if (Status==STATUS_SUCCESS) {
        PMINIRDR_DISPATCH pMiniRdrDispatch;

        if (!SynchronousIo) {
            //get ready for any arbitrary finish order...assume return of pending
            InterlockedIncrement(&RxContext->ReferenceCount);

            if (!FlagOn(RxContext->Flags, RX_CONTEXT_FLAG_IN_FSP)) {
                RxMarkPendingForLowIo(RxContext);
            }

            RxDbgTrace( 0, Dbg, ("RxLowIoSubmit, Operation is ASYNC!\n"));
        }

        pMiniRdrDispatch = NULL;
        RxGetMiniRdrDispatchForLowIo(RxContext);

        if (pMiniRdrDispatch != NULL) {
            do {
                RxContext->InformationToReturn = 0;
                MINIRDR_CALL(
                    Status,
                    RxContext,
                    pMiniRdrDispatch,
                    MRxLowIOSubmit[LowIoContext->Operation],
                    (RxContext));

                if (Status == STATUS_PENDING){
                    if (!SynchronousIo) {
                        goto FINALLY;
                    }
                    RxWaitSync(RxContext);
                    Status = RxContext->StoredStatus;
                } else {
                    if (!SynchronousIo && Status != STATUS_RETRY) {
                        //we were wrong about pending..so clear the bit and deref
                        if (!FlagOn(RxContext->Flags, RX_CONTEXT_FLAG_IN_FSP)) {
                            RxUnmarkPendingForLowIo(RxContext);
                        }

                        InterlockedDecrement(&RxContext->ReferenceCount);
                    }
                }
            } while (Status == STATUS_RETRY);
        } else {
            Status = STATUS_INVALID_PARAMETER;
        }
    }

    //you do not come here for pended,async IO
    RxContext->StoredStatus = Status;
    SetFlag(LowIoContext->Flags,LOWIO_CONTEXT_FLAG_SYNCCALL);
    Status = RxLowIoCompletionTail(RxContext);

FINALLY:
    RxDbgTrace(-1, Dbg, ("RxLowIoSubmit, Status=%08lx\n",Status));
    return(Status);
}


VOID
RxInitializeLowIoPerFcbInfo(
    PLOWIO_PER_FCB_INFO LowIoPerFcbInfo
    )
/*++

Routine Description:

    This routine is called in FcbInitialization to initialize the LowIo part of the structure.



Arguments:

    LowIoPerFcbInfo - the struct to be initialized

Return Value:


--*/
{
    PAGED_CODE();

    InitializeListHead(&LowIoPerFcbInfo->PagingIoReadsOutstanding);
    InitializeListHead(&LowIoPerFcbInfo->PagingIoWritesOutstanding);
}

