/*++ BUILD Version: 0009    // Increment this if a change has global effects
Copyright (c) 1987-1993  Microsoft Corporation

Module Name:

    transact.c

Abstract:

    This file conatins the implementation of the transact exchange.

Author:

    Balan Sethu Raman (SethuR) 06-Feb-95    Created

Revision:

    Joe Linn (JoeLi)    -- Revise multiple packet implementation

--*/

#include "precomp.h"
#pragma hdrstop

#include "align.h"
#pragma warning(error:4100)   // Unreferenced formal parameter

#ifdef  ALLOC_PRAGMA
#pragma alloc_text(PAGE, SmbCeInitializeTransactionParameters)
#pragma alloc_text(PAGE, SmbCeUninitializeTransactionParameters)
#pragma alloc_text(PAGE, SmbCeDiscardTransactExchange)
#pragma alloc_text(PAGE, SmbCeSubmitTransactionRequest)
#pragma alloc_text(PAGE, _SmbCeTransact)
#pragma alloc_text(PAGE, SmbTransactBuildHeader)
#pragma alloc_text(PAGE, SmbTransactExchangeStart)
#pragma alloc_text(PAGE, SmbTransactExchangeAbort)
#pragma alloc_text(PAGE, SmbTransactExchangeErrorHandler)
#pragma alloc_text(PAGE, SmbTransactExchangeSendCallbackHandler)
#pragma alloc_text(PAGE, SmbCeInitializeTransactExchange)
#pragma alloc_text(PAGE, SendSecondaryRequests)
#endif

//#define SET_DONTSUBSUME_PARAMS
#ifdef SET_DONTSUBSUME_PARAMS
ULONG MRxSmbDontSubsumeParams = 1;
#else
ULONG MRxSmbDontSubsumeParams = 0;
#endif
#if DBG
#define DONTSUBSUME_PARAMS MRxSmbDontSubsumeParams
#else
#define DONTSUBSUME_PARAMS FALSE
#endif

SMB_TRANSACTION_OPTIONS RxDefaultTransactionOptions = DEFAULT_TRANSACTION_OPTIONS;

RXDT_DefineCategory(TRANSACT);
#define Dbg        (DEBUG_TRACE_TRANSACT)

#define MIN(x,y)  ((x) < (y) ? (x) : (y))

#define SMB_TRANSACT_MAXIMUM_PARAMETER_SIZE (0xffff)
#define SMB_TRANSACT_MAXIMUM_DATA_SIZE      (0xffff)

typedef struct _SMB_TRANSACT_RESP_FORMAT_DESCRIPTION {
    ULONG WordCount;
    ULONG TotalParameterCount;
    ULONG TotalDataCount;
    ULONG ParameterCount;
    ULONG ParameterOffset;
    ULONG ParameterDisplacement;
    ULONG DataCount;
    ULONG DataOffset;
    ULONG DataDisplacement;
    ULONG ByteCount;
    ULONG ApparentMsgLength;
} SMB_TRANSACT_RESP_FORMAT_DESCRIPTION, *PSMB_TRANSACT_RESP_FORMAT_DESCRIPTION;

NTSTATUS
SmbTransactAccrueAndValidateFormatData(
    IN struct _SMB_TRANSACT_EXCHANGE *pTransactExchange,    // The exchange instance
    IN  PSMB_HEADER pSmbHeader,
    IN  ULONG        BytesIndicated,
    OUT PSMB_TRANSACT_RESP_FORMAT_DESCRIPTION Format
    );

extern NTSTATUS
SmbTransactExchangeFinalize(
    PSMB_EXCHANGE pExchange,
    BOOLEAN       *pPostFinalize);

extern NTSTATUS
ParseTransactResponse(
    IN struct _SMB_TRANSACT_EXCHANGE *pTransactExchange,    // The exchange instance
    IN PSMB_TRANSACT_RESP_FORMAT_DESCRIPTION Format,
    IN ULONG        BytesIndicated,
    IN ULONG        BytesAvailable,
    OUT ULONG       *pBytesTaken,
    IN  PSMB_HEADER pSmbHeader,
    OUT PMDL        *pCopyRequestMdlPointer,
    OUT PULONG      pCopyRequestSize);


extern NTSTATUS
SendSecondaryRequests(PVOID pContext);

extern NTSTATUS
SmbCeInitializeTransactExchange(
    PSMB_TRANSACT_EXCHANGE              pTransactExchange,
    PRX_CONTEXT                         RxContext,
    PSMB_TRANSACTION_OPTIONS            pOptions,
    PSMB_TRANSACTION_SEND_PARAMETERS    pSendParameters,
    PSMB_TRANSACTION_RECEIVE_PARAMETERS pReceiveParameters,
    PSMB_TRANSACTION_RESUMPTION_CONTEXT pResumptionContext);

NTSTATUS
SmbCeInitializeTransactionParameters(
   PVOID  pSetup,
   USHORT SetupLength,
   PVOID  pParam,
   ULONG  ParamLength,
   PVOID  pData,
   ULONG  DataLength,
   PSMB_TRANSACTION_PARAMETERS pTransactionParameters
)
/*++

Routine Description:

    This routine initializes the transaction parameters

Arguments:

    pSetup             - the setup buffer

    SetupLength        - the setup buffer length

    pParam             - the param buffer

    ParamLength        - the param buffer length

    pData              - the data buffer

    DataLength         - the data buffer length

    pTransactionParameters - the transaction parameters instance

Return Value:

    RXSTATUS - The return status for the operation

Notes:

    The TRANSACTION parameters come in two flavours -- the send parameters for the data
    that is to be sent to the server and the receive parameters for receiving the data
    from the server. There is one subtle difference in the way in which the parameters are
    stored and referenced in these two cases. In the send case the Setup buffer is stored
    as a pointer itself while in the receive case it is stored in the form of a MDL.

    This is because the SMB protocol requires that the Header + setup information for a
    transaction request cannot be greated then the maximum SMB buffer size, i.e., setup
    information cannot spill to a secondary request. The buffer that is allocated for the
    header is made sufficiently large enough to hold the setup data as well. On the other
    hand the receives are handled in a two phase manner, -- the indication at the DPC
    level followed by a copy data request if required. In order to avoid having to transition
    between DPC level and a worker thread the MDL's for the buffers are eagerly evaluated.

--*/
{
    NTSTATUS Status = STATUS_SUCCESS;
    PMDL     pSetupMdl = NULL;
    PMDL     pParamMdl = NULL;
    PMDL     pDataMdl  = NULL;

    PAGED_CODE();

    if (pTransactionParameters->Flags & TRANSACTION_RECEIVE_PARAMETERS_FLAG) {
        if ((pSetup != NULL) && (SetupLength > 0)) {
            pSetupMdl = RxAllocateMdl(pSetup,SetupLength);
            if (pSetupMdl == NULL) {
                Status = STATUS_INSUFFICIENT_RESOURCES;
            } else {
                RxProbeAndLockPages(pSetupMdl,KernelMode,IoModifyAccess,Status);
                if ((Status != RX_MAP_STATUS(SUCCESS))) {
                    IoFreeMdl(pSetupMdl);
                    pSetupMdl = NULL;
                } else {
                    if (MmGetSystemAddressForMdlSafe(pSetupMdl,LowPagePriority) == NULL) { //this maps the Mdl
                        Status = STATUS_INSUFFICIENT_RESOURCES;
                    }
                }
            }
        }

        if ((Status == RX_MAP_STATUS(SUCCESS)) && (pParam != NULL) && (ParamLength > 0)) {
            pParamMdl = RxAllocateMdl(pParam,ParamLength);
            if (pParamMdl == NULL) {
                Status = STATUS_INSUFFICIENT_RESOURCES;
            } else {
                RxProbeAndLockPages(pParamMdl,KernelMode,IoModifyAccess,Status);
                if ((Status != RX_MAP_STATUS(SUCCESS))) {
                    IoFreeMdl(pParamMdl);
                    pParamMdl = NULL;
                } else {
                    if (MmGetSystemAddressForMdlSafe(pParamMdl,LowPagePriority) == NULL) { //this maps the Mdl
                        Status = STATUS_INSUFFICIENT_RESOURCES;
                    }
                }
            }
        }

        pTransactionParameters->SetupLength = SetupLength;
        pTransactionParameters->ParamLength = ParamLength;
        pTransactionParameters->pParamMdl = pParamMdl;
        pTransactionParameters->pSetupMdl = pSetupMdl;
    } else {
        pTransactionParameters->SetupLength = SetupLength;
        pTransactionParameters->pSetup      = pSetup;
        pTransactionParameters->ParamLength = ParamLength;
        pTransactionParameters->pParam      = pParam;
        pTransactionParameters->pParamMdl = NULL;
    }

    ASSERT( !((pData == NULL)&&(DataLength!=0)) );
    if ((Status == RX_MAP_STATUS(SUCCESS)) && (pData != NULL) && (DataLength > 0)) {
        pDataMdl = RxAllocateMdl(pData,DataLength);
        if (pDataMdl == NULL) {
            Status = STATUS_INSUFFICIENT_RESOURCES;
        } else {
            RxProbeAndLockPages(pDataMdl,KernelMode,IoModifyAccess,Status);
            if ((Status != RX_MAP_STATUS(SUCCESS))) {
                IoFreeMdl(pDataMdl);
                pDataMdl = NULL;
            } else {
                if (MmGetSystemAddressForMdlSafe(pDataMdl,LowPagePriority) == NULL) { //this maps the Mdl
                    Status = STATUS_INSUFFICIENT_RESOURCES;
                }
            }
        }
    }

    pTransactionParameters->pDataMdl  = pDataMdl;
    pTransactionParameters->DataLength  = DataLength;
    ASSERT((Status != RX_MAP_STATUS(SUCCESS)) || (DataLength == 0) || (pDataMdl != NULL));

    if ((Status != RX_MAP_STATUS(SUCCESS))) {
        if (pTransactionParameters->Flags & TRANSACTION_RECEIVE_PARAMETERS_FLAG) {
            if (pSetupMdl != NULL) {
                MmUnlockPages(pSetupMdl);  //this unmaps as well
                IoFreeMdl(pSetupMdl);
            }

            if (pParamMdl != NULL) {
                MmUnlockPages(pParamMdl);
                IoFreeMdl(pParamMdl);
            }
        }

        if (pDataMdl != NULL) {
            MmUnlockPages(pDataMdl);
            IoFreeMdl(pDataMdl);
        }
    }

    return Status;
}

VOID
SmbCeUninitializeTransactionParameters(
   PSMB_TRANSACTION_PARAMETERS pTransactionParameters
)
/*++

Routine Description:

    This routine uninitializes the transaction parameters, i.e., free the associated MDL's

Arguments:

    pTransactionParameters - the parameter instance for uninitialization

--*/
{
    PAGED_CODE();

    if (pTransactionParameters->Flags & TRANSACTION_RECEIVE_PARAMETERS_FLAG) {
        if (pTransactionParameters->pSetupMdl != NULL) {
            MmUnlockPages(pTransactionParameters->pSetupMdl);
            IoFreeMdl(pTransactionParameters->pSetupMdl);
        }
    }

    if (pTransactionParameters->pParamMdl != NULL) {
         MmUnlockPages(pTransactionParameters->pParamMdl);
        IoFreeMdl(pTransactionParameters->pParamMdl);
    }

    if (pTransactionParameters->pDataMdl != NULL
        && !BooleanFlagOn(pTransactionParameters->Flags,SMB_XACT_FLAGS_CALLERS_SENDDATAMDL)) {
        MmUnlockPages(pTransactionParameters->pDataMdl);
        IoFreeMdl(pTransactionParameters->pDataMdl);
    }
}

VOID
SmbCeDiscardTransactExchange(PSMB_TRANSACT_EXCHANGE pTransactExchange)
/*++

Routine Description:

    This routine discards a transact exchange

Arguments:

    pExchange - the exchange instance

--*/
{
    PSMB_TRANSACTION_RESUMPTION_CONTEXT pResumptionContext;

    PAGED_CODE();

    // Deallocate any transact exchange specfic allocations ...
    if (pTransactExchange->pActualPrimaryRequestSmbHeader != NULL) {
        RxFreePool(pTransactExchange->pActualPrimaryRequestSmbHeader);
    }

    if (pTransactExchange->pReceiveSetupMdl != NULL) {
        MmUnlockPages(pTransactExchange->pReceiveSetupMdl);
        IoFreeMdl(pTransactExchange->pReceiveSetupMdl);
    }

    if (pTransactExchange->pReceiveParamMdl != NULL) {
        MmUnlockPages(pTransactExchange->pReceiveParamMdl);
        IoFreeMdl(pTransactExchange->pReceiveParamMdl);
    }

    if (pTransactExchange->pReceiveDataMdl != NULL) {
        MmUnlockPages(pTransactExchange->pReceiveDataMdl);
        IoFreeMdl(pTransactExchange->pReceiveDataMdl);
    }

    if (pTransactExchange->pSendSetupMdl != NULL) {
        MmUnlockPages(pTransactExchange->pSendSetupMdl);
        IoFreeMdl(pTransactExchange->pSendSetupMdl);
    }

    if ((pTransactExchange->pSendDataMdl != NULL) &&
         !BooleanFlagOn(pTransactExchange->Flags,SMB_XACT_FLAGS_CALLERS_SENDDATAMDL)) {
        MmUnlockPages(pTransactExchange->pSendDataMdl);
        IoFreeMdl(pTransactExchange->pSendDataMdl);
    }

    if (pTransactExchange->pSendParamMdl != NULL) {
        MmUnlockPages(pTransactExchange->pSendParamMdl);
        IoFreeMdl(pTransactExchange->pSendParamMdl);
    }

    if ((pResumptionContext = pTransactExchange->pResumptionContext) != NULL) {
        NTSTATUS FinalStatus;
        PSMBCEDB_SERVER_ENTRY pServerEntry = SmbCeGetExchangeServerEntry((PSMB_EXCHANGE)pTransactExchange);

        RxDbgTrace(0, Dbg,
                 ("SmbCeTransactExchangeFinalize: everythings is good! parambytes (%ld) databytes (%ld)\n",
                  pTransactExchange->ParamBytesReceived, pTransactExchange->DataBytesReceived
                ));

        FinalStatus = pTransactExchange->Status;

        if (pTransactExchange->Status != STATUS_SUCCESS) {
            FinalStatus = CscTransitionVNetRootForDisconnectedOperation(
                              pTransactExchange->RxContext,
                              SmbCeGetExchangeVNetRoot(pTransactExchange),
                              pTransactExchange->Status);
        }
        else if (pTransactExchange->SmbStatus != STATUS_SUCCESS)
        {
            FinalStatus = CscTransitionVNetRootForDisconnectedOperation(
                              pTransactExchange->RxContext,
                              SmbCeGetExchangeVNetRoot(pTransactExchange),
                              pTransactExchange->SmbStatus);

        }

        if (pServerEntry->ServerStatus != STATUS_SUCCESS &&
            !SmbCeIsServerInDisconnectedMode(pServerEntry) &&
            !FlagOn(pTransactExchange->SmbCeFlags,SMBCE_EXCHANGE_MAILSLOT_OPERATION)) {
            // If the server entry is in error state, the transact cannot receive a response from server.
            // In this case, we return the server status.
            pResumptionContext->FinalStatusFromServer = pServerEntry->ServerStatus;
        } else {
            // If the server entry is in good or disconnected state, we return the smb status.
            pResumptionContext->FinalStatusFromServer = pTransactExchange->SmbStatus;
        }

        if ((FinalStatus == STATUS_SUCCESS)||
            (FinalStatus == STATUS_MORE_PROCESSING_REQUIRED)) {

            FinalStatus = pResumptionContext->FinalStatusFromServer;
        }

        pResumptionContext->SmbCeResumptionContext.Status = FinalStatus;
        pResumptionContext->SetupBytesReceived = pTransactExchange->SetupBytesReceived;
        pResumptionContext->DataBytesReceived = pTransactExchange->DataBytesReceived;
        pResumptionContext->ParameterBytesReceived = pTransactExchange->ParamBytesReceived;
        pResumptionContext->ServerVersion = pTransactExchange->ServerVersion;

        SmbCeResume(&pResumptionContext->SmbCeResumptionContext);
    }

    SmbCeDereferenceAndDiscardExchange((PSMB_EXCHANGE)pTransactExchange);
}

NTSTATUS
SmbCeSubmitTransactionRequest(
    PRX_CONTEXT                           RxContext,
    PSMB_TRANSACTION_OPTIONS              pOptions,
    PSMB_TRANSACTION_PARAMETERS           pSendParameters,
    PSMB_TRANSACTION_PARAMETERS           pReceiveParameters,
    PSMB_TRANSACTION_RESUMPTION_CONTEXT   pResumptionContext )
/*++

Routine Description:

    This routine submits a transaction request, i.e., allocates/initializes a transaction
    exchange, sets up the completion information and initiates it

Arguments:

    pNetRoot           - the netroot for which the transaction request is intended

    pOptions           - the transaction options

    pSendParameters    - the transaction parameters to be sent to the server

    pReceiveParameters - the transaction results from the server

    pResumptionContext - the context for resuming the local activity on completion of the
                         transaction

Return Value:

    RXSTATUS - The return status for the operation
      STATUS_PENDING -- if the transcation was initiated successfully
      Other error codes if the request could not be submitted successfully

Notes:

    Whenever a status of STATUS_PENDING is returned it implies that the transact
    exchange has assumed ownership of the MDLs passed in as receive and send
    parameters. They will be released on completion of the exchange.

--*/
{
    NTSTATUS Status = STATUS_SUCCESS;

    RxCaptureFcb;
    RxCaptureFobx;

    PMRX_V_NET_ROOT pVNetRoot = NULL;

    PSMB_TRANSACT_EXCHANGE pTransactExchange;
    PSMB_EXCHANGE          pExchange = NULL;

    PAGED_CODE();

    if (capFobx == NULL) {
        if (RxContext->MajorFunction == IRP_MJ_CREATE) {
            pVNetRoot = RxContext->Create.pVNetRoot;
        }
    } else {
        // These are the root objects which are associated with the device FCB. In
        // such cases

        pVNetRoot = (PMRX_V_NET_ROOT)capFobx;

        if (NodeType(pVNetRoot) != RDBSS_NTC_V_NETROOT) {
            pVNetRoot = capFobx->pSrvOpen->pVNetRoot;
        }
    }

    if (pVNetRoot == NULL) {
        PSMBCEDB_SERVER_ENTRY pServerEntry;

        pServerEntry = SmbCeGetAssociatedServerEntry(capFcb->pNetRoot->pSrvCall);

        // Allocate and initialize an exchange for the given net root.
        Status = SmbCeInitializeExchange2(
                    &pExchange,
                    RxContext,
                    pServerEntry,
                    TRANSACT_EXCHANGE,
                    &TransactExchangeDispatch);
    } else {
        // Allocate and initialize an exchange for the given net root.
        Status = SmbCeInitializeExchange(
                    &pExchange,
                    RxContext,
                    pVNetRoot,
                    TRANSACT_EXCHANGE,
                    &TransactExchangeDispatch);
    }

    if (Status == STATUS_SUCCESS) {
        // Initialize the transact exchange
        pTransactExchange = (PSMB_TRANSACT_EXCHANGE)pExchange;

        Status = SmbCeInitializeTransactExchange(
                     pTransactExchange,
                     RxContext,
                     pOptions,
                     pSendParameters,
                     pReceiveParameters,
                     pResumptionContext);

        if (Status == STATUS_SUCCESS) {
            // The transact exchange can be either asynchronous or synchronous. In
            // the asynchronous case an additional reference is taken which is
            // passed onto the caller alongwith the exchange squirelled away in the
            // RX_CONTEXT if STATUS_PENDING is being returned. This enables the
            // caller to control when the exchange is discarded. This works
            // especially well in dealing with cancellation of asynchronous
            // exchanges.

            // This reference will be accounted for by the finalization routine
            // of the transact exchange.
            SmbCeReferenceExchange((PSMB_EXCHANGE)pTransactExchange);

            if (BooleanFlagOn(pOptions->Flags,SMB_XACT_FLAGS_ASYNCHRONOUS)) {
                // The corresponding dereference is the callers responsibility
                SmbCeReferenceExchange((PSMB_EXCHANGE)pTransactExchange);
            }

            if (pTransactExchange->Flags & SMB_XACT_FLAGS_MAILSLOT_OPERATION) {
                pTransactExchange->SmbCeFlags |= SMBCE_EXCHANGE_MAILSLOT_OPERATION;
            }

            pResumptionContext->pTransactExchange = pTransactExchange;
            pResumptionContext->SmbCeResumptionContext.Status = STATUS_SUCCESS;

            SmbCeIncrementPendingLocalOperations(pExchange);

            // Initiate the exchange
            Status = SmbCeInitiateExchange(pExchange);

            if (Status != STATUS_PENDING) {
                pExchange->Status = Status;

                if (pExchange->SmbStatus == STATUS_SUCCESS) {
                    pExchange->SmbStatus = Status;
                }

                if (BooleanFlagOn(pOptions->Flags,SMB_XACT_FLAGS_ASYNCHRONOUS)) {
                    PMRXSMB_RX_CONTEXT pMRxSmbContext = MRxSmbGetMinirdrContext(RxContext);

                    pMRxSmbContext->pExchange     = NULL;

                    // Since the exchange has already been completed there is no
                    // point in returning the additional reference to the caller
                    SmbCeDereferenceExchange((PSMB_EXCHANGE)pTransactExchange);
                }
            }

            SmbCeDecrementPendingLocalOperationsAndFinalize(pExchange);

            // Map the status to STATUS_PENDING so that continuation routines
            // do not attempt to finalize.
            Status = STATUS_PENDING;
        } else {
            PMRXSMB_RX_CONTEXT MRxSmbContext = MRxSmbGetMinirdrContext(RxContext);

            ASSERT(MRxSmbContext->pExchange == pExchange);
            MRxSmbContext->pExchange = NULL;

            SmbCeDiscardExchange(pExchange);
        }
    }

    return Status;
}

NTSTATUS
_SmbCeTransact(
   PRX_CONTEXT                         RxContext,
   PSMB_TRANSACTION_OPTIONS            pOptions,
   PVOID                               pInputSetupBuffer,
   ULONG                               InputSetupBufferLength,
   PVOID                               pOutputSetupBuffer,
   ULONG                               OutputSetupBufferLength,
   PVOID                               pInputParamBuffer,
   ULONG                               InputParamBufferLength,
   PVOID                               pOutputParamBuffer,
   ULONG                               OutputParamBufferLength,
   PVOID                               pInputDataBuffer,
   ULONG                               InputDataBufferLength,
   PVOID                               pOutputDataBuffer,
   ULONG                               OutputDataBufferLength,
   PSMB_TRANSACTION_RESUMPTION_CONTEXT pResumptionContext)
/*++

Routine Description:

    This routine implements a standardized mechanism of submitting transaction requests,
    and synchronizing with their completion. This does not provide the smae amount of control
    that SmbCeSubmitTransactRequest provides. Nevertheless, this implements a common mechanism
    that should satisfy most needs

Arguments:

    RxContext               - the context for the transaction

    pOptions                - the transaction options

    pSetupBuffer            - the transaction setup buffer

    SetupBufferlength       - the setup buffer length

    pInputParamBuffer       - the Input param buffer

    InputParamBufferLength  - the input param buffer length

    pOutputParamBuffer      - the output param buffer

    OutputParamBufferlength - the output param buffer length

    pInputDataBuffer        - the Input data buffer

    InputDataBufferLength   - the input data buffer length

    pOutputDataBuffer       - the output data buffer

    OutputDataBufferlength  - the output data buffer length

    pResumptionContext       - the transaction resumption context

Return Value:

    RXSTATUS - The return status for the operation
      STATUS_SUCCESS if successfull.
      Other error codes if the request could not be submitted successfully

Notes:

    In the case of asynchronous exchanges if STATUS_PENDING is returned the
    Exchange instance is squirelled away in the minirdr context associated with
    the given RX_CONTEXT instance. This exchange will not be discarded without
    the callers intervention. It is the callers responsibility to invoke
    SmbCeDereferenceAndDiscardExchange to discard the exchange

--*/
{
    NTSTATUS Status;

    SMB_TRANSACTION_SEND_PARAMETERS     SendParameters;
    SMB_TRANSACTION_RECEIVE_PARAMETERS  ReceiveParameters;
    BOOLEAN                             fAsynchronous;

    PAGED_CODE();

    fAsynchronous = BooleanFlagOn(pOptions->Flags,SMB_XACT_FLAGS_ASYNCHRONOUS);

    Status = SmbCeInitializeTransactionSendParameters(
                 pInputSetupBuffer,
                 (USHORT)InputSetupBufferLength,
                 pInputParamBuffer,
                 InputParamBufferLength,
                 pInputDataBuffer,
                 InputDataBufferLength,
                 &SendParameters);

    if (Status == STATUS_SUCCESS) {
        Status = SmbCeInitializeTransactionReceiveParameters(
                     pOutputSetupBuffer,        // the setup information expected in return
                     (USHORT)OutputSetupBufferLength,   // the length of the setup information
                     pOutputParamBuffer,        // the buffer for the param information
                     OutputParamBufferLength,   // the length of the param buffer
                     pOutputDataBuffer,         // the buffer for data
                     OutputDataBufferLength,    // the length of the buffer
                     &ReceiveParameters);

        if (Status != STATUS_SUCCESS) {
            SmbCeUninitializeTransactionSendParameters(&SendParameters);
        }
    }

    if (Status == STATUS_SUCCESS) {
        Status = SmbCeSubmitTransactionRequest(
                     RxContext,                    // the RXContext for the transaction
                     pOptions,                     // transaction options
                     &SendParameters,              // input parameters
                     &ReceiveParameters,           // expected results
                     pResumptionContext            // the context for resumption.
                     );

        if ((Status != STATUS_SUCCESS) &&
            (Status != STATUS_PENDING)) {
            SmbCeUninitializeTransactionReceiveParameters(&ReceiveParameters);
            SmbCeUninitializeTransactionSendParameters(&SendParameters);
        } else {
            if (!fAsynchronous) {
                if (Status == STATUS_PENDING) {
                    SmbCeWaitOnTransactionResumptionContext(pResumptionContext);
                    Status = pResumptionContext->SmbCeResumptionContext.Status;
                    if (Status != STATUS_SUCCESS) {
                        RxDbgTrace(0,Dbg,("SmbCeTransact: Transaction Request Completion Status %lx\n",Status));
                    }
                } else if (Status != STATUS_SUCCESS) {
                    RxDbgTrace(0,Dbg,("SmbCeTransact: SmbCeSubmitTransactRequest returned %lx\n",Status));
                } else {
                    Status = pResumptionContext->SmbCeResumptionContext.Status;
                }
            }
        }
    }

    ASSERT(fAsynchronous || (Status != STATUS_PENDING));

    if (fAsynchronous && (Status != STATUS_PENDING)) {
        pResumptionContext->SmbCeResumptionContext.Status = Status;
        pResumptionContext->FinalStatusFromServer = Status;
        SmbCeResume(&pResumptionContext->SmbCeResumptionContext);
        Status = STATUS_PENDING;
    }

    return Status;
}

NTSTATUS
SmbTransactBuildHeader(
    PSMB_TRANSACT_EXCHANGE  pTransactExchange,
    UCHAR                   SmbCommand,
    PSMB_HEADER             pHeader)
/*++

Routine Description:

    This routine builds the SMB header for transact exchanges

Arguments:

    pTransactExchange  - the exchange instance

    SmbCommand - the SMB command

    pHeader    - the SMB buffer header

Return Value:

    RXSTATUS - The return status for the operation

Notes:

--*/
{
    NTSTATUS Status;
    ULONG    BufferConsumed;

    UCHAR    LastCommandInHeader;
    PUCHAR   pCommand;

    PAGED_CODE();

    // Initialize the SMB header  ...
    Status = SmbCeBuildSmbHeader(
                 (PSMB_EXCHANGE)pTransactExchange,
                 pHeader,
                 sizeof(SMB_HEADER),
                 &BufferConsumed,
                 &LastCommandInHeader,
                 &pCommand);

    if (Status == STATUS_SUCCESS) {
        PSMBCEDB_SERVER_ENTRY pServerEntry;

        ASSERT(LastCommandInHeader == SMB_COM_NO_ANDX_COMMAND);
        *pCommand = SmbCommand;

        pServerEntry = SmbCeGetExchangeServerEntry(pTransactExchange);

        if (FlagOn(pServerEntry->Server.DialectFlags,DF_NT_SMBS)) {
            // for NT servers, we have to set the pid/pidhigh fields so that RPC will work. unless this is a
            // mailslot write.
            if (!(pTransactExchange->Flags & SMB_XACT_FLAGS_MAILSLOT_OPERATION)) {
                SmbCeSetFullProcessIdInHeader(
                    (PSMB_EXCHANGE)pTransactExchange,
                    RxGetRequestorProcessId(pTransactExchange->RxContext),
                    ((PNT_SMB_HEADER)pHeader));
            }
        }

        if (pTransactExchange->Flags & SMB_XACT_FLAGS_MAILSLOT_OPERATION) {
            pHeader->Flags2 &= ~(SMB_FLAGS2_NT_STATUS);
        }

        if (pTransactExchange->Flags & SMB_XACT_FLAGS_DFS_AWARE) {
            pHeader->Flags2 |= SMB_FLAGS2_DFS;
        }
    }

    return Status;
}


NTSTATUS
SmbTransactExchangeStart(
      PSMB_EXCHANGE  pExchange)
/*++

Routine Description:

    This is the start routine for transact exchanges. This initiates the construction of the
    appropriate SMB's if required.

Arguments:

    pExchange - the exchange instance

Return Value:

    RXSTATUS - The return status for the operation

Notes:

--*/
{
    NTSTATUS Status;

    PSMB_TRANSACT_EXCHANGE pTransactExchange;
    PVOID                  pActualPrimaryRequestSmbHeader;
    PSMB_HEADER            pPrimaryRequestSmbHeader;

    // The MDL's used in sending the primary request associated with the TRANSACT SMB
    PMDL  pPartialDataMdl       = NULL;
    PMDL  pPartialParamMdl      = NULL;
    PMDL  pPaddingMdl           = NULL;
    PMDL  pPrimaryRequestSmbMdl = NULL;
    PMDL  pLastMdlInChain       = NULL;

    ULONG   MaximumSmbBufferSize;
    ULONG   PrimaryRequestSmbSize = 0;
    ULONG   PaddingLength = 0;
    BOOLEAN QuadwordAlignmentRequired = FALSE;

    ULONG ParamBytesToBeSent = 0;
    ULONG DataBytesToBeSent = 0;

    ULONG ParamOffset,DataOffset;
    ULONG SmbLength;
    ULONG BccOffset;
    ULONG MdlLength;

    USHORT *pBcc;

    PAGED_CODE();

    pTransactExchange        = (PSMB_TRANSACT_EXCHANGE)pExchange;

    pActualPrimaryRequestSmbHeader = pTransactExchange->pActualPrimaryRequestSmbHeader;
    pPrimaryRequestSmbHeader = pTransactExchange->pPrimaryRequestSmbHeader;

    ASSERT(pActualPrimaryRequestSmbHeader != NULL);
    ASSERT(pPrimaryRequestSmbHeader != NULL);

    ASSERT(!(pExchange->SmbCeFlags & SMBCE_EXCHANGE_SESSION_CONSTRUCTOR) &&
           !(pExchange->SmbCeFlags & SMBCE_EXCHANGE_NETROOT_CONSTRUCTOR));

    // Initialize the SMB header  ...
    Status = SmbTransactBuildHeader(
                 pTransactExchange,
                 pTransactExchange->TransactSmbCommand,
                 pPrimaryRequestSmbHeader);

    if ((Status != RX_MAP_STATUS(SUCCESS))) {
        // Finalize the exchange.
        pExchange->Status = Status;
        return Status;
    }

    PrimaryRequestSmbSize = sizeof(SMB_HEADER);

    // Compute the BccOffset and the ParamOffset which is in turn used in computing the
    // param and data bytes to be sent as part of the primary request.
    switch (pTransactExchange->TransactSmbCommand) {
    case SMB_COM_TRANSACTION:
    case SMB_COM_TRANSACTION2:
        {
            PREQ_TRANSACTION pTransactRequest = (PREQ_TRANSACTION)
                                             (pPrimaryRequestSmbHeader + 1);
            USHORT SetupLength = pTransactRequest->SetupCount * sizeof(WORD);

            BccOffset = sizeof(SMB_HEADER) +
                        FIELD_OFFSET(REQ_TRANSACTION,Buffer) +
                        SetupLength;

            ParamOffset = ROUND_UP_COUNT(
                              (BccOffset +
                              pTransactExchange->TransactionNameLength +
                              sizeof(USHORT)),
                              ALIGN_DWORD);

            pBcc = (PUSHORT)((PBYTE)pPrimaryRequestSmbHeader + BccOffset);
        }
        break;

    case SMB_COM_NT_TRANSACT:
        {
            PREQ_NT_TRANSACTION pNtTransactRequest = (PREQ_NT_TRANSACTION)
                                                  (pPrimaryRequestSmbHeader + 1);
            USHORT SetupLength = pNtTransactRequest->SetupCount * sizeof(WORD);

            RxDbgTrace( 0, Dbg, ("SmbTransactExchangeSTAAT1: init for NT_T (p,d,mp,md) %d %d %d %d\n",
                         pNtTransactRequest->TotalParameterCount, pNtTransactRequest->TotalDataCount,
                         pNtTransactRequest->MaxParameterCount, pNtTransactRequest->MaxDataCount));
            RxDbgTrace( 0, Dbg, ("SmbTransactExchangeSTAyuk: init for NT_T (s,ms) %d %d \n",
                         pNtTransactRequest->SetupCount,  pNtTransactRequest->MaxSetupCount));


            BccOffset = sizeof(SMB_HEADER) +
                        FIELD_OFFSET(REQ_NT_TRANSACTION,Buffer[0]) +
                        SetupLength;

            ParamOffset = ROUND_UP_COUNT(
                              (BccOffset + sizeof(USHORT)),
                              ALIGN_DWORD);

            pBcc = (PUSHORT)((PBYTE)pPrimaryRequestSmbHeader + BccOffset);

            if (pTransactExchange->NtTransactFunction == NT_TRANSACT_SET_QUOTA) {
                QuadwordAlignmentRequired = TRUE;
            }
       }
       break;

    default:
        ASSERT(!"Valid Smb Command for initiating Transaction");
        return STATUS_INVALID_PARAMETER;
    }

    // Compute the data/param bytes that can be sent as part of the primary request
    MaximumSmbBufferSize = pTransactExchange->MaximumTransmitSmbBufferSize;

    ParamBytesToBeSent = MIN(
                             (MaximumSmbBufferSize - ParamOffset),
                             pTransactExchange->SendParamBufferSize);
    if (!QuadwordAlignmentRequired) {
        DataOffset = ROUND_UP_COUNT(ParamOffset + ParamBytesToBeSent, ALIGN_DWORD);
    } else {
        DataOffset = ROUND_UP_COUNT(ParamOffset + ParamBytesToBeSent, ALIGN_QUAD);
    }

    if (DataOffset < MaximumSmbBufferSize) {
        DataBytesToBeSent = MIN((MaximumSmbBufferSize - DataOffset),
                                pTransactExchange->SendDataBufferSize);
        PaddingLength = DataOffset - (ParamOffset + ParamBytesToBeSent);
    } else {
        DataBytesToBeSent = 0;
    }

    if ( DataBytesToBeSent == 0) {
        DataOffset = PaddingLength = 0;
    }

    RxDbgTrace( 0, Dbg, ("SmbCeTransactExchangeStart: params,padding,data=%d,%d,%d\n",
                           ParamBytesToBeSent,PaddingLength,DataBytesToBeSent  ));
    RxDbgTrace( 0, Dbg, ("SmbCeTransactExchangeStart: paramsoffset,dataoffset=%d,%d\n",
                           ParamOffset,DataOffset  ));
    RxDbgTrace( 0, Dbg, ("SmbCeTransactExchangeStart: phdr,pbcc=%08lx,%08lx\n",
                           pPrimaryRequestSmbHeader,pBcc  ));

    // Update the primary request buffer with the final sizes of the data/parameter etc.
    switch (pTransactExchange->TransactSmbCommand) {
    case SMB_COM_TRANSACTION:
    case SMB_COM_TRANSACTION2:
        {
            PREQ_TRANSACTION pTransactRequest = (PREQ_TRANSACTION)
                                             (pPrimaryRequestSmbHeader + 1);

            RxDbgTrace( 0, Dbg, ("SmbCeTransactExchangeStart: TRANSACTION/TRANSACTION2\n"));

            SmbPutUshort( &pTransactRequest->ParameterCount, (USHORT)ParamBytesToBeSent );
            SmbPutUshort( &pTransactRequest->ParameterOffset, (USHORT)ParamOffset);
            SmbPutUshort( &pTransactRequest->DataCount, (USHORT)DataBytesToBeSent);
            SmbPutUshort( &pTransactRequest->DataOffset, (USHORT)DataOffset);
        }
        break;

   case SMB_COM_NT_TRANSACT:
        {
            PREQ_NT_TRANSACTION pNtTransactRequest = (PREQ_NT_TRANSACTION)
                                                  (pPrimaryRequestSmbHeader + 1);

            RxDbgTrace( 0, Dbg, ("SmbCeTransactExchangeStart: NT transacton\n"));
            RxDbgTrace( 0, Dbg, ("SmbTransactExchangeSTAAT2: init for NT_T (p,d,mp,md) %d %d %d %d\n",
                         pNtTransactRequest->TotalParameterCount, pNtTransactRequest->TotalDataCount,
                         pNtTransactRequest->MaxParameterCount, pNtTransactRequest->MaxDataCount));


            SmbPutUlong( &pNtTransactRequest->ParameterCount, ParamBytesToBeSent);
            SmbPutUlong( &pNtTransactRequest->ParameterOffset, ParamOffset);
            SmbPutUlong( &pNtTransactRequest->DataCount, DataBytesToBeSent);
            SmbPutUlong( &pNtTransactRequest->DataOffset, DataOffset);
        }
        break;

    default:
        ASSERT(!"Valid Smb Command for initiating Transaction");
        return STATUS_INVALID_PARAMETER;
    }

    // Update the Bcc field in the SMB and compute the SMB length
    SmbPutUshort(
        pBcc,
        (USHORT)((ParamOffset - BccOffset - sizeof(USHORT)) +
                 ParamBytesToBeSent +
                 PaddingLength +
                 DataBytesToBeSent)
        );

    SmbLength = ParamOffset +
                ParamBytesToBeSent +
                PaddingLength +
                DataBytesToBeSent;

    // The primary request buffer should be locked down for transmission. In order to
    // preclude race conditions while freeing this routine assumes ownership of the buffer.
    // There are two reasons why this model has to be adopted ...
    // 1) Inititaiting a transaction request can possibly involve a reconnection attempt
    // which will involve network traffic. Consequently the transmission of the primary
    // request can potentially occur in a worker thread which is different from the one
    // initializing the exchange. This problem can be worked around by carrying all the
    // possible context around and actually constructing the header as part of this routine.
    // But this would imply that those requests which could have been filtered out easily
    // because of error conditions etc. will be handled very late.

    pTransactExchange->pActualPrimaryRequestSmbHeader = NULL;
    pTransactExchange->pPrimaryRequestSmbHeader = NULL;

    // Ensure that the MDL's have been probed & locked. The new MDL's have been allocated.
    // The partial MDL's are allocated to be large enough to span the maximum buffer
    // length possible.

    MdlLength = ParamOffset;
    if (pTransactExchange->fParamsSubsumedInPrimaryRequest) {
        MdlLength += ParamBytesToBeSent + PaddingLength;
    }

    RxAllocateHeaderMdl(
        pPrimaryRequestSmbHeader,
        MdlLength,
        pPrimaryRequestSmbMdl
        );

    if (pPrimaryRequestSmbMdl != NULL) {
        Status = STATUS_SUCCESS;
    } else {
        RxDbgTrace( 0, Dbg, ("SmbCeTransactExchangeStart: Insuffcient resources for MDL's\n"));
        Status = STATUS_INSUFFICIENT_RESOURCES;
    }

    if ((DataBytesToBeSent > 0) &&
       (Status == RX_MAP_STATUS(SUCCESS))) {
        pPartialDataMdl = RxAllocateMdl(
                              0,
                              (MIN(pTransactExchange->SendDataBufferSize,MaximumSmbBufferSize) +
                               PAGE_SIZE - 1)
                              );

        if (pPartialDataMdl != NULL) {
            Status = STATUS_SUCCESS;
        } else {
            RxDbgTrace( 0, Dbg, ("SmbCeTransactExchangeStart: Insuffcient resources for MDL's\n"));
            Status = STATUS_INSUFFICIENT_RESOURCES;
        }
    }

    if ((ParamBytesToBeSent > 0) &&
        !pTransactExchange->fParamsSubsumedInPrimaryRequest &&
        (Status == RX_MAP_STATUS(SUCCESS))) {

        pPartialParamMdl = RxAllocateMdl(
                               pTransactExchange->pSendParamBuffer,
                               ParamBytesToBeSent);

        if (PaddingLength!= 0) {
            pPaddingMdl = RxAllocateMdl(0,(sizeof(DWORD) + PAGE_SIZE - 1));
        } else {
            pPaddingMdl = NULL;
        }

        if ((pPartialParamMdl != NULL) &&
            ((pPaddingMdl != NULL)||(PaddingLength==0))) {
            Status = STATUS_SUCCESS;
        } else {
            RxDbgTrace( 0, Dbg, ("SmbCeTransactExchangeStart: no param/pad MDLs %08lx %08lx\n",
               pPartialParamMdl,pPaddingMdl));
            Status = STATUS_INSUFFICIENT_RESOURCES;
        }
    }

    // At this point the validity of all the parameters will have been ascertained. The trivial
    // cases have been filtered out. Start the transact exchange.

    // Implementation Note: The Transact exchange implementation relies upon chaining the
    // MDL's together to build the relevant request buffers that need be sent. This ensures
    // that redundant copying of data is avoided altogether. Depending upon the parameters
    // specified the composite MDL that is sent is composed of the following MDL's.
    // TRANSACT2 and NT TRANSACT exchanges ...
    //          The composite buffer is made up off atmost four MDL's that are chained together. These
    //           are the header buffer, the setup buffer, parameter buffer and the data buffer.
    //          All the secondary requests are made up off atmost three MDL's that are chained together.
    //          These are the header buffer, the parameter buffer and the data buffer.
    // TRANSACT exchanges ....
    //          The composite buffer is made up off atmost three MDL's that are chained together. These are
    //          the header buffer ( includes the name and the setup information) , the parameter buffer
    //          and the data buffer.
    // All the secondary requests are made up off atmost three MDL's that are chained together.
    // These are the header buffer, the parameter buffer and the data buffer.
    // In all of these cases the number of MDL's can go up by 1 if a padding MDL is required
    // between the parameter buffer and the data buffer to ensure that all alignment requirements
    // are satisfied.

    if ((Status == RX_MAP_STATUS(SUCCESS))) {

        RxProbeAndLockHeaderPages(pPrimaryRequestSmbMdl,KernelMode,IoModifyAccess,Status);
        if ((Status != RX_MAP_STATUS(SUCCESS))) {  //do this now. the code below will try to unlock
            IoFreeMdl(pPrimaryRequestSmbMdl);
            pPrimaryRequestSmbMdl = NULL;
        } else {
            if (MmGetSystemAddressForMdlSafe(pPrimaryRequestSmbMdl,LowPagePriority) == NULL) { //map it
                Status = STATUS_INSUFFICIENT_RESOURCES;
            }
        }
    }

    if ((Status == RX_MAP_STATUS(SUCCESS))) {
        pLastMdlInChain = pPrimaryRequestSmbMdl;

        if (ParamBytesToBeSent > 0) {
            RxDbgTrace(
                0,
                Dbg,
                ("SmbCeTransactExchangeStart: Sending Param bytes %ld at offset %ld\n",
                 ParamBytesToBeSent,
                 ParamOffset)
                );
            pTransactExchange->ParamBytesSent = ParamBytesToBeSent;

            if (!pTransactExchange->fParamsSubsumedInPrimaryRequest) {
                IoBuildPartialMdl(
                    pTransactExchange->pSendParamMdl,
                    pPartialParamMdl,
                    (PBYTE)MmGetMdlVirtualAddress(pTransactExchange->pSendParamMdl),
                    ParamBytesToBeSent);

                // Chain the MDL's together
                pLastMdlInChain->Next = pPartialParamMdl;
                pLastMdlInChain       = pPartialParamMdl;
            }
        }

        // Link the data buffer or portions of it if the size constraints are satisfied
        // If padding is required between the parameter and data portions in the
        // primary request include the padding MDL, otherwise chain the data MDL
        // directly.
        if (DataBytesToBeSent > 0) {
            if (!pTransactExchange->fParamsSubsumedInPrimaryRequest &&
                (PaddingLength > 0)) {
                RxDbgTrace( 0, Dbg, ("SmbCeTransactExchangeStart: Padding Length %ld\n",PaddingLength));

                RxBuildPaddingPartialMdl(pPaddingMdl,PaddingLength);
                pLastMdlInChain->Next = pPaddingMdl;
                pLastMdlInChain = pPaddingMdl;
            }

            RxDbgTrace( 0, Dbg,("SmbCeTransactExchangeStart: Sending Data bytes %ld at offset %ld\n",
                 DataBytesToBeSent, DataOffset) );

            pTransactExchange->DataBytesSent = DataBytesToBeSent;

            IoBuildPartialMdl(
                pTransactExchange->pSendDataMdl,
                pPartialDataMdl,
                (PBYTE)MmGetMdlVirtualAddress(pTransactExchange->pSendDataMdl),
                DataBytesToBeSent);

            pLastMdlInChain->Next = pPartialDataMdl;
            pLastMdlInChain       = pPartialDataMdl;
        }

        if ((Status == RX_MAP_STATUS(SUCCESS))) {

            if (FlagOn(pTransactExchange->SmbCeFlags,SMBCE_EXCHANGE_MAILSLOT_OPERATION)) {
                pTransactExchange->SmbCeFlags |= SMBCE_EXCHANGE_MID_VALID;
                pTransactExchange->Mid        = SMBCE_MAILSLOT_OPERATION_MID;
            }

            // There are cases in which the transaction exchange can be completed by merely sending
            // the primary request SMB. This should be distinguished from those cases in which either
            // a response is expected or a number of secondary requests need to be issued based upon
            // the parameter buffer length, data buffer length and the flags specified.
            if ((pTransactExchange->Flags & SMB_TRANSACTION_NO_RESPONSE ) &&
                (pTransactExchange->SendDataBufferSize == DataBytesToBeSent) &&
                (pTransactExchange->SendParamBufferSize == ParamBytesToBeSent)) {
                // No response is expected in this case. Therefore Send should suffice instead of
                // Tranceive

                // since we don't expect to do any more here, set the exchange status to success
                pExchange->Status = STATUS_SUCCESS;
                pTransactExchange->pResumptionContext->FinalStatusFromServer = STATUS_SUCCESS;

                RxDbgTrace( 0, Dbg, ("SmbCeTransactExchangeStart: SmbCeSend(No Response expected)\n"));
                Status = SmbCeSend(
                             pExchange,
                             RXCE_SEND_SYNCHRONOUS,
                             pPrimaryRequestSmbMdl,
                             SmbLength);

                if ((Status != RX_MAP_STATUS(SUCCESS))) {
                    RxDbgTrace( 0, Dbg, ("SmbCeTransactExchangeStart: SmbCeSend returned %lx\n",Status));
                }
            } else {
                // This transaction involves ttansmit/receive of multiple SMB's. A tranceive is in
                // order.

                if ((pTransactExchange->SendDataBufferSize == DataBytesToBeSent) &&
                    (pTransactExchange->SendParamBufferSize == ParamBytesToBeSent)) {
                    RxDbgTrace( 0, Dbg, ("SmbCeTransactExchangeStart: No Secondary Requests\n"));
                    pTransactExchange->State = TRANSACT_EXCHANGE_TRANSMITTED_SECONDARY_REQUESTS;
                } else {
                    pTransactExchange->State = TRANSACT_EXCHANGE_TRANSMITTED_PRIMARY_REQUEST;
                }

                RxDbgTrace( 0, Dbg, ("SmbCeTransactExchangeStart: SmbCeTranceive(Response expected)\n"));
                //CODE.IMPROVEMENT send.sync????.........yeeeeech.
                Status = SmbCeTranceive(
                             pExchange,
                             RXCE_SEND_SYNCHRONOUS,
                             pPrimaryRequestSmbMdl,
                             SmbLength);

                if ((Status != RX_MAP_STATUS(SUCCESS))) {
                    RxDbgTrace( 0, Dbg, ("SmbCeTransactExchangeStart: SmbCeTranceive returned %lx\n",Status));
                }
            }
        }
    }

    if (pPartialParamMdl != NULL) {
        IoFreeMdl(pPartialParamMdl);
    }

    if (pPartialDataMdl != NULL) {
        IoFreeMdl(pPartialDataMdl);
    }

    if (pPaddingMdl != NULL) {
        IoFreeMdl(pPaddingMdl);
    }

    if (pPrimaryRequestSmbMdl != NULL) {
        if (RxMdlIsLocked(pPrimaryRequestSmbMdl))
        {
            RxUnlockHeaderPages(pPrimaryRequestSmbMdl);
        }
        IoFreeMdl(pPrimaryRequestSmbMdl);
    }

    RxFreePool(pActualPrimaryRequestSmbHeader);

    if (Status != STATUS_PENDING) {
        pExchange->Status = Status;
    }

    return Status;
}

NTSTATUS
SmbTransactExchangeReceive(
    IN struct _SMB_EXCHANGE *pExchange,    // The exchange instance
    IN ULONG          BytesIndicated,
    IN ULONG          BytesAvailable,
    OUT ULONG        *pBytesTaken,
    IN  PSMB_HEADER   pSmbHeader,
    OUT PMDL *pDataBufferPointer,
    OUT PULONG        pDataSize,
    IN ULONG          ReceiveFlags)
/*++

Routine Description:

    This is the recieve indication handling routine for transact exchanges

Arguments:

    pExchange - the exchange instance

    BytesIndicated - the number of bytes indicated

    Bytes Available - the number of bytes available

    pBytesTaken     - the number of bytes consumed

    pSmbHeader      - the byte buffer

    pDataBufferPointer - the buffer into which the remaining data is to be copied.

    pDataSize       - the buffer size.

Return Value:

    RXSTATUS - The return status for the operation

Notes:

    This routine is called at DPC level.

--*/
{
    NTSTATUS Status;
    PNTSTATUS pFinalSmbStatus;

    BOOLEAN fError = FALSE;
    BOOLEAN fIndicationNotSufficient = FALSE;
    BOOLEAN fMoreParsingRequired     = FALSE;
    BOOLEAN fDoErrorProcessing       = FALSE; //this is a hack CODE.IMPROVEMENT

    SMB_TRANSACT_RESP_FORMAT_DESCRIPTION Format;

    GENERIC_ANDX  CommandToProcess;

    ULONG TransactResponseSize       = 0;
    ULONG SetupBytesOffsetInResponse = 0;
    ULONG SetupBytesInResponse       = 0;
    ULONG CopyDataSize               = 0;

    PMDL  pSetupMdl       = NULL;
    PMDL  pCopyRequestMdl = NULL;

    PSMB_TRANSACT_EXCHANGE pTransactExchange = (PSMB_TRANSACT_EXCHANGE)pExchange;

    RxDbgTrace( 0, Dbg,
               ("SmbTransactExchangeReceive: Entering w/ Bytes Available (%ld) Bytes Indicated (%ld) State (%ld)\n",
                BytesAvailable,
                BytesIndicated,
                pTransactExchange->State
               ));
    RxDbgTrace( 0, Dbg,
               ("SmbTransactExchangeReceive: Buffer %08lx Consumed (%ld) MDL (%08lx)\n",
                pSmbHeader,
                *pBytesTaken,
                *pDataBufferPointer
               ));

    pFinalSmbStatus = &pTransactExchange->SmbStatus;
    Status = SmbCeParseSmbHeader(
                 pExchange,
                 pSmbHeader,
                 &CommandToProcess,
                 pFinalSmbStatus,
                 BytesAvailable,
                 BytesIndicated,
                 pBytesTaken);

    if (Status != STATUS_SUCCESS) {
        Status = STATUS_INVALID_NETWORK_RESPONSE;
        goto FINALLY;
    }

    //this need some explanation. parseheader is written so as to take some extra smbs off the from
    //of the packet...specifically, stuff like sessionsetup&X and TC&X. since no transact is a valid followon
    //it would not make since if (a) not enough were indicated or (b) an early command had an error. so
    //we must have success. CODE.REVIEW.JOELINN you should look in parseheader and (1) remove the *taken=avail and
    //(b) look for asserts that the server sends stuff back correctly. these must be changed into BAD_RESPONSE_AND_DISCARDs
    //the "Status = STATUS_SUCCESS" is to try to get the compiler to optimize.

    if (*((PBYTE)(pSmbHeader+1)) == 0 && (pTransactExchange->State!=TRANSACT_EXCHANGE_TRANSMITTED_PRIMARY_REQUEST)) {
        RxDbgTrace(0,Dbg,("SmbTransactExchangeReceive: FinalSmbStatus = %lx\n", *pFinalSmbStatus));

        if (NT_SUCCESS(*pFinalSmbStatus)) {
            Status = STATUS_INVALID_NETWORK_RESPONSE;
            goto FINALLY;
        }
    }

    //we know that status is SUCCESS from the assert above. but we will still continue to check so as
    //to be more resilient when we don't have msg boundaries. we have the following cases depending on the
    //characteristics of the smbresponse
    //
    //   non-error:   get the data and then return the stored responsestatus. the process of getting the data
    //                causes us to update the param and data counts so that we know when we have reached the
    //                end of the data. the parse routine re-ups the receive if needed.
    //   error:       there are main cases:
    //                    a) the server has sent no data. here we discard the packet and we can just get out. the
    //                       finalize routine will pickup the status correctly.
    //                    b) here, we have to discard the packet AND update the byte counts AND re-up the receive
    //                       if necessary. to discard the packet, we must either compute the apparent msg length from
    //                       the WC and BC parameters (best) OR use our maximum buffer size

    fMoreParsingRequired = FALSE;

    if ((Status == RX_MAP_STATUS(SUCCESS))) {
        if (TRUE) { //maybe sometimes we wont copy!
            if (CommandToProcess.WordCount > 0) {
                ULONG TransactResponseSize = 0;

                // Ensure that at the very least enough bytes have been indicated to determine
                // the length of the setup, parameters and data for the transaction.

                //CODE.IMPROVEMENT.ASHAMED this is very clumsy....we should have computed this earlier.
                // and saved it in the exchange. at a minimum move it to the validateformat routine
                switch (CommandToProcess.AndXCommand) {
                case SMB_COM_NT_TRANSACT:
                case SMB_COM_NT_TRANSACT_SECONDARY:
                    TransactResponseSize = FIELD_OFFSET(RESP_NT_TRANSACTION,Buffer);
                    break;

                case SMB_COM_TRANSACTION:
                case SMB_COM_TRANSACTION2:
                case SMB_COM_TRANSACTION_SECONDARY:
                case SMB_COM_TRANSACTION2_SECONDARY:
                    TransactResponseSize = FIELD_OFFSET(RESP_TRANSACTION,Buffer);
                    break;

                default:
                    TransactResponseSize = 0xffffffff;
                    Status = RX_MAP_STATUS(INVALID_NETWORK_RESPONSE);
                    break;
                }

                if (BytesIndicated >= (sizeof(SMB_HEADER) + TransactResponseSize)) {
                    fMoreParsingRequired = TRUE;
                } else {
                    fIndicationNotSufficient = TRUE;
                    *pFinalSmbStatus = STATUS_INVALID_NETWORK_RESPONSE;
                }
            } else {
                // allow a response with wordcount==0 to go thru if we're the right state
                fMoreParsingRequired = (pTransactExchange->State==TRANSACT_EXCHANGE_TRANSMITTED_PRIMARY_REQUEST);
            }
        }
    }

    if (fMoreParsingRequired) {
        // The header was successfully parsed and the SMB response did not contain any errors
        // The stage is set for processing the transaction response.

        switch (pTransactExchange->State) {
        case TRANSACT_EXCHANGE_TRANSMITTED_PRIMARY_REQUEST:
            {
                // The primary request for the transaction has been sent and there are
                // secondary requests to be sent.
                // The only response expected at this time is an interim response. Any
                // other response will be treated as an error.
                PRESP_TRANSACTION_INTERIM pInterimResponse;

                RxDbgTrace(0,Dbg,("SmbCeTransactExchangeReceive: Processing interim response\n"));

                if ((*pBytesTaken + FIELD_OFFSET(RESP_TRANSACTION_INTERIM,Buffer)) <= BytesIndicated) {
                    pInterimResponse = (PRESP_TRANSACTION_INTERIM)((PBYTE)pSmbHeader + *pBytesTaken);
                    if ((NT_SUCCESS(pExchange->SmbStatus)) &&
                        (pSmbHeader->Command == pTransactExchange->TransactSmbCommand) &&
                        (SmbGetUshort(&pInterimResponse->WordCount) == 0) &&
                        (SmbGetUshort(&pInterimResponse->ByteCount) == 0)) {

                        // The interim response was valid. Transition the state of the exchange
                        // and transmit the secondary requests.
                        *pBytesTaken += FIELD_OFFSET(RESP_TRANSACTION_INTERIM,Buffer);
                        //CODE.IMPROVEMENT that only works if the server doesn't send extra crap
                        pTransactExchange->State = TRANSACT_EXCHANGE_RECEIVED_INTERIM_RESPONSE;

                        // Determine if any secondary transaction requests need to be sent. if none are
                        // required then modify the state
                        ASSERT((pTransactExchange->ParamBytesSent < pTransactExchange->SendParamBufferSize) ||
                               (pTransactExchange->DataBytesSent < pTransactExchange->SendDataBufferSize));
                        ASSERT((pTransactExchange->ParamBytesSent <= pTransactExchange->SendParamBufferSize) &&
                               (pTransactExchange->DataBytesSent <= pTransactExchange->SendDataBufferSize));

                        if (!(pTransactExchange->Flags & SMB_TRANSACTION_NO_RESPONSE )) {
                            Status = SmbCeReceive(pExchange);
                        }

                        if ((Status != RX_MAP_STATUS(SUCCESS))) {
                            pExchange->Status = Status;
                        } else {
                            Status = STATUS_SUCCESS;
                            SmbCeIncrementPendingLocalOperations(pExchange);
                            RxPostToWorkerThread(
                                MRxSmbDeviceObject,
                                CriticalWorkQueue,
                                &pExchange->WorkQueueItem,
                                SendSecondaryRequests,
                                pExchange);
                        }
                    } else if( !NT_SUCCESS(pExchange->SmbStatus) ) {
                        RxDbgTrace(0,Dbg,("SmbCeTransactExchangeReceive: Error on Response\n"));
                        Status = pExchange->SmbStatus;
                    } else {
                        RxDbgTrace(0,Dbg,("SmbCeTransactExchangeReceive: Invalid interim response\n"));
                        Status = STATUS_INVALID_NETWORK_RESPONSE;
                    }
                } else {
                    fIndicationNotSufficient = TRUE;
                    Status = RX_MAP_STATUS(MORE_PROCESSING_REQUIRED);
                }
            }
            break;

        case TRANSACT_EXCHANGE_RECEIVED_INTERIM_RESPONSE:
            RxDbgTrace(0,Dbg,("SmbCeTransactExchangeReceive: received again while in interim response\n"));
          //no break: this is okay
        case TRANSACT_EXCHANGE_TRANSMITTED_SECONDARY_REQUESTS:
        case TRANSACT_EXCHANGE_RECEIVED_PRIMARY_RESPONSE:
            {
                BOOLEAN fPrimaryResponse = FALSE;
                PRESP_TRANSACTION    pTransactResponse;
                PRESP_NT_TRANSACTION pNtTransactResponse;
                ULONG TotalParamBytesInResponse;
                ULONG TotalDataBytesInResponse;

                RxDbgTrace(0,Dbg,("SmbCeTransactExchangeReceive: Processing Primary/Secondary response\n"));

                //do this here so there's only one copy if the code
                pTransactResponse = (PRESP_TRANSACTION)((PBYTE)pSmbHeader +
                                              SmbGetUshort(&CommandToProcess.AndXOffset));

                // All the requests ( both primary and secondary have been sent ). The
                // only responses expected in this state are (1) a primary response and (2) a
                // secondary response. Any other response is an error.
                if (pSmbHeader->Command == pTransactExchange->TransactSmbCommand) {
                    switch (pSmbHeader->Command) {
                    case SMB_COM_TRANSACTION:
                    case SMB_COM_TRANSACTION2:
                        //pTransactResponse = (PRESP_TRANSACTION)((PBYTE)pSmbHeader +
                        //                                        SmbGetUshort(&CommandToProcess.AndXOffset));
                        fPrimaryResponse = TRUE;
                        SetupBytesOffsetInResponse = FIELD_OFFSET(RESP_TRANSACTION,Buffer);
                        SetupBytesInResponse = sizeof(USHORT) * pTransactResponse->SetupCount;

                        // Initialize the total count of data and param bytes that will be received from
                        // the server during the course ofthe transaction response.
                        TotalParamBytesInResponse = SmbGetUshort(&pTransactResponse->TotalParameterCount);
                        TotalDataBytesInResponse  = SmbGetUshort(&pTransactResponse->TotalDataCount);

                    // fall through
                    case SMB_COM_TRANSACTION_SECONDARY:
                    case SMB_COM_TRANSACTION2_SECONDARY:
                        TransactResponseSize = FIELD_OFFSET(RESP_TRANSACTION,Buffer);
                        break;
                    case SMB_COM_NT_TRANSACT:
                        //pNtTransactResponse = (PRESP_NT_TRANSACTION)((PBYTE)pSmbHeader +
                        //                                        SmbGetUshort(&CommandToProcess.AndXOffset));
                        pNtTransactResponse = (PRESP_NT_TRANSACTION)pTransactResponse;
                        fPrimaryResponse = TRUE;
                        SetupBytesOffsetInResponse = FIELD_OFFSET(RESP_NT_TRANSACTION,Buffer);
                        SetupBytesInResponse = sizeof(USHORT) * pNtTransactResponse->SetupCount;

                        // Initialize the total count of data and param bytes that will be received from
                        // the server during the course ofthe transaction response.
                        TotalParamBytesInResponse = SmbGetUshort(&pNtTransactResponse->TotalParameterCount);
                        TotalDataBytesInResponse  = SmbGetUshort(&pNtTransactResponse->TotalDataCount);

                        // fall through ..
                    case SMB_COM_NT_TRANSACT_SECONDARY:
                        TransactResponseSize = FIELD_OFFSET(RESP_NT_TRANSACTION,Buffer);
                        break;

                    default:
                        // Abort the exchange. An unexpected response was received during the
                        // course of the transaction.
                        ASSERT(!"Valid network response");
                        Status = STATUS_INVALID_NETWORK_RESPONSE;
                    }

                    if ((Status == RX_MAP_STATUS(SUCCESS))) {
                        if (fPrimaryResponse) {
                            RxDbgTrace( 0,
                                 Dbg,
                                 ("SmbTransactExchangeReceive: Primary Response Setup Bytes(%ld) Param Bytes (%ld) Data Bytes (%ld)\n",
                                  SetupBytesInResponse,
                                  TotalParamBytesInResponse,
                                  TotalDataBytesInResponse
                                 )
                               );

                            if ((TotalParamBytesInResponse > pTransactExchange->ReceiveParamBufferSize) ||
                                (TotalDataBytesInResponse > pTransactExchange->ReceiveDataBufferSize)) {
                                Status = STATUS_INVALID_NETWORK_RESPONSE;
                                goto FINALLY;
                            } else {
                                pTransactExchange->ReceiveParamBufferSize = TotalParamBytesInResponse;
                                pTransactExchange->ReceiveDataBufferSize  = TotalDataBytesInResponse;
                            }
                        }

                        if (Status == STATUS_SUCCESS &&
                            TransactResponseSize + *pBytesTaken <= BytesIndicated) {
                            if (fPrimaryResponse &&
                                (SetupBytesInResponse > 0)) {

                                PBYTE pSetupStartAddress;
                                ULONG SetupBytesIndicated = MIN(SetupBytesInResponse,
                                                            BytesIndicated - SetupBytesOffsetInResponse);

                                if( pTransactExchange->pReceiveSetupMdl ) {
                                    pSetupStartAddress = (PBYTE)MmGetSystemAddressForMdlSafe(
                                                                pTransactExchange->pReceiveSetupMdl,
                                                                LowPagePriority
                                                                );

                                    if( pSetupStartAddress == NULL ) {
                                        Status = STATUS_INSUFFICIENT_RESOURCES;
                                    } else {
                                        if (SetupBytesInResponse == SetupBytesIndicated) {
                                            RtlCopyMemory(
                                                pSetupStartAddress,
                                                ((PBYTE)pSmbHeader + SetupBytesOffsetInResponse),
                                                SetupBytesIndicated);

                                            pSetupStartAddress += SetupBytesIndicated;
                                            SetupBytesInResponse -= SetupBytesIndicated;
                                            SetupBytesOffsetInResponse += SetupBytesIndicated;
                                            pTransactExchange->SetupBytesReceived = SetupBytesInResponse;
                                        } else {
                                            // NTRAID-87018-2/10/2000 yunlin we do a indication_not_sufficient
                                            ASSERT(!"this code doesn't work");
                                            RxDbgTrace(0,Dbg,("SmbTransactExchangeReceive: Setup Bytes Partially Indicated\n"));
                                            // Some setup bytes have not been indicated. An MDL needs to be
                                            // created for copying the data. This MDL should also include the padding
                                            // MDL for copying the padding bytes ...
                                            pSetupMdl = RxAllocateMdl(pSetupStartAddress,SetupBytesInResponse);

                                            if ( pSetupMdl != NULL ) {
                                                IoBuildPartialMdl(
                                                     pTransactExchange->pReceiveSetupMdl,
                                                     pSetupMdl,
                                                     pSetupStartAddress,
                                                     SetupBytesInResponse);
                                            } else {
                                                Status = STATUS_INSUFFICIENT_RESOURCES;
                                            }
                                        }
                                    }
                                }

                                RxDbgTrace(0,Dbg,("SmbTransactExchangeReceive: Setup Bytes Indicated (%ld)\n",SetupBytesIndicated));
                            }

                            if (Status == STATUS_SUCCESS) {
                                // from here, we cannot go back and redo the header....so we have to change state so
                                //that the copy routine doesn't try to reparse
                                pTransactExchange->State = TRANSACT_EXCHANGE_RECEIVED_PRIMARY_RESPONSE;

                                Status = SmbTransactAccrueAndValidateFormatData(
                                             pTransactExchange,
                                             pSmbHeader,
                                             BytesIndicated,
                                             &Format);

                                if (Status != STATUS_SUCCESS) {
                                    goto FINALLY;
                                }

                                Status = ParseTransactResponse(
                                             pTransactExchange,&Format,
                                             BytesIndicated,
                                             BytesAvailable,
                                             pBytesTaken,
                                             pSmbHeader,
                                             &pCopyRequestMdl,
                                             &CopyDataSize);

                                if (Status == STATUS_MORE_PROCESSING_REQUIRED) {
                                    // Link the setup MDL with the MDL returned
                                    if (pSetupMdl != NULL) {
                                        if (pCopyRequestMdl != NULL) {
                                            pSetupMdl->Next = pCopyRequestMdl;
                                        }

                                        pCopyRequestMdl = pSetupMdl;
                                        CopyDataSize += SetupBytesInResponse;
                                    }
                                }

                                //check if the server has sent extra bytes.....
                                // ---------------------------------------------------------------------------------------------
                                {
                                    ULONG ApparentMsgLength = max(BytesAvailable,Format.ApparentMsgLength);
                                    ULONG DeficitBytes = ApparentMsgLength - (*pBytesTaken+CopyDataSize);

                                    if (ApparentMsgLength < *pBytesTaken+CopyDataSize) {
                                        Status = STATUS_INVALID_NETWORK_RESPONSE;
                                        goto FINALLY;
                                    }

                                    if (DeficitBytes > 0) {
                                        RxLog(("XtraBytes %lx %lx",pTransactExchange,DeficitBytes));
                                        SmbLog(LOG,
                                               SmbTransactExchangeReceive_1,
                                               LOGPTR(pTransactExchange)
                                               LOGULONG(DeficitBytes));

                                        if (CopyDataSize==0) {
                                            if (*pBytesTaken > BytesAvailable) {
                                                Status = STATUS_INVALID_NETWORK_RESPONSE;
                                                goto FINALLY;
                                            }

                                            RxLog(("Extra Bytes were sent and copydatasize==0........\n"));
                                            SmbLog(LOG,
                                                   SmbTransactExchangeReceive_2,
                                                   LOGULONG(CopyDataSize));
                                            *pBytesTaken = BytesAvailable; //cant take more than this
                                        } else {
                                            PMDL LastMdl,TrailingBytesMdl;

                                            if ( DeficitBytes > TRAILING_BYTES_BUFFERSIZE) {
                                                Status = STATUS_INVALID_NETWORK_RESPONSE;
                                                goto FINALLY;
                                            }

                                            TrailingBytesMdl = &pTransactExchange->TrailingBytesMdl;

                                            MmInitializeMdl(
                                                TrailingBytesMdl,
                                                &pTransactExchange->TrailingBytesBuffer.Bytes[0],
                                                DeficitBytes
                                                );
                                            MmBuildMdlForNonPagedPool(TrailingBytesMdl);
                                            LastMdl = pCopyRequestMdl;
                                            ASSERT(LastMdl != NULL);
                                            for (;LastMdl->Next!=NULL;LastMdl=LastMdl->Next) ;
                                            ASSERT(LastMdl != NULL);
                                            ASSERT(LastMdl->Next == NULL);
                                            LastMdl->Next = TrailingBytesMdl;
                                            CopyDataSize += DeficitBytes;
                                        }
                                    }
                                }
                                // ---------------------------------------------------------------------------------------------


                                RxDbgTrace(0,Dbg,("SmbTransactExchangeReceive: ParseTransactResponse returned %lx\n",Status));
                            }

                            *pDataBufferPointer = pCopyRequestMdl;
                            *pDataSize          = CopyDataSize;
                        } else {
                            RxDbgTrace(0,Dbg,("SmbTransactExchangeReceive: Indication not sufficient: trsz %08lx bytestakn %08lx \n",
                                         TransactResponseSize, *pBytesTaken));
                            fIndicationNotSufficient = TRUE;

                            if (Status == STATUS_SUCCESS) {
                                Status = STATUS_MORE_PROCESSING_REQUIRED;
                            }
                        }
                    }
                } else {
                    Status = STATUS_INVALID_NETWORK_RESPONSE;
                }
            }
            break;

        default:
            {
                ASSERT(!"Valid Transact Exchange State for receiving responses");
                RxDbgTrace( 0, Dbg, ("SmbTransactExchangeReceive: Aborting Exchange -- invalid state\n"));
            }
            break;
        }
    } else {
        // We get here if either the status or the smbstatus is not success.
        // If sufficient bytes were not indicated for processing the header a copy data request
        // needs to be posted. this occurs if status is status_more_processing_required
        RxDbgTrace( 0, Dbg, ("SmbTransactExchangeReceive: bad status(es) from parseheadr %08lx %08lx\n",
                            Status,*pFinalSmbStatus));
        fDoErrorProcessing       = TRUE;
    }

    if ((Status == RX_MAP_STATUS(SUCCESS)) &&
        (pTransactExchange->ParamBytesReceived == pTransactExchange->ReceiveParamBufferSize) &&
        (pTransactExchange->DataBytesReceived  == pTransactExchange->ReceiveDataBufferSize) &&
        (pTransactExchange->PendingCopyRequests == 0)) {

        NOTHING;

    } else if (fDoErrorProcessing) {
        BOOLEAN DoItTheShortWay = TRUE;
        ULONG ApparentMsgLength;
        RxDbgTrace(0,Dbg,("SmbTransactExchangeReceive: Error processing response %lx .. Exchange aborted\n",Status));

        if (BytesAvailable > BytesIndicated ||
            !FlagOn(ReceiveFlags,TDI_RECEIVE_ENTIRE_MESSAGE)) {

            Status = SmbTransactAccrueAndValidateFormatData(
                         pTransactExchange,
                         pSmbHeader,
                         BytesIndicated,
                         &Format);

            if (Status != STATUS_SUCCESS) {
                goto FINALLY;
            }

            ApparentMsgLength = max(BytesAvailable,Format.ApparentMsgLength);

            //if wordcount!=0 then the server is sending us bytes.....we have to continue doing
            //receives until we have seen all the bytes
            if ((pTransactExchange->ParameterBytesSeen<Format.ParameterCount) ||
                (pTransactExchange->DataBytesSeen<Format.DataCount)) {
                NTSTATUS ReceiveStatus;

                // The exchange has been successfully completed. Finalize it.
                RxDbgTrace(0,Dbg,("ParseTransactResponse: Register for more error responses\n"));
                RxLog(("TxErr: %lx %lx %lx",pTransactExchange,
                       pTransactExchange->ParameterBytesSeen,pTransactExchange->DataBytesSeen));
                SmbLog(LOG,
                       SmbTransactExchangeReceive_3,
                       LOGPTR(pTransactExchange)
                       LOGULONG(pTransactExchange->ParameterBytesSeen)
                       LOGULONG(pTransactExchange->DataBytesSeen));
                ReceiveStatus = SmbCeReceive((PSMB_EXCHANGE)pTransactExchange);
                if (ReceiveStatus != STATUS_SUCCESS) {
                    // There was an error in registering the receive. Abandon the transaction.
                    Status = ReceiveStatus;
                    RxLog(("TxErrAbandon %lx",pTransactExchange));
                    SmbLog(LOG,
                           SmbTransactExchangeReceive_4,
                           LOGPTR(pTransactExchange)
                           LOGULONG(Status));
                    //Make it fail the next two tests.....
                    ApparentMsgLength = 0; DoItTheShortWay = FALSE; //CODE.IMPROVEMENT bad coding...use some escape
                }
            }

            //netbt will not allow us to discard the packet by setting taken=available. so, check for
            //available>indicated. if true, take the bytes by conjuring up a buffer

            if (ApparentMsgLength>BytesIndicated) {
                //we'll have to lay down a buffer for this so that NetBT won't blow the session away
                //CODE.IMPROVEMENT we should put this code into OE a well.......
                //CODE.IMPROVEMENT if we had an smbbuf (as suggested above) we could use that
                //                 to do the copy
                ASSERT(pTransactExchange->Status == STATUS_MORE_PROCESSING_REQUIRED);
                pTransactExchange->DiscardBuffer = RxAllocatePoolWithTag(
                                                       NonPagedPool,
                                                       ApparentMsgLength,
                                                       MRXSMB_XACT_POOLTAG);
                if (pTransactExchange->DiscardBuffer!=NULL) {
                    *pBytesTaken = 0;
                    *pDataSize = ApparentMsgLength;
                    *pDataBufferPointer = &pTransactExchange->TrailingBytesMdl;
                    MmInitializeMdl(*pDataBufferPointer,
                        pTransactExchange->DiscardBuffer,
                        ApparentMsgLength
                        );

                    MmBuildMdlForNonPagedPool(*pDataBufferPointer);
                    pTransactExchange->SaveTheRealStatus = Status;
                    RxLog(("XRtakebytes %lx %lx\n",pTransactExchange,Status));
                    SmbLog(LOG,
                           SmbTransactExchangeReceive_5,
                           LOGPTR(pTransactExchange)
                           LOGULONG(Status));
                    Status = STATUS_MORE_PROCESSING_REQUIRED;
                    DoItTheShortWay = FALSE;
                }
            }
        }

        if (DoItTheShortWay) {
            goto FINALLY;
        }
    }

    RxDbgTrace( 0, Dbg,
               ("SmbTransactExchangeReceiveExit: Bytes Consumed (%ld) Status (%08lx) MDL (%08lx) size(%08lx)\n",
                *pBytesTaken, Status, *pDataBufferPointer, *pDataSize
               ));

    if ((Status == STATUS_SUCCESS) ||
        (Status == STATUS_MORE_PROCESSING_REQUIRED)) {
        return Status;
    }

FINALLY:
    *pBytesTaken = BytesAvailable;
    *pDataBufferPointer = NULL;

    // Abort the exchange
    pTransactExchange->Status = Status;
    Status = STATUS_SUCCESS;

    RxDbgTrace(0,Dbg,("SmbTransactExchangeReceive: Exchange aborted.\n",Status));

    return Status;

    UNREFERENCED_PARAMETER(ReceiveFlags);
}

NTSTATUS
SmbTransactExchangeAbort(
      PSMB_EXCHANGE  pExchange)
/*++

Routine Description:

    This is the abort routine for transact exchanges

Arguments:

    pExchange - the exchange instance

Return Value:

    RXSTATUS - The return status for the operation

--*/
{
    PAGED_CODE();

    // The SMB exchange completed with an error. Invoke the RDBSS callback routine
    // and scavenge the exchange instance.

    pExchange->Status = STATUS_REQUEST_ABORTED;

    return STATUS_SUCCESS;
}

NTSTATUS
SmbTransactExchangeErrorHandler(
    IN PSMB_EXCHANGE pExchange)     // the SMB exchange instance
/*++

Routine Description:

    This is the error indication handling routine for transact exchanges

Arguments:

    pExchange - the exchange instance

Return Value:

    RXSTATUS - The return status for the operation

--*/
{
    PAGED_CODE();

    // The SMB exchange completed with an error. Invoke the RDBSS callback routine
    // and scavenge the exchange instance.
    return STATUS_SUCCESS;

    UNREFERENCED_PARAMETER(pExchange);
}

NTSTATUS
SmbTransactExchangeSendCallbackHandler(
    IN PSMB_EXCHANGE    pExchange,    // The exchange instance
    IN PMDL             pXmitBuffer,
    IN NTSTATUS         SendCompletionStatus)
/*++

Routine Description:

    This is the send call back indication handling routine for transact exchanges

Arguments:

    pExchange - the exchange instance

Return Value:

    RXSTATUS - The return status for the operation

--*/
{
    PAGED_CODE();

    return STATUS_SUCCESS;
    UNREFERENCED_PARAMETER(pExchange);
    UNREFERENCED_PARAMETER(pXmitBuffer);
    UNREFERENCED_PARAMETER(SendCompletionStatus);
}

NTSTATUS
SmbTransactExchangeCopyDataHandler(
    IN PSMB_EXCHANGE    pExchange,    // The exchange instance
    IN PMDL             pDataBuffer,  // the buffer
    IN ULONG            DataSize)
/*++

Routine Description:

    This is the copy data handling routine for transact exchanges

Arguments:

    pExchange - the exchange instance

    pDataBuffer - the buffer

    DataSize    - the amount of data returned

Return Value:

    RXSTATUS - The return status for the operation

--*/
{
    NTSTATUS Status = STATUS_SUCCESS;
    PSMB_TRANSACT_EXCHANGE pTransactExchange = (PSMB_TRANSACT_EXCHANGE)pExchange;

    PMDL  pCopyRequestMdl = NULL;
    PMDL  pCurMdl = NULL;
    ULONG CopyRequestSize = 0;
    PMDL TrailingBytesMdl = &pTransactExchange->TrailingBytesMdl;

    ULONG BytesConsumed;

    RxDbgTrace(+1,Dbg,("SmbTransactExchangeCopyDataHandler: Entered\n"));

    if (pTransactExchange->DiscardBuffer!=NULL) {
        //we just copied to get rid of the buffer....
        //free the buffer, set the status and get out
        RxFreePool(pTransactExchange->DiscardBuffer);
        Status = pTransactExchange->SaveTheRealStatus;
        RxDbgTrace(-1,Dbg,("SmbTransactExchangeCopyDataHandler: Discard Exit, status =%08lx\n"));
        DbgPrint("copyHandlerDiscard, st=%08lx\n",Status);
        return Status;
    }

    switch (pTransactExchange->State) {
    case TRANSACT_EXCHANGE_TRANSMITTED_PRIMARY_REQUEST :
    case TRANSACT_EXCHANGE_TRANSMITTED_SECONDARY_REQUESTS :
        {
            PSMB_HEADER pSmbHeader = (PSMB_HEADER)MmGetSystemAddressForMdlSafe(pDataBuffer,LowPagePriority);

            RxDbgTrace(0,Dbg,("SmbTransactExchangeCopyDataHandler: Reparsing response\n"));

            if (pSmbHeader == NULL) {
                Status = STATUS_INSUFFICIENT_RESOURCES;
            } else {
                // The response could not be parsed with the indicated bytes. Invoke
                // the receive method to resume parsing of the complete SMB
                Status = SmbTransactExchangeReceive(
                             pExchange,
                             DataSize,
                             DataSize,
                             &BytesConsumed,
                             pSmbHeader,
                             &pCopyRequestMdl,
                             &CopyRequestSize,
                             TDI_RECEIVE_ENTIRE_MESSAGE);
            }

            if ((Status == RX_MAP_STATUS(SUCCESS))) {
                ASSERT(BytesConsumed == DataSize);
                ASSERT(pCopyRequestMdl == NULL);
                ASSERT(CopyRequestSize == 0);
            }
        }
        break;

    case TRANSACT_EXCHANGE_RECEIVED_PRIMARY_RESPONSE :
        {
            RxDbgTrace(0,Dbg,("SmbTransactExchangeCopyDataHandler: Completing secondary response processing\n"));

            // In this state only secondary responses will be received. All the secondary
            // responses can be parsed from the indication. Therefore it is sufficient to
            // merely free the MDL's and re-register with the connection engine for
            // receiving subsequent requests.
            InterlockedDecrement(&pTransactExchange->PendingCopyRequests);

            if ((pTransactExchange->ParamBytesReceived == pTransactExchange->ReceiveParamBufferSize) &&
                (pTransactExchange->DataBytesReceived  == pTransactExchange->ReceiveDataBufferSize) &&
                (pTransactExchange->PendingCopyRequests == 0)) {
                // The exchange has been successfully completed. Finalize it.
                RxDbgTrace(0,Dbg,("SmbTransactExchangeCopyDataHandler: Processed last secondary response successfully\n"));
                pExchange->Status = STATUS_SUCCESS;
            }
        }
        break;

    default:
        {
            ASSERT(!"Valid State fore receiving copy data completion indication");
            pExchange->Status = STATUS_INVALID_NETWORK_RESPONSE;
        }
        break;
    }

    // Free up the data buffers.
    pCurMdl = pDataBuffer;

    while (pCurMdl != NULL) {
        PMDL pPrevMdl = pCurMdl;
        pCurMdl = pCurMdl->Next;
        if (pPrevMdl!=TrailingBytesMdl) {
            IoFreeMdl(pPrevMdl);
        }
    }

    RxDbgTrace(-1,Dbg,("SmbTransactExchangeCopyDataHandler: Exit\n"));
    return Status;
}

NTSTATUS
SmbCeInitializeTransactExchange(
    PSMB_TRANSACT_EXCHANGE              pTransactExchange,
    PRX_CONTEXT                         RxContext,
    PSMB_TRANSACTION_OPTIONS            pOptions,
    PSMB_TRANSACTION_SEND_PARAMETERS    pSendParameters,
    PSMB_TRANSACTION_RECEIVE_PARAMETERS pReceiveParameters,
    PSMB_TRANSACTION_RESUMPTION_CONTEXT pResumptionContext)
/*++

Routine Description:

    This routine initializes a transact exchange instance

Arguments:

    pTransactExchange - the exchange instance

    RxContext         - RDBSS context for the file involved in the transaction.

    pOptions          - the transaction options

    pSendParameters   - the parameters to be sent to the server

    pReceiveParameters - the results from the server

    pResumptionContext   - the resumption context

Return Value:

    RXSTATUS - The return status for the operation

--*/
{
    NTSTATUS Status = STATUS_SUCCESS;
    RxCaptureFobx;

    UCHAR SmbCommand;

    PMDL pSendDataMdl;
    PMDL pSendParamMdl; //used if we can't subsume
    PMDL pReceiveDataMdl;
    PMDL pReceiveParamMdl;

    PVOID pSendSetupBuffer;
    ULONG SendSetupBufferSize;

    PMDL  pReceiveSetupMdl;
    ULONG ReceiveSetupBufferSize;

    ULONG SendDataBufferSize;
    ULONG ReceiveDataBufferSize;

    PVOID pSendParamBuffer;
    ULONG SendParamBufferSize;
    ULONG ReceiveParamBufferSize;

    ULONG MaxSmbBufferSize = 0;
    ULONG PrimaryRequestSmbSize = 0;

    // The fields in theSMB request that are dialect independent and need to be filled in
    PUSHORT pBcc;    // the byte count field
    PUSHORT pSetup;  // the setup data
    PBYTE   pParam;  // the param data

    BOOLEAN fTransactionNameInUnicode = FALSE;

    PSMB_EXCHANGE pExchange = (PSMB_EXCHANGE)pTransactExchange;

    PVOID         pActualPrimaryRequestSmbHeader;
    PSMB_HEADER   pPrimaryRequestSmbHeader;

    PSMBCEDB_SERVER_ENTRY pServerEntry;

    PAGED_CODE();

    ASSERT(pTransactExchange->Type == TRANSACT_EXCHANGE);

    pTransactExchange->RxContext = RxContext;
    pServerEntry = SmbCeGetExchangeServerEntry(pExchange);

    {
        PMRXSMB_RX_CONTEXT pMRxSmbContext = MRxSmbGetMinirdrContext(RxContext);
        pMRxSmbContext->pExchange     = (PSMB_EXCHANGE)pTransactExchange;
    }

    ASSERT(pSendParameters != NULL);
    if (pSendParameters != NULL) {
        pSendDataMdl        = pSendParameters->pDataMdl;
        pSendParamBuffer    = pSendParameters->pParam;
        SendParamBufferSize = pSendParameters->ParamLength;
        pSendParamMdl       = pSendParameters->pParamMdl;
        pSendSetupBuffer    = pSendParameters->pSetup;
        SendSetupBufferSize = pSendParameters->SetupLength;
        SendDataBufferSize  = pSendParameters->DataLength;
        ASSERT( !((pSendDataMdl == NULL)&&(SendDataBufferSize!=0)) );
        RxDbgTrace( 0, Dbg, ("SmbTransactExchangeInitialize: at the top pbuf/psize/dsize=%08lx/%08lx\n"
                                 ,pSendParamBuffer,SendParamBufferSize,SendDataBufferSize));
    } else {
        Status = STATUS_INVALID_PARAMETER;
        RxDbgTrace( 0, Dbg, ("SmbTransactExchangeInitialize: Invalid Parameters\n",Status));
        return Status;
    }

    if (pReceiveParameters != NULL) {
        pReceiveDataMdl  = pReceiveParameters->pDataMdl;
        pReceiveParamMdl = pReceiveParameters->pParamMdl;
        pReceiveSetupMdl = pReceiveParameters->pSetupMdl;

        ReceiveDataBufferSize  = ((pReceiveDataMdl != NULL) ? MmGetMdlByteCount(pReceiveDataMdl) : 0);
        ASSERT (ReceiveDataBufferSize==pReceiveParameters->DataLength);
        ReceiveParamBufferSize = ((pReceiveParamMdl != NULL) ? MmGetMdlByteCount(pReceiveParamMdl) : 0);
        ReceiveSetupBufferSize = ((pReceiveSetupMdl != NULL) ? MmGetMdlByteCount(pReceiveSetupMdl) : 0);
    } else {
        pReceiveDataMdl = pReceiveParamMdl = pReceiveSetupMdl = NULL;
        ReceiveDataBufferSize = ReceiveParamBufferSize = ReceiveDataBufferSize = 0;
    }

    MaxSmbBufferSize = MIN (pServerEntry->Server.MaximumBufferSize,
                           pOptions->MaximumTransmitSmbBufferSize);
    pTransactExchange->MaximumTransmitSmbBufferSize = MaxSmbBufferSize;

    //CODE.IMPROVEMENT this switch should be replace by four ifs each testing for the right df-flag....

    // Ensure that the SMB dialect supports the exchange capability.
    switch (pServerEntry->Server.Dialect) {
    case NTLANMAN_DIALECT:
        {
            if (!FlagOn(pOptions->Flags,SMB_XACT_FLAGS_MAILSLOT_OPERATION) &&
                FlagOn(pServerEntry->Server.DialectFlags,DF_UNICODE)) {
                fTransactionNameInUnicode = TRUE;
            }
        }
        break;

    case LANMAN10_DIALECT:
    case WFW10_DIALECT:
        {
            // these guys only support transact...not T2 or NT. look for the name.....
            if (pOptions->pTransactionName == NULL) {
                RxDbgTrace( 0, Dbg, ("SmbTransactExchangeInitialize: Server Dialect does not support nameless transactions\n"));
                return STATUS_NOT_SUPPORTED;
            }
        }
       //no break intentional........
    case LANMAN12_DIALECT:
    case LANMAN21_DIALECT:
        {
            //  The NT_TRANSACT SMB is supported by NT servers only. Ensure that no attempt is being made
            //  to send an NT_TRANSACT SMB to a non NT server aka downlevel
            if (pOptions->NtTransactFunction != 0) {
                RxDbgTrace( 0, Dbg, ("SmbTransactExchangeInitialize: Server Dialect does not support transactions\n"));
                return STATUS_NOT_SUPPORTED;
            }

            fTransactionNameInUnicode = FALSE;
        }
        break;
    default:
        RxDbgTrace( 0, Dbg, ("SmbTransactExchangeInitialize: Server Dialect does not support transactions\n"));
        return STATUS_NOT_SUPPORTED;
    }

    PrimaryRequestSmbSize = sizeof(SMB_HEADER) + SendSetupBufferSize;

    // Ensure that the parameter sizes are all valid. The parameter and the data buffer
    // must be less than the maximum size to begin with.
    if ( pOptions->NtTransactFunction == 0) {
        if ((SendParamBufferSize > SMB_TRANSACT_MAXIMUM_PARAMETER_SIZE) ||
            (ReceiveParamBufferSize > SMB_TRANSACT_MAXIMUM_PARAMETER_SIZE) ||
            (SendDataBufferSize > SMB_TRANSACT_MAXIMUM_DATA_SIZE) ||
            (ReceiveDataBufferSize  > SMB_TRANSACT_MAXIMUM_DATA_SIZE)) {
            RxDbgTrace( 0, Dbg, ("SmbTransactExchangeInitialize: Parameters exceed maximum value\n"));
            return STATUS_INVALID_PARAMETER;
        }

        PrimaryRequestSmbSize += sizeof(REQ_TRANSACTION);

        // In all cases the name is sent as a UNICODE string if the appropriate capability is
        // supported. The only exception to this rule is for mail slots for which the name is
        // always transmitted as an ANSI string. Account for the null character as well in the
        // transaction name length.
        if (pOptions->pTransactionName != NULL) {
            if (!fTransactionNameInUnicode) {
                pTransactExchange->TransactionNameLength = RtlUnicodeStringToAnsiSize(pOptions->pTransactionName);
            } else {
                pTransactExchange->TransactionNameLength = pOptions->pTransactionName->Length + sizeof(WCHAR);

                PrimaryRequestSmbSize += (ULONG)((PBYTE)ALIGN_SMB_WSTR(PrimaryRequestSmbSize)
                                      - (PBYTE)(ULONG_PTR)PrimaryRequestSmbSize);
            }

            SmbCommand = SMB_COM_TRANSACTION;
        } else {
            // SMB protocol requires that a single NULL byte be sent as part of all
            // TRANSACT2 transactions.
            pTransactExchange->TransactionNameLength = 1;

            SmbCommand = SMB_COM_TRANSACTION2;
        }

        PrimaryRequestSmbSize += pTransactExchange->TransactionNameLength;
    } else {
        PrimaryRequestSmbSize += sizeof(REQ_NT_TRANSACTION);
        SmbCommand = SMB_COM_NT_TRANSACT;
        pTransactExchange->TransactionNameLength = 0;
    }

    // The header, setup bytes and the name if specified must be part of the primary
    // request SMB for a transaction to be successful. The secondary requests have no
    // provision for sending setup/name.
    if (PrimaryRequestSmbSize > MaxSmbBufferSize) {
        RxDbgTrace( 0, Dbg, ("SmbTransactExchangeInitialize: Primary request + setup exceeds maximum buffer size\n"));
        return STATUS_INVALID_PARAMETER;
    }

    // Include the byte count size and then align the size to a DWORD boundary.
    PrimaryRequestSmbSize = ROUND_UP_COUNT(PrimaryRequestSmbSize+sizeof(USHORT),ALIGN_DWORD);

    // Try to allocate for the param buffer as well if possible.    The additional DWORD
    // takes into account the worst case of alignment padding required.
    //if ( (PrimaryRequestSmbSize + SendParamBufferSize + sizeof(DWORD)) > MaxSmbBufferSize)
    if ((SendParamBufferSize!=0)
         && (((PrimaryRequestSmbSize + SendParamBufferSize) > MaxSmbBufferSize)
              || (DONTSUBSUME_PARAMS))    ){
        // The param will spill over to a secondary request. Do not attempt to over
        // allocate the primary request. if we can't subsume the params, then we'll need an MDL
        // to partial from.

        RxDbgTrace( 0, Dbg, ("SmbTransactExchangeInitialize: cannot subsume params\n"));
        pTransactExchange->fParamsSubsumedInPrimaryRequest = FALSE;
        pSendParamMdl = RxAllocateMdl(pSendParamBuffer,SendParamBufferSize);
        if (pSendParamMdl == NULL) {
            Status = STATUS_INSUFFICIENT_RESOURCES;
        } else {
            RxDbgTrace( 0, Dbg, ("SmbTransactExchangeInitialize: BIGPARAMMDL %08lx\n",pSendParamMdl));
            RxProbeAndLockPages(pSendParamMdl,KernelMode,IoModifyAccess,Status);
            if ((Status != RX_MAP_STATUS(SUCCESS))) {
                IoFreeMdl(pSendParamMdl);
            } else {
                if (MmGetSystemAddressForMdlSafe(pSendParamMdl,LowPagePriority) == NULL) { //map it
                    Status = STATUS_INSUFFICIENT_RESOURCES;
                }

                pSendParameters->pParamMdl = pSendParamMdl; // save it away
            }
        }
    } else {
        PrimaryRequestSmbSize = ROUND_UP_COUNT(PrimaryRequestSmbSize+SendParamBufferSize,ALIGN_DWORD);

        // Update the transact exchange to reflect the fact that no separate param MDL is
        // required.
        pTransactExchange->fParamsSubsumedInPrimaryRequest = TRUE;
    }

    //CODE.IMPROVEMENT this should be replaced by a call to get a smbbuf as in OrdExchg

    pActualPrimaryRequestSmbHeader = (PSMB_HEADER)RxAllocatePoolWithTag(
                                                PagedPool,
                               (PrimaryRequestSmbSize + 4 + TRANSPORT_HEADER_SIZE),
                                                MRXSMB_XACT_POOLTAG); //up to 4 pad bytes

    if (pActualPrimaryRequestSmbHeader == NULL) {
        RxDbgTrace( 0, Dbg, ("SmbTransactExchangeInitialize: Cannot allocate primary request SMB\n"));
        Status = STATUS_INSUFFICIENT_RESOURCES;
    }
    else {
        (PCHAR) pPrimaryRequestSmbHeader =
            (PCHAR) pActualPrimaryRequestSmbHeader + TRANSPORT_HEADER_SIZE;
    }

    if (Status == STATUS_SUCCESS) {
        switch (SmbCommand) {
        case SMB_COM_TRANSACTION :
        case SMB_COM_TRANSACTION2:
            {
                PREQ_TRANSACTION pTransactRequest;

                pTransactRequest  = (PREQ_TRANSACTION)(pPrimaryRequestSmbHeader + 1);
                pTransactRequest->WordCount = (UCHAR)(14 + (SendSetupBufferSize/sizeof(USHORT)));
                SmbPutUshort(
                    &pTransactRequest->TotalParameterCount,
                    (USHORT)SendParamBufferSize);
                SmbPutUshort(
                    &pTransactRequest->TotalDataCount,
                    (USHORT)SendDataBufferSize);
                SmbPutUshort(
                    &pTransactRequest->MaxParameterCount,
                    (USHORT)ReceiveParamBufferSize);
                SmbPutUshort(
                    &pTransactRequest->MaxDataCount,
                    (USHORT)ReceiveDataBufferSize);

                pTransactRequest->MaxSetupCount = (UCHAR)(ReceiveSetupBufferSize/sizeof(USHORT));

                pTransactRequest->Reserved = 0;
                pTransactRequest->Reserved3 = 0;
                SmbPutUshort(&pTransactRequest->Reserved2, 0);

                SmbPutUshort( &pTransactRequest->Flags, pOptions->Flags&~SMB_XACT_INTERNAL_FLAGS_MASK );
                pTransactRequest->SetupCount = (UCHAR)(SendSetupBufferSize/sizeof(USHORT));
                SmbPutUlong(&pTransactRequest->Timeout, pOptions->TimeoutIntervalInMilliSeconds);
                pSetup = (PUSHORT)pTransactRequest->Buffer;

                // Copy the transact name and align the buffer if required.
                if (pOptions->pTransactionName != NULL) {
                    PBYTE pName;
                    ULONG TransactionNameLength = pTransactExchange->TransactionNameLength;

                    // Set the name field in the SMB.
                    pName = (PBYTE)pSetup +
                            SendSetupBufferSize +
                            sizeof(USHORT);          // account for the bcc field

                    ASSERT(SmbCommand == SMB_COM_TRANSACTION);
                    RxDbgTrace( 0, Dbg, ("SmbTransactExchangeInitialize: TransactionName(Length %ld) %ws\n",
                                    TransactionNameLength,
                                    pOptions->pTransactionName->Buffer));

                    if (fTransactionNameInUnicode) {
                        pName = ALIGN_SMB_WSTR(pName);
                        Status = SmbPutUnicodeString(&pName,
                                     pOptions->pTransactionName,
                                     &TransactionNameLength);
                    } else {
                        Status = SmbPutUnicodeStringAsOemString(&pName,
                                     pOptions->pTransactionName,
                                     &TransactionNameLength);
                    }
                }

                pParam = (PBYTE)pSetup +
                         SendSetupBufferSize +
                         sizeof(USHORT) +                          // the bcc field
                         pTransactExchange->TransactionNameLength;
                pParam = ROUND_UP_POINTER(pParam, ALIGN_DWORD);
            }
            break;

        case SMB_COM_NT_TRANSACT:
            {
                PREQ_NT_TRANSACTION pNtTransactRequest;

                pNtTransactRequest = (PREQ_NT_TRANSACTION)(pPrimaryRequestSmbHeader + 1);
                pNtTransactRequest->WordCount = (UCHAR)(19 + (SendSetupBufferSize/sizeof(USHORT)));

                SmbPutUlong( &pNtTransactRequest->TotalParameterCount, SendParamBufferSize);
                SmbPutUlong( &pNtTransactRequest->TotalDataCount, SendDataBufferSize);
                SmbPutUlong( &pNtTransactRequest->MaxParameterCount, ReceiveParamBufferSize);
                SmbPutUlong( &pNtTransactRequest->MaxDataCount, ReceiveDataBufferSize);
                RxDbgTrace( 0, Dbg, ("SmbTransactExchangeInitialize: init for NT_T (p,d,mp,md) %d %d %d %d\n",
                           pNtTransactRequest->TotalParameterCount, pNtTransactRequest->TotalDataCount,
                           pNtTransactRequest->MaxParameterCount, pNtTransactRequest->MaxDataCount));

                pNtTransactRequest->MaxSetupCount = (UCHAR)(ReceiveSetupBufferSize / sizeof(USHORT));
                SmbPutUshort( &pNtTransactRequest->Flags, pOptions->Flags&~SMB_XACT_INTERNAL_FLAGS_MASK );
                SmbPutUshort( &pNtTransactRequest->Function, pOptions->NtTransactFunction );
                pNtTransactRequest->SetupCount = (UCHAR)(SendSetupBufferSize/sizeof(USHORT));
                pSetup = (PUSHORT)pNtTransactRequest->Buffer;
                pParam = (PBYTE)pSetup +
                         SendSetupBufferSize +
                         sizeof(USHORT);                          // the bcc field
                pParam = ROUND_UP_POINTER(pParam, ALIGN_DWORD);
            }
            break;

        default:
            ASSERT(!"Valid Smb Command Type for Transact exchange");
            Status = STATUS_INVALID_PARAMETER;
        }
    }

    if (Status == STATUS_SUCCESS) {
        // All related initialization of a transaction exchange has been
        // completed. At this point the transact exchange assumes ownership
        // of the various buffers ( specified as MDLs ) in the receive and
        // send parameters. It will get rid of them during finalization
        // of the exchange. In order to ensure that the caller does not
        // attempt to free any of these buffers they are reset in the
        // receive/send parameters.

        // Copy the setup data
        RtlCopyMemory(pSetup,pSendSetupBuffer,SendSetupBufferSize);

        if (pTransactExchange->fParamsSubsumedInPrimaryRequest) {
            RxDbgTrace( 0, Dbg, ("SmbTransactExchangeInitialize: subsuming where/size=%08lx/%08lx\n"
                                 ,pSendParamBuffer,SendParamBufferSize));
            RtlCopyMemory(pParam,pSendParamBuffer,SendParamBufferSize);
        }

        // Initialize the transact exchange.
        pTransactExchange->Status = STATUS_MORE_PROCESSING_REQUIRED;

        pTransactExchange->Mid = 0;
        pTransactExchange->TransactSmbCommand = SmbCommand;
        pTransactExchange->pActualPrimaryRequestSmbHeader = pActualPrimaryRequestSmbHeader;
        pTransactExchange->pPrimaryRequestSmbHeader = pPrimaryRequestSmbHeader;
        pTransactExchange->PrimaryRequestSmbSize    = PrimaryRequestSmbSize;

        pTransactExchange->pSendDataMdl = pSendDataMdl;
        pTransactExchange->SendDataBufferSize = SendDataBufferSize;
        pTransactExchange->pReceiveDataMdl  = pReceiveDataMdl;
        pTransactExchange->ReceiveDataBufferSize = ReceiveDataBufferSize;
        pTransactExchange->DataBytesSent = 0;
        pTransactExchange->DataBytesReceived = 0;

        pTransactExchange->pSendParamBuffer = pSendParamBuffer;
        pTransactExchange->SendParamBufferSize = SendParamBufferSize;
        pTransactExchange->pSendParamMdl  = pSendParamMdl;
        pTransactExchange->pReceiveParamMdl  = pReceiveParamMdl;
        pTransactExchange->ReceiveParamBufferSize = ReceiveParamBufferSize;
        pTransactExchange->ParamBytesSent = 0;
        pTransactExchange->ParamBytesReceived = 0;

        pTransactExchange->pReceiveSetupMdl       = pReceiveSetupMdl;
        pTransactExchange->ReceiveSetupBufferSize = ReceiveSetupBufferSize;
        pTransactExchange->SetupBytesReceived = 0;

        pTransactExchange->NtTransactFunction  = pOptions->NtTransactFunction;
        pTransactExchange->Flags               = pOptions->Flags;

        if ((capFobx != NULL) &&
            BooleanFlagOn(capFobx->Flags,FOBX_FLAG_DFS_OPEN)) {
            pTransactExchange->Flags |= SMB_XACT_FLAGS_DFS_AWARE;
        } else if (RxContext->MajorFunction == IRP_MJ_CREATE) {
            PMRX_NET_ROOT pNetRoot = RxContext->pFcb->pNetRoot;

            if (FlagOn(pNetRoot->Flags,NETROOT_FLAG_DFS_AWARE_NETROOT) &&
                RxContext->Create.NtCreateParameters.DfsContext == UIntToPtr(DFS_OPEN_CONTEXT)) {
                    pTransactExchange->Flags |= SMB_XACT_FLAGS_DFS_AWARE;
            }
        }

        pTransactExchange->pResumptionContext  = pResumptionContext;

        // Reset the Send and Receive parameter data structures to transfer
        // the ownership of the MDLs to the exchange.

        if (pSendParameters->Flags & SMB_XACT_FLAGS_CALLERS_SENDDATAMDL) {
            pTransactExchange->Flags |= SMB_XACT_FLAGS_CALLERS_SENDDATAMDL;
        }

        RtlZeroMemory(
            pSendParameters,
            sizeof(SMB_TRANSACTION_SEND_PARAMETERS));

        RtlZeroMemory(
            pReceiveParameters,
            sizeof(SMB_TRANSACTION_RECEIVE_PARAMETERS));
    }

    if (Status != STATUS_SUCCESS) {
        // Clean up the memory allocated in an effort to initialize the transact exchange
        if (pActualPrimaryRequestSmbHeader) {

            RxFreePool(pActualPrimaryRequestSmbHeader);
        }
    } else {
        PMRXSMB_RX_CONTEXT pMRxSmbContext = MRxSmbGetMinirdrContext(RxContext);

        pMRxSmbContext->pExchange = (PSMB_EXCHANGE)pTransactExchange;

        if (!FlagOn(pTransactExchange->Flags,SMB_XACT_FLAGS_MAILSLOT_OPERATION)) {
            // No reconnection attempts are allowed in transact exchanges except mailslot
            pTransactExchange->SmbCeFlags &= ~SMBCE_EXCHANGE_ATTEMPT_RECONNECTS;
        }

        if (pOptions->Flags & SMB_XACT_FLAGS_INDEFINITE_DELAY_IN_RESPONSE ) {
            pTransactExchange->SmbCeFlags |= SMBCE_EXCHANGE_INDEFINITE_DELAY_IN_RESPONSE;
        }
    }

    return Status;
}

NTSTATUS
SmbTransactExchangeFinalize(
    PSMB_EXCHANGE pExchange,
    BOOLEAN       *pPostFinalize)
/*++

Routine Description:

    This routine finalizes the transact exchange. It resumes the RDBSS by invoking
    the call back and discards the exchange

Arguments:

    pExchange - the exchange instance

    CurrentIrql - the interrupt request level

    pPostFinalize - set to TRUE if the request is to be posted

Return Value:

    RXSTATUS - The return status for the operation

--*/
{
    PSMB_TRANSACT_EXCHANGE               pTransactExchange;
    PSMB_TRANSACTION_RESUMPTION_CONTEXT  pResumptionContext;
    LONG                                 References;
    PSMBCEDB_SERVER_ENTRY pServerEntry = SmbCeGetExchangeServerEntry(pExchange);

    ASSERT(pExchange->Type == TRANSACT_EXCHANGE);

    pTransactExchange  = (PSMB_TRANSACT_EXCHANGE)pExchange;

    RxLog((">>>XE %lx",pTransactExchange));
    SmbLog(LOG,
           SmbTransactExchangeFinalize,
           LOGPTR(pTransactExchange));

    // Disassociate the MID associated with the exchange
    if (pExchange->SmbCeFlags & SMBCE_EXCHANGE_MID_VALID) {
        SmbCeDissociateMidFromExchange(pExchange->SmbCeContext.pServerEntry,pExchange);
    }

    if ((pTransactExchange->ReceiveParamBufferSize > 0) &&
        (pTransactExchange->ReceiveParamBufferSize !=
        pTransactExchange->ParamBytesReceived)) {
        RxDbgTrace(0, Dbg,
                 ("SmbCeTransactExchangeFinalize: Param Bytes Receive error ... expected(%ld) received(%ld)\n",
                  pTransactExchange->ReceiveParamBufferSize, pTransactExchange->ParamBytesReceived
                ));
    }

    if ((pTransactExchange->ReceiveDataBufferSize > 0) &&
        (pTransactExchange->ReceiveDataBufferSize !=
        pTransactExchange->DataBytesReceived)) {
        RxDbgTrace(0, Dbg,
                 ("SmbCeTransactExchangeFinalize: Data Bytes Receive error ... expected(%ld) received(%ld)\n",
                  pTransactExchange->ReceiveDataBufferSize, pTransactExchange->DataBytesReceived
                 ));
    }

    if (RxShouldPostCompletion()) {
        RxPostToWorkerThread(
            MRxSmbDeviceObject,
            CriticalWorkQueue,
            &pExchange->WorkQueueItem,
            SmbCeDiscardTransactExchange,
            pTransactExchange);
    } else {
        SmbCeDiscardTransactExchange(pTransactExchange);
    }

    return STATUS_SUCCESS;

    UNREFERENCED_PARAMETER(pPostFinalize);
}

NTSTATUS
SmbTransactAccrueAndValidateFormatData(
    IN struct _SMB_TRANSACT_EXCHANGE *pTransactExchange,    // The exchange instance
    IN  PSMB_HEADER                  pSmbHeader,
    IN  ULONG                        BytesIndicated,
    OUT PSMB_TRANSACT_RESP_FORMAT_DESCRIPTION Format
    )
/*++

Routine Description:

    This is the recieve indication handling routine for net root construction exchanges

Arguments:


Return Value:

    RXSTATUS - The return status for the operation
          STATUS_SUCCESS -- all the data was indicated and it was valid
          STATUS_INVALID_NETWORK_RESPONSE -- something about the format parameters is untoward.

Notes:

    This routine is called at DPC level.

--*/
{
    NTSTATUS Status = RX_MAP_STATUS(SUCCESS);
    PRESP_TRANSACTION pTransactResponse = (PRESP_TRANSACTION)(pSmbHeader+1);
    PBYTE WordCountPtr;
    UCHAR WordCount;
    PBYTE ByteCountPtr;
    USHORT ByteCount;

    RtlZeroMemory(Format,sizeof(*Format));

    Format->WordCount = WordCount = pTransactResponse->WordCount;
    ByteCountPtr = (&pTransactResponse->WordCount)+1+(sizeof(USHORT)*WordCount);

    if (((ULONG)(ByteCountPtr+sizeof(USHORT)-((PBYTE)pSmbHeader)))>BytesIndicated) {
        ByteCount = SmbGetUshort(ByteCountPtr);
        DbgPrint("ExtraTransactBytes wc,bcp,bc,smbh %lx,%lx,%lx,%lx\n",
                 WordCount,ByteCountPtr,ByteCount,pSmbHeader);
        return STATUS_INVALID_NETWORK_RESPONSE;
    }

    Format->ByteCount = ByteCount = SmbGetUshort(ByteCountPtr);
    Format->ApparentMsgLength = (ULONG)((ByteCountPtr+sizeof(USHORT)-((PBYTE)pSmbHeader))+ByteCount);

    if (WordCount==0) {
        return(STATUS_SUCCESS);
    }

#if 0
    ULONG WordCount;
    ULONG TotalParameterCount;
    ULONG TotalDataCount;
    ULONG ParameterCount;
    ULONG ParameterOffset;
    ULONG ParameterDisplacement;
    ULONG DataCount;
    ULONG DataOffset;
    ULONG DataDisplacement;
    ULONG ByteCount;
    ULONG ApparentMsgLength;
#endif

    // where is the validation: stuff that should be checked is
    //    a) that the values fit in the params and data spcified
    //    b) that we are not over the limit on bytes received
    //    c) that the response that we are receiving is valid for the command that we sent
    // we didn't compute ApparentMsgLength.......
    // The validation has not been done here. We rely on Transact Receive routine to detect the invalid response.

    //CODE.IMPROVEMENT  we could save some space with an unstuffer here........... but
    //   we'd have to amortize the cost over a lot more places. we should look on RISC machines to see if they turn it
    //   into a single copy.

    switch (pSmbHeader->Command) {
    case SMB_COM_TRANSACTION2:
    case SMB_COM_TRANSACTION:
    case SMB_COM_TRANSACTION_SECONDARY:
    case SMB_COM_TRANSACTION2_SECONDARY:
        {
            Format->TotalParameterCount    = SmbGetUshort(&pTransactResponse->TotalParameterCount);
            Format->TotalDataCount         = SmbGetUshort(&pTransactResponse->TotalDataCount);

            Format->ParameterCount          = SmbGetUshort(&pTransactResponse->ParameterCount);
            Format->ParameterOffset         = SmbGetUshort(&pTransactResponse->ParameterOffset);
            Format->ParameterDisplacement   = SmbGetUshort(&pTransactResponse->ParameterDisplacement);

            Format->DataCount         = SmbGetUshort(&pTransactResponse->DataCount);
            Format->DataOffset        = SmbGetUshort(&pTransactResponse->DataOffset);
            Format->DataDisplacement  = SmbGetUshort(&pTransactResponse->DataDisplacement);
        }
        break;

    case SMB_COM_NT_TRANSACT:
    case SMB_COM_NT_TRANSACT_SECONDARY:
        {
            PRESP_NT_TRANSACTION pNtTransactResponse;

            pNtTransactResponse = (PRESP_NT_TRANSACTION)(pTransactResponse);

            Format->TotalParameterCount  = SmbGetUlong(&pNtTransactResponse->TotalParameterCount);
            Format->TotalDataCount = SmbGetUlong(&pNtTransactResponse->TotalDataCount);

            Format->ParameterCount  = SmbGetUlong(&pNtTransactResponse->ParameterCount);
            Format->ParameterOffset = SmbGetUlong(&pNtTransactResponse->ParameterOffset);
            Format->ParameterDisplacement = SmbGetUlong(&pNtTransactResponse->ParameterDisplacement);

            Format->DataCount   = SmbGetUlong(&pNtTransactResponse->DataCount);
            Format->DataOffset  = SmbGetUlong(&pNtTransactResponse->DataOffset);
            Format->DataDisplacement  = SmbGetUlong(&pNtTransactResponse->DataDisplacement);
        }
        break;

    default:
        // Bug Check
        return STATUS_INVALID_NETWORK_RESPONSE;
    }

    //do this here so we can use it as validation criterion
    pTransactExchange->ParameterBytesSeen += Format->ParameterCount;
    pTransactExchange->DataBytesSeen += Format->DataCount;

    return Status;
}

NTSTATUS
ParseTransactResponse(
    IN struct _SMB_TRANSACT_EXCHANGE *pTransactExchange,    // The exchange instance
    IN PSMB_TRANSACT_RESP_FORMAT_DESCRIPTION Format,
    IN ULONG        BytesIndicated,
    IN ULONG        BytesAvailable,
    OUT ULONG       *pBytesTaken,
    IN  PSMB_HEADER pSmbHeader,
    OUT PMDL        *pCopyRequestMdlPointer,
    OUT PULONG      pCopyRequestSize)
/*++

Routine Description:

    This is the recieve indication handling routine for net root construction exchanges

Arguments:

    pTransactExchange - the exchange instance

    BytesIndicated    - the number of bytes indicated

    Bytes Available   - the number of bytes available

    pBytesTaken       - the number of bytes consumed

    pSmbHeader        - the byte buffer

    pCopyRequestMdlPointer - the buffer into which the remaining data is to be copied.

    pCopyRequestSize       - the buffer size.

Return Value:

    RXSTATUS - The return status for the operation
          STATUS_MORE_PROCESSING_REQUIRED -- if a copy of the data needs to be done before
          processing can be completed. This occurs because all the data was not indicated
          STATUS_SUCCESS -- all the data was indicated and it was valid
          STATUS_* -- They indicate an error which would normally leads to the abortion of the
          exchange.

Notes:

    This routine is called at DPC level.

--*/
{
    NTSTATUS Status = STATUS_SUCCESS;

    ULONG ParamBytesInResponse  = 0;
    ULONG ParamOffsetInResponse = 0;
    ULONG DataBytesInResponse   = 0;
    ULONG DataOffsetInResponse  = 0;

    ULONG PaddingLength = 0;

    PMDL  pFirstMdlInCopyDataRequestChain = NULL;
    PMDL  pLastMdlInCopyDataRequestChain = NULL;
    PMDL  pParamMdl = NULL;
    PMDL  pPaddingMdl = NULL;
    PMDL  pDataMdl  = NULL;

    PBYTE pParamStartAddress;
    PBYTE pDataStartAddress;
    PBYTE pSmbBuffer = (PBYTE)pSmbHeader;

    switch (pSmbHeader->Command) {
    case SMB_COM_TRANSACTION2:
    case SMB_COM_TRANSACTION:
    case SMB_COM_TRANSACTION_SECONDARY:
    case SMB_COM_TRANSACTION2_SECONDARY:
        {
            PRESP_TRANSACTION pTransactResponse;

            pTransactResponse = (PRESP_TRANSACTION)(pSmbBuffer + *pBytesTaken);
            *pBytesTaken = *pBytesTaken + sizeof(RESP_TRANSACTION);
        }
        break;
    case SMB_COM_NT_TRANSACT:
    case SMB_COM_NT_TRANSACT_SECONDARY:
        {
            PRESP_NT_TRANSACTION pNtTransactResponse;

            pNtTransactResponse = (PRESP_NT_TRANSACTION)(pSmbBuffer + *pBytesTaken);
            *pBytesTaken = *pBytesTaken + sizeof(RESP_NT_TRANSACTION);
        }
        break;
    default:
        // Bug Check
        ASSERT(!"Valid SMB command in Transaction response");
        return STATUS_INVALID_NETWORK_RESPONSE;
    }

#if 0
    ULONG WordCount;
    ULONG TotalParameterCount;
    ULONG TotalDataCount;
    ULONG ParameterCount;
    ULONG ParameterOffset;
    ULONG ParameterDisplacement;
    ULONG DataCount;
    ULONG DataOffset;
    ULONG DataDisplacement;
    ULONG ByteCount;
    ULONG ApparentMsgLength;
#endif
    ParamBytesInResponse  = Format->ParameterCount;
    ParamOffsetInResponse = Format->ParameterOffset;
    DataBytesInResponse   = Format->DataCount;
    DataOffsetInResponse  = Format->DataOffset;

    if (ParamBytesInResponse > 0) {
        ASSERT(pTransactExchange->pReceiveParamMdl != NULL);
        pParamStartAddress = (PBYTE)MmGetSystemAddressForMdlSafe(pTransactExchange->pReceiveParamMdl,LowPagePriority);

        if (pParamStartAddress == NULL) {
            Status = STATUS_INSUFFICIENT_RESOURCES;
        } else {
            pParamStartAddress += Format->ParameterDisplacement;
        }
    } else {
        pParamStartAddress = NULL;
    }

    if (DataBytesInResponse > 0) {
        ASSERT(pTransactExchange->pReceiveDataMdl != NULL);
        pDataStartAddress  = (PBYTE)MmGetSystemAddressForMdlSafe(pTransactExchange->pReceiveDataMdl,LowPagePriority);

        if (pDataStartAddress == NULL) {
            Status = STATUS_INSUFFICIENT_RESOURCES;
        } else {
            pDataStartAddress  += Format->DataDisplacement;
        }
    } else {
        pDataStartAddress = NULL;
    }

    RxDbgTrace( 0, Dbg, ("ParseTransactResponse: Param Bytes(%ld) Param Offset (%ld) Data Bytes (%ld) Data Offset(%ld)\n",
                        ParamBytesInResponse,
                        ParamOffsetInResponse,
                        DataBytesInResponse,
                        DataOffsetInResponse));

    // If either the param bytes or the data bytes have already been indicated, copy
    // them into the respective buffers and trim the size of the MDL for the copy
    // data request.

    if (ParamOffsetInResponse <= BytesIndicated) {
        *pBytesTaken = ParamOffsetInResponse;
        if (ParamBytesInResponse > 0) {
            ULONG ParamBytesIndicated = MIN(
                                            ParamBytesInResponse,
                                            BytesIndicated - ParamOffsetInResponse);

            RxDbgTrace( 0, Dbg, ("ParseTransactResponse: Param Bytes indicated %ld\n",ParamBytesIndicated));
            RtlCopyMemory(
                pParamStartAddress,
                (pSmbBuffer + ParamOffsetInResponse),
                ParamBytesIndicated);

            *pBytesTaken = *pBytesTaken + ParamBytesIndicated;
            pParamStartAddress += ParamBytesIndicated;
            ParamBytesInResponse -= ParamBytesIndicated;
            ParamOffsetInResponse += ParamBytesIndicated;
            pTransactExchange->ParamBytesReceived  += ParamBytesIndicated;
        }
    }

    if ( (DataOffsetInResponse <= BytesIndicated) &&
         (DataOffsetInResponse > 0) ) {
        *pBytesTaken = DataOffsetInResponse;  //you have to move up EVEN IF NO BYTES!!!!!
        if (DataBytesInResponse > 0) {
            ULONG DataBytesIndicated = MIN(
                                           DataBytesInResponse,
                                           BytesIndicated - DataOffsetInResponse);

            RxDbgTrace( 0, Dbg, ("ParseTransactResponse: Data Bytes indicated %ld\n",DataBytesIndicated));
            RtlCopyMemory(
                pDataStartAddress,
                (pSmbBuffer + DataOffsetInResponse),
                DataBytesIndicated);

            *pBytesTaken = *pBytesTaken + DataBytesIndicated;
            pDataStartAddress += DataBytesIndicated;
            DataBytesInResponse -= DataBytesIndicated;
            DataOffsetInResponse += DataBytesIndicated;
            pTransactExchange->DataBytesReceived  += DataBytesIndicated;
        }
    }

    RxDbgTrace( 0, Dbg, ("ParseTransactResponse: Made it past the copies......... \n"));

    if (ParamBytesInResponse > 0) {
        // There are more param bytes that have not been indicated. Set up an MDL
        // to copy them over.

        RxDbgTrace( 0, Dbg, ("ParseTransactResponse: Posting Copy request for Param Bytes %ld\n",ParamBytesInResponse));
        pParamMdl = RxAllocateMdl(
                        ((PBYTE)MmGetMdlVirtualAddress(pTransactExchange->pReceiveParamMdl)
                        + pTransactExchange->ParamBytesReceived),
                        ParamBytesInResponse);

        if (pParamMdl != NULL) {
            IoBuildPartialMdl(
                pTransactExchange->pReceiveParamMdl,
                pParamMdl,
                ((PBYTE)MmGetMdlVirtualAddress(pTransactExchange->pReceiveParamMdl)
                 + pTransactExchange->ParamBytesReceived),
                ParamBytesInResponse);
            pFirstMdlInCopyDataRequestChain = pParamMdl;
            pLastMdlInCopyDataRequestChain  = pParamMdl;
        } else {
            Status = STATUS_INSUFFICIENT_RESOURCES;
        }

        pTransactExchange->ParamBytesReceived += ParamBytesInResponse;
    }

    if ((Status == RX_MAP_STATUS(SUCCESS)) &&
        (DataBytesInResponse > 0)) {

        RxDbgTrace( 0, Dbg, ("ParseTransactResponse: Posting Copy request for Data Bytes %ld\n",DataBytesInResponse));

        // In certain cases a padding MDL needs to be inserted between the param and data portions
        // of the response to consume the padding bytes sent by the server.
        if ((ParamBytesInResponse > 0) &&
            ((PaddingLength = DataOffsetInResponse -
                           (ParamBytesInResponse + ParamOffsetInResponse)) > 0)) {
            RxDbgTrace( 0, Dbg, ("ParseTransactResponse: Posting Copy request for padding bytes %ld\n",PaddingLength));
            // There are some padding bytes present. Construct an MDL to consume them
            //pPaddingMdl = RxAllocateMdl(&MRxSmb_pPaddingData,PaddingLength);
            ASSERT(!"this doesn't work");
            if (pPaddingMdl != NULL) {
                if (pLastMdlInCopyDataRequestChain != NULL) {
                    pLastMdlInCopyDataRequestChain->Next = pPaddingMdl;
                } else {
                    pFirstMdlInCopyDataRequestChain = pPaddingMdl;
                }
                pLastMdlInCopyDataRequestChain = pPaddingMdl;
            } else {
                Status = STATUS_INSUFFICIENT_RESOURCES;
            }
        }

        // There are more data bytes which have not been indicated. Set up an MDL
        // to copy them over.
        if ((Status == RX_MAP_STATUS(SUCCESS))) {
            if (pTransactExchange->pReceiveDataMdl->ByteCount >= DataBytesInResponse) {
                pDataMdl = RxAllocateMdl(
                               ((PBYTE)MmGetMdlVirtualAddress(pTransactExchange->pReceiveDataMdl)
                                + pTransactExchange->DataBytesReceived),
                               DataBytesInResponse);

                if (pDataMdl != NULL) {
                    IoBuildPartialMdl(
                        pTransactExchange->pReceiveDataMdl,
                        pDataMdl,
                        ((PBYTE)MmGetMdlVirtualAddress(pTransactExchange->pReceiveDataMdl)
                         + pTransactExchange->DataBytesReceived),
                        DataBytesInResponse);

                    if (pLastMdlInCopyDataRequestChain != NULL) {
                        pLastMdlInCopyDataRequestChain->Next = pDataMdl;
                    } else {
                        pFirstMdlInCopyDataRequestChain = pDataMdl;
                    }

                    pLastMdlInCopyDataRequestChain = pDataMdl;
                    pTransactExchange->DataBytesReceived += DataBytesInResponse;
                } else {
                    Status = STATUS_INSUFFICIENT_RESOURCES;
                }
            } else {
                Status = STATUS_INVALID_NETWORK_RESPONSE;
            }
        }
    }

    if ((Status != RX_MAP_STATUS(SUCCESS))) {
        if (pDataMdl != NULL) {
            IoFreeMdl(pDataMdl);
        }

        if (pPaddingMdl != NULL) {
            IoFreeMdl(pPaddingMdl);
        }

        if (pParamMdl != NULL) {
            IoFreeMdl(pParamMdl);
        }
    } else {
        if (pFirstMdlInCopyDataRequestChain != NULL) {
            ULONG MdlLength = ParamBytesInResponse+PaddingLength+DataBytesInResponse;
            *pCopyRequestMdlPointer = pFirstMdlInCopyDataRequestChain;
            *pCopyRequestSize = MdlLength;
            RxDbgTrace( 0, Dbg, ("ParseTransactResponse: final mdl and copy size %08lx %08lx(%ld)\n",
                              pFirstMdlInCopyDataRequestChain,MdlLength,MdlLength));
            IF_DEBUG {
                PMDL imdl = pFirstMdlInCopyDataRequestChain;
                ULONG mdllength = MdlLength;
                mdllength -= MmGetMdlByteCount(imdl);
                for (;;) {
                    if (!(imdl=imdl->Next)) break;
                    mdllength -= MmGetMdlByteCount(imdl);
                }
                ASSERT(mdllength==0);
            }

            InterlockedIncrement(&pTransactExchange->PendingCopyRequests);
            Status = STATUS_MORE_PROCESSING_REQUIRED;
        }

        if ((pTransactExchange->ParamBytesReceived < pTransactExchange->ReceiveParamBufferSize) ||
            (pTransactExchange->DataBytesReceived  < pTransactExchange->ReceiveDataBufferSize)) {
            NTSTATUS ReceiveStatus;

            // The exchange has been successfully completed. Finalize it.
            RxDbgTrace(0,Dbg,("ParseTransactResponse: Register for more responses\n"));
            ReceiveStatus = SmbCeReceive((PSMB_EXCHANGE)pTransactExchange);
            if (ReceiveStatus != STATUS_SUCCESS) {
                // There was an error in registering the receive. Abandon the
                // transaction.
                Status = ReceiveStatus;
            }
        }
    }

    return Status;

    UNREFERENCED_PARAMETER(BytesAvailable);

}

#if DBG
ULONG SmbSendBadSecondary = 0;
#endif
NTSTATUS
SendSecondaryRequests(PVOID pContext)
/*++

Routine Description:

    This routine sends all the secondary requests associated with the transaction

Arguments:

    pTransactExchange - the exchange instance

Return Value:

    RXSTATUS - The return status for the operation

Notes:

--*/
{
    PSMB_EXCHANGE pExchange = (PSMB_EXCHANGE)pContext;
    PSMB_TRANSACT_EXCHANGE pTransactExchange = (PSMB_TRANSACT_EXCHANGE)pExchange;

    NTSTATUS Status = STATUS_SUCCESS;

    ULONG MaximumSmbBufferSize;

    // The MDL's used in sending the primary request associated with the TRANSACT SMB
    PMDL  pPartialDataMdl         = NULL;
    PMDL  pPartialParamMdl        = NULL;
    PMDL  pPaddingMdl             = NULL;
    PMDL  pSecondaryRequestSmbMdl = NULL;
    PMDL  pLastMdlInChain         = NULL;

    ULONG SecondaryRequestSmbSize = 0;
    ULONG SmbLength;
    ULONG PaddingLength;

    ULONG ParamOffset,ParamDisplacement;
    ULONG DataOffset,DataDisplacement;
    ULONG ByteCountOffset;
    USHORT ByteCount;
    PUSHORT pByteCount;

    ULONG ParamBytesToBeSent;        // Param bytes to be sent per request
    ULONG DataBytesToBeSent;         // data bytes to be sent per request
    ULONG SendParamBufferSize;       // Total param bytes to be sent in secondary requests
    ULONG SendDataBufferSize;        // Total data bytes to be sent in secondary requests
    PBYTE pSendParamStartAddress = NULL;
    PBYTE pSendDataStartAddress  = NULL;
    PBYTE pOriginalParamBuffer = NULL;
    PBYTE pOriginalDataBuffer = NULL;
    ULONG TotalParamBytes,TotalDataBytes;

    BOOLEAN ParamPartialMdlAlreadyUsed = FALSE;
    BOOLEAN DataPartialMdlAlreadyUsed = FALSE;

    PVOID pActualSecondaryRequestSmbHeader = NULL;
    PSMB_HEADER pSecondaryRequestSmbHeader = NULL;

    PAGED_CODE();

    ASSERT(pTransactExchange->State == TRANSACT_EXCHANGE_RECEIVED_INTERIM_RESPONSE);


    TotalParamBytes = pTransactExchange->SendParamBufferSize;
    SendParamBufferSize = TotalParamBytes - pTransactExchange->ParamBytesSent;

    TotalDataBytes = pTransactExchange->SendDataBufferSize;
    SendDataBufferSize = TotalDataBytes - pTransactExchange->DataBytesSent;

    ASSERT((SendParamBufferSize > 0) || (SendDataBufferSize > 0));

    switch (pTransactExchange->TransactSmbCommand) {
    case SMB_COM_TRANSACTION:
        SecondaryRequestSmbSize = sizeof(SMB_HEADER) +
            FIELD_OFFSET(REQ_TRANSACTION_SECONDARY,Buffer);
        break;

    case SMB_COM_TRANSACTION2:
        //CODE.IMPROVEMENT.ASHAMED smb.h should containa REQ_TRANSACTION2_SECONDARY instead
        //        of this bogus comment about how the server can ignore it
        SecondaryRequestSmbSize = sizeof(SMB_HEADER) +
            FIELD_OFFSET(REQ_TRANSACTION_SECONDARY,Buffer)
            + sizeof(USHORT);  //add in the extra word
        break;

    case SMB_COM_NT_TRANSACT:
        SecondaryRequestSmbSize = sizeof(SMB_HEADER) +
            FIELD_OFFSET(REQ_NT_TRANSACTION_SECONDARY,Buffer);
        break;

    default:
        ASSERT(!"Valid Smb Command in transaction exchange");
        Status = STATUS_TRANSACTION_ABORTED;
    }

    SecondaryRequestSmbSize = QuadAlign(SecondaryRequestSmbSize); //pad to quadword boundary

    //CODE.IMPROVEMENT we could overallocate here....sometimes the copy would be faster
    pActualSecondaryRequestSmbHeader = (PSMB_HEADER)
                                 RxAllocatePoolWithTag(
                                     NonPagedPool,
                                     SecondaryRequestSmbSize + TRANSPORT_HEADER_SIZE,
                                     MRXSMB_XACT_POOLTAG);

    if ((Status == RX_MAP_STATUS(SUCCESS)) && pActualSecondaryRequestSmbHeader != NULL) {

        (PCHAR) pSecondaryRequestSmbHeader =
            (PCHAR) pActualSecondaryRequestSmbHeader + TRANSPORT_HEADER_SIZE;

        // Initialize the SMB header  ...

        ASSERT(
                 ((SMB_COM_TRANSACTION+1) == SMB_COM_TRANSACTION_SECONDARY)
               &&((SMB_COM_TRANSACTION2+1)== SMB_COM_TRANSACTION2_SECONDARY)
               &&((SMB_COM_NT_TRANSACT+1) == SMB_COM_NT_TRANSACT_SECONDARY)
             );

        Status = SmbTransactBuildHeader(
                     pTransactExchange,                        // the exchange instance
                     (UCHAR)(pTransactExchange->TransactSmbCommand+1), // the SMB command ..see the asserts above
                     pSecondaryRequestSmbHeader);              // the SMB buffer

        RxDbgTrace( 0, Dbg, ("SendSecondaryRequests: SmbCeBuildSmbHeader returned %lx\n",Status));
    } else {
        Status = STATUS_INSUFFICIENT_RESOURCES;
    }

    if ((Status == RX_MAP_STATUS(SUCCESS))) {
        MaximumSmbBufferSize = pTransactExchange->MaximumTransmitSmbBufferSize;

        // Ensure that the MDL's have been probed & locked. The new MDL's have been allocated.
        // The partial MDL's are allocated to be large enough to span the maximum buffer
        // length possible.

        // Initialize the data related MDL's for the secondary request
        if (SendDataBufferSize > 0) {
            RxDbgTrace( 0, Dbg, ("SendSecondaryRequests: Data Bytes remaining %ld\n",SendDataBufferSize));

            pOriginalDataBuffer = (PBYTE)MmGetMdlVirtualAddress(pTransactExchange->pSendDataMdl);
            pSendDataStartAddress = pOriginalDataBuffer + pTransactExchange->DataBytesSent;

            pPartialDataMdl = RxAllocateMdl(
                                  0,
                                  (MIN(pTransactExchange->SendDataBufferSize,
                                       MaximumSmbBufferSize) +
                                       PAGE_SIZE - 1));

            if (pPartialDataMdl == NULL) {
                Status = STATUS_INSUFFICIENT_RESOURCES;
            }
        }

        // Initialize the parameter related MDL's for the secondary request
        if ((SendParamBufferSize > 0) && (Status == RX_MAP_STATUS(SUCCESS))) {
            RxDbgTrace( 0, Dbg, ("SendSecondaryRequests: Param Bytes remaining %ld\n",SendParamBufferSize));
            pOriginalParamBuffer = (PBYTE)MmGetMdlVirtualAddress(pTransactExchange->pSendParamMdl);
            pSendParamStartAddress = pOriginalParamBuffer + pTransactExchange->ParamBytesSent;

            pPartialParamMdl  = RxAllocateMdl(
                                    0,
                                    (MIN(pTransactExchange->SendParamBufferSize,
                                         MaximumSmbBufferSize) +
                                         PAGE_SIZE - 1));

            //CODE.IMPROVEMENT we shouldn't allocate this if datasize==0
            pPaddingMdl       = RxAllocateMdl(0,(sizeof(DWORD) + PAGE_SIZE - 1));

            if ((pPartialParamMdl == NULL) ||
                (pPaddingMdl == NULL)) {
                RxDbgTrace( 0, Dbg, ("SendSecondaryRequests: Error allocating param MDLS\n"));
                Status = STATUS_INSUFFICIENT_RESOURCES;
            }
        }

        // Initialize the secondary request SMB MDL
        if ((Status == RX_MAP_STATUS(SUCCESS))) {

            RxAllocateHeaderMdl(
                pSecondaryRequestSmbHeader,
                SecondaryRequestSmbSize,
                pSecondaryRequestSmbMdl
                );

            if (pSecondaryRequestSmbMdl != NULL) {

                RxProbeAndLockHeaderPages(
                    pSecondaryRequestSmbMdl,
                    KernelMode,
                    IoModifyAccess,
                    Status);

                if ((Status != RX_MAP_STATUS(SUCCESS))) {
                    IoFreeMdl(pSecondaryRequestSmbMdl);
                    pSecondaryRequestSmbMdl = NULL;
                } else {
                    if (MmGetSystemAddressForMdlSafe(pSecondaryRequestSmbMdl,LowPagePriority) == NULL) { //map it
                        Status = STATUS_INSUFFICIENT_RESOURCES;
                    }
                }
            } else {
                RxDbgTrace( 0, Dbg, ("SendSecondaryRequests: Error allocating 2ndsmb MDL\n"));
                Status = STATUS_INSUFFICIENT_RESOURCES;
            }
        }
    }

    while ((Status == RX_MAP_STATUS(SUCCESS)) &&
           ((SendParamBufferSize > 0) || (SendDataBufferSize > 0))) {
        PaddingLength      = 0;
        DataBytesToBeSent  = 0;
        ParamBytesToBeSent = 0;
        ParamDisplacement = 0;
        pLastMdlInChain    = pSecondaryRequestSmbMdl;

        ParamOffset = DataOffset = SecondaryRequestSmbSize;

        ParamBytesToBeSent = MIN((MaximumSmbBufferSize - ParamOffset),
                               SendParamBufferSize);

        if (ParamBytesToBeSent > 0) {
            // Form a MDL for the portion of the parameter buffer being transmitted
            if (ParamPartialMdlAlreadyUsed) {
                MmPrepareMdlForReuse(pPartialParamMdl);
            }

            ParamPartialMdlAlreadyUsed = TRUE;
            IoBuildPartialMdl(
                pTransactExchange->pSendParamMdl,
                pPartialParamMdl,
                pSendParamStartAddress,
                ParamBytesToBeSent);

            ParamDisplacement  = (ULONG)(pSendParamStartAddress - pOriginalParamBuffer);
            pSendParamStartAddress  += ParamBytesToBeSent;
            SendParamBufferSize     -= ParamBytesToBeSent;
            DataOffset              += QuadAlign(ParamBytesToBeSent);

            pLastMdlInChain->Next = pPartialParamMdl;
            pLastMdlInChain = pPartialParamMdl;
        } else {
            // don't do this! the padding stuff uses it. you can set it later
            // ParamOffset = 0;
        }

        if ((DataOffset < MaximumSmbBufferSize) && (SendDataBufferSize > 0) ) {
            // There is room for data bytes to be sent
            // Check if we need a padding MDL ....
            PaddingLength = DataOffset - (ParamOffset + ParamBytesToBeSent);

            if (PaddingLength > 0) {
                RxDbgTrace( 0, Dbg, ("SmbCeTransactExchangeStart: Padding Length %ld\n",PaddingLength));
                RxBuildPaddingPartialMdl(pPaddingMdl,PaddingLength);
                pLastMdlInChain->Next = pPaddingMdl;
                pLastMdlInChain = pPaddingMdl;
            }

            // Link the data buffer or portions of it if the size constraints are satisfied
            DataBytesToBeSent = MIN((MaximumSmbBufferSize - DataOffset),
                                  SendDataBufferSize);
            ASSERT (DataBytesToBeSent > 0);

            // Form a MDL for the portions of the data buffer being sent
            if (DataPartialMdlAlreadyUsed) {
                MmPrepareMdlForReuse(pPartialDataMdl);
            }

            DataPartialMdlAlreadyUsed = TRUE;
            IoBuildPartialMdl(
                pTransactExchange->pSendDataMdl,
                pPartialDataMdl,
                pSendDataStartAddress,
                DataBytesToBeSent);

            //  chain the data MDL
            pLastMdlInChain->Next = pPartialDataMdl;
            pLastMdlInChain = pPartialDataMdl;

            DataDisplacement  = (ULONG)(pSendDataStartAddress - pOriginalDataBuffer);
            pSendDataStartAddress   += DataBytesToBeSent;
            SendDataBufferSize      -= DataBytesToBeSent;
        } else {
            DataOffset = DataDisplacement  = 0;
            DbgDoit(if (SmbSendBadSecondary){DataOffset = QuadAlign(ParamOffset + ParamBytesToBeSent);});
        }

        if (ParamBytesToBeSent == 0) {
            ParamOffset = 0;
        }

        RxDbgTrace( 0, Dbg, ("SendSecondaryRequests: Secondary Request Param(%ld) padding(%ld) Data(%ld)\n",
                            ParamBytesToBeSent,
                            PaddingLength,
                            DataBytesToBeSent));
        RxDbgTrace( 0, Dbg, ("SendSecondaryRequests:  ParamO(%ld) DataO(%ld)\n",ParamOffset,DataOffset));
        RxDbgTrace( 0, Dbg, ("SendSecondaryRequests:  ParamD(%ld) DataD(%ld)\n",ParamDisplacement,DataDisplacement));
        RxDbgTrace( 0, Dbg, ("SendSecondaryRequests:  TotParam(%ld) TotData(%ld)\n",TotalParamBytes,TotalDataBytes));

        // Update the secondary request buffer with the final sizes of the data/parameter etc.
        switch (pTransactExchange->TransactSmbCommand) {
        case SMB_COM_TRANSACTION:
        case SMB_COM_TRANSACTION2:
            {
                PREQ_TRANSACTION_SECONDARY pTransactRequest;

                //ASSERT(!"this has not been tested");

                pTransactRequest = (PREQ_TRANSACTION_SECONDARY)(pSecondaryRequestSmbHeader + 1);

                pTransactRequest->WordCount = 8;                                     // Count of parameter words = 8
                SmbPutUshort(&pTransactRequest->TotalParameterCount, (USHORT)TotalParamBytes); // Total parameter bytes being sent
                SmbPutUshort(&pTransactRequest->TotalDataCount, (USHORT)TotalDataBytes);      // Total data bytes being sent
                SmbPutUshort(&pTransactRequest->ParameterCount, (USHORT)ParamBytesToBeSent);   // Parameter bytes sent this buffer
                SmbPutUshort(&pTransactRequest->ParameterOffset, (USHORT)ParamOffset);          // Offset (from header start) to params
                SmbPutUshort(&pTransactRequest->ParameterDisplacement, (USHORT)ParamDisplacement);    // Displacement of these param bytes
                SmbPutUshort(&pTransactRequest->DataCount, (USHORT)DataBytesToBeSent);   // Parameter bytes sent this buffer
                SmbPutUshort(&pTransactRequest->DataOffset, (USHORT)DataOffset);               // Offset (from header start) to Datas
                SmbPutUshort(&pTransactRequest->DataDisplacement, (USHORT)DataDisplacement);   // Displacement of these Data bytes
                ByteCountOffset = FIELD_OFFSET(REQ_TRANSACTION_SECONDARY,ByteCount);
                if (pTransactExchange->TransactSmbCommand == SMB_COM_TRANSACTION2 ) {
                    //see CODE.IMPROVEMENT.ASHAMED above.......
                    ByteCountOffset += sizeof(USHORT);
                    pTransactRequest->WordCount++;  //one extra word
                    SmbPutUshort((&pTransactRequest->DataDisplacement)+1, 0); //the +1 is to move up 1 USHORT
                }
            }
            break;

        case SMB_COM_NT_TRANSACT:
            {
                PREQ_NT_TRANSACTION_SECONDARY pNtTransactRequest;

                pNtTransactRequest= (PREQ_NT_TRANSACTION_SECONDARY)(pSecondaryRequestSmbHeader + 1);

                //CODE.IMPROVEMENT this should be stufferized.....the whole thing should be.
                //                 (6/15 there are unimplemented things in the stuffer that'd make it hard

                //CODE.IMPROVEMENT move the constant things to the top
                //CODE.IMPROVEMENT you don't need the macros here because things are alinged.....put in asserts
                //                 actually, use the aligned stuff
                pNtTransactRequest->WordCount = 18;                                     // Count of parameter words = 18
                pNtTransactRequest->Reserved1 = 0;                                      // MBZ
                SmbPutUshort(&pNtTransactRequest->Reserved2, 0);                        // MBZ
                SmbPutUlong(&pNtTransactRequest->TotalParameterCount, TotalParamBytes); // Total parameter bytes being sent
                SmbPutUlong(&pNtTransactRequest->TotalDataCount, TotalDataBytes);      // Total data bytes being sent
                SmbPutUlong(&pNtTransactRequest->ParameterCount, ParamBytesToBeSent);   // Parameter bytes sent this buffer
                SmbPutUlong(&pNtTransactRequest->ParameterOffset, ParamOffset);          // Offset (from header start) to params
                SmbPutUlong(&pNtTransactRequest->ParameterDisplacement, ParamDisplacement);    // Displacement of these param bytes
                SmbPutUlong(&pNtTransactRequest->DataCount, DataBytesToBeSent);   // Parameter bytes sent this buffer
                SmbPutUlong(&pNtTransactRequest->DataOffset, DataOffset);               // Offset (from header start) to Datas
                SmbPutUlong(&pNtTransactRequest->DataDisplacement, DataDisplacement);   // Displacement of these Data bytes
                pNtTransactRequest->Reserved3 = 0;                                      // MBZ

                ByteCountOffset = FIELD_OFFSET(REQ_NT_TRANSACTION_SECONDARY,ByteCount);
            }
            break;

        default:
            ASSERT(!"Valid Smb Command for initiating Transaction");
            Status = STATUS_INVALID_PARAMETER;
            break;
        }

        // Send the secondary SMB
        SmbLength = SecondaryRequestSmbSize +
                    ParamBytesToBeSent +
                    PaddingLength +
                    DataBytesToBeSent;

        ByteCount = (USHORT)(SmbLength-(sizeof(SMB_HEADER)+ByteCountOffset+sizeof(USHORT)));
        pByteCount = (PUSHORT)((PBYTE)pSecondaryRequestSmbHeader+sizeof(SMB_HEADER)+ByteCountOffset);
        SmbPutUshort(pByteCount,ByteCount);

        RxDbgTrace( 0, Dbg, ("SendSecondaryRequests: len %d bytecount %d(%x)\n", SmbLength, ByteCount, ByteCount));
        RxDbgTrace( 0, Dbg, ("SendSecondaryRequests: msgmdl=%08lx\n", pSecondaryRequestSmbHeader));

        RxLog(("2nd: %lx %lx %lx %lx %lx %lx",ParamOffset,ParamDisplacement,TotalParamBytes,DataOffset,DataDisplacement,TotalDataBytes));
        RxLog(("2nd:: %lx %lx",ByteCount,SmbLength));
        SmbLog(LOG,
               SendSecondaryRequests,
               LOGULONG(ParamOffset)
               LOGULONG(ParamDisplacement)
               LOGULONG(TotalParamBytes)
               LOGULONG(DataOffset)
               LOGULONG(DataDisplacement)
               LOGULONG(TotalDataBytes)
               LOGXSHORT(ByteCount)
               LOGULONG(SmbLength));

        Status = SmbCeSend(
                     pExchange,
                     RXCE_SEND_SYNCHRONOUS,
                     pSecondaryRequestSmbMdl,
                     SmbLength);

        RxDbgTrace( 0, Dbg, ("SendSecondaryRequests: SmbCeSend returned %lx\n",Status));
        if ((Status != RX_MAP_STATUS(PENDING)) && (Status != RX_MAP_STATUS(SUCCESS))) {
            RxDbgTrace( 0, Dbg, ("SendSecondaryRequests: SmbCeSend returned bad status %lx\n",Status));
            //here we should just get out
            goto FINALLY;    //yes we cold have said break....but that's not what we're doing
        } else {
            Status = RX_MAP_STATUS(SUCCESS);
        }
    }

FINALLY:
    if (pPartialDataMdl != NULL) {
        IoFreeMdl(pPartialDataMdl);
    }

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

    if (pPartialParamMdl != NULL) {
        IoFreeMdl(pPartialParamMdl);
    }

    if (pPaddingMdl != NULL) {
        IoFreeMdl(pPaddingMdl);
    }

    if (pSecondaryRequestSmbMdl != NULL) {
        RxUnlockHeaderPages(pSecondaryRequestSmbMdl);
        IoFreeMdl(pSecondaryRequestSmbMdl);
    }

    //we always finalize......but we only set the status if there's an error or
    //                        we expect no response
    if ((Status != RX_MAP_STATUS(SUCCESS)) || (pTransactExchange->Flags & SMB_TRANSACTION_NO_RESPONSE )) {
        pExchange->Status = Status;

        if (!(pTransactExchange->Flags & SMB_TRANSACTION_NO_RESPONSE)) {
            SmbCeDecrementPendingReceiveOperations(pExchange);
        }
    }

    SmbCeDecrementPendingLocalOperationsAndFinalize(pExchange);

    return Status;
}




SMB_EXCHANGE_DISPATCH_VECTOR
TransactExchangeDispatch = {
                            SmbTransactExchangeStart,
                            SmbTransactExchangeReceive,
                            SmbTransactExchangeCopyDataHandler,
                            NULL,                                  // SmbTransactExchangeSendCallbackHandler
                            SmbTransactExchangeFinalize,
                            NULL
                           };



#ifndef RX_NO_DBGFIELD_HLPRS

#define DECLARE_FIELD_HLPR(x) ULONG SmbPseTxeField_##x = FIELD_OFFSET(SMB_TRANSACT_EXCHANGE,x);
#define DECLARE_FIELD_HLPR2(x,y) ULONG SmbPseTxeField_##x##y = FIELD_OFFSET(SMB_TRANSACT_EXCHANGE,x.y);

DECLARE_FIELD_HLPR(RxContext);
DECLARE_FIELD_HLPR(ReferenceCount);
DECLARE_FIELD_HLPR(State);
DECLARE_FIELD_HLPR(pSendDataMdl);
DECLARE_FIELD_HLPR(pReceiveDataMdl);
DECLARE_FIELD_HLPR(pSendParamMdl);
DECLARE_FIELD_HLPR(pReceiveParamMdl);
DECLARE_FIELD_HLPR(pSendSetupMdl);
DECLARE_FIELD_HLPR(pReceiveSetupMdl);
DECLARE_FIELD_HLPR(PrimaryRequestSmbSize);
DECLARE_FIELD_HLPR(SmbCommand);
DECLARE_FIELD_HLPR(NtTransactFunction);
DECLARE_FIELD_HLPR(Flags);
DECLARE_FIELD_HLPR(Fid);
#endif


