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

Module Name:

    smbcxchng.c

Abstract:

    This is the include file that implements the SMB_*_EXCHANGE creation, deletion and
    dispatch routines.

Notes:


--*/

#include "precomp.h"
#pragma hdrstop


ULONG SmbCeTraceExchangeReferenceCount = 0;

RXDT_DefineCategory(SMBXCHNG);
#define Dbg        (DEBUG_TRACE_SMBXCHNG)

// The exchange engine in the mini redirector requires to maintain enough state
// to ensure that all the active exchanges are completed correctly when a shut down
// occurs. Since the exchanges can be finalized by different threads, including
// posted completions the exchange engine on startup initializes an event upon startup
// which is subsequently used to signal the terminating condition.
//
// The count of active changes has to be tracked continously and the signalling
// of the event depends upon the number of active exchanges reaching the count of
// zero and the exchange engine being in a stopped state.

SMBCE_STARTSTOP_CONTEXT SmbCeStartStopContext;

NTSTATUS
MRxSmbInitializeSmbCe()
{
    KeInitializeEvent(
        &SmbCeStartStopContext.StopEvent,
        NotificationEvent,
        FALSE);

    SmbCeStartStopContext.ActiveExchanges = 0;
    SmbCeStartStopContext.State = SMBCE_STARTED;

    return STATUS_SUCCESS;
}

NTSTATUS
MRxSmbTearDownSmbCe()
{
    BOOLEAN fWait;

    if (SmbCeStartStopContext.State == SMBCE_STARTED) {
        SmbCeAcquireSpinLock();
        SmbCeStartStopContext.State = SMBCE_STOPPED;
        fWait = (SmbCeStartStopContext.ActiveExchanges > 0);
        SmbCeReleaseSpinLock();

        if (fWait) {
            KeWaitForSingleObject(
                &SmbCeStartStopContext.StopEvent,
                Executive,
                KernelMode,
                FALSE,
                NULL);
        }
    }

    return STATUS_SUCCESS;
}

NTSTATUS
SmbCeIncrementActiveExchangeCount()
{
    NTSTATUS Status = STATUS_SUCCESS;

    SmbCeAcquireSpinLock();
    if (SmbCeStartStopContext.State != SMBCE_STARTED) {
        Status = STATUS_UNSUCCESSFUL;
    } else {
        InterlockedIncrement(&SmbCeStartStopContext.ActiveExchanges);
    }
    SmbCeReleaseSpinLock();

    return Status;
}

VOID
SmbCeDecrementActiveExchangeCount()
{
    LONG FinalRefCount;

    ASSERT(SmbCeStartStopContext.ActiveExchanges > 0);
    if (InterlockedDecrement(&SmbCeStartStopContext.ActiveExchanges) == 0) {
        SmbCeAcquireSpinLock();
        if (SmbCeStartStopContext.State == SMBCE_STOPPED) {
            KeSetEvent(&SmbCeStartStopContext.StopEvent,0,FALSE);
        }
        SmbCeReleaseSpinLock();
    }
}


NTSTATUS
SmbCeReferenceServer(
    PSMB_EXCHANGE  pExchange)
/*++

Routine Description:

   This routine initializes the server associated with an exchange.

Arguments:

    pExchange  - the exchange to be initialized.

Return Value:

    RXSTATUS - The return status for the operation

Notes:

    The initiation of an exchange proceeds in multiple steps. The first step involves
    referencing the corresponding server,session and netroot entries. Subsequently the
    exchange is placed in a SMB_EXCHANGE_START state and the exchange is dispatched to the
    Start method. The act of referencing the session or the net root may suspend the exchange.

    The session and net roots are aliased entities, i.e., there is more then one reference
    to it. It is conceivable that the construction is in progress when a reference is made.
    In such cases the exchange is suspended and resumed when the construction is complete.

    On some transports a reconnect is possible without having to tear down an existing
    connection, i.e. attempting to send a packet reestablishes the connection at the
    lower level. Since this is not supported by all the transports ( with the exception
    of TCP/IP) the reference server entry initiates this process by tearing down the
    existing transport and reinitialising it.

--*/
{
    NTSTATUS Status = STATUS_SUCCESS;

    PSMBCEDB_SERVER_ENTRY pServerEntry;
    BOOLEAN ResourceAcquired;

    pServerEntry = pExchange->SmbCeContext.pServerEntry;

    ASSERT(pExchange->SmbCeState == SMBCE_EXCHANGE_INITIALIZATION_START);

    if (pServerEntry->Header.State != SMBCEDB_ACTIVE) {
        if  (pExchange->SmbCeFlags & SMBCE_EXCHANGE_ATTEMPT_RECONNECTS) {
            // Acquire the resource
            SmbCeAcquireResource();
            ResourceAcquired = TRUE;

            switch (pServerEntry->Header.State) {
            case SMBCEDB_INVALID:
                {
                    SMBCEDB_OBJECT_STATE State;

                    // Assume the worst case. if everything goes well the ServerStatus will
                    // be updated as a result of parsing the negotiate response.

                    pServerEntry->ServerStatus = STATUS_CONNECTION_DISCONNECTED;
                    SmbCeUpdateServerEntryState(
                        pServerEntry,
                        SMBCEDB_CONSTRUCTION_IN_PROGRESS);

                    // release the resource for the server entry

                    ResourceAcquired = FALSE;
                    SmbCeReleaseResource();

                    // Initialize the transport associated with the server

                    Status = SmbCeInitializeServerTransport(pServerEntry);

                    if (Status == STATUS_SUCCESS) {
                        Status = SmbCeNegotiate(
                                     pServerEntry,
                                     (PMRX_SRV_CALL)pExchange->SmbCeContext.pVNetRoot->pNetRoot->pSrvCall);
                    }

                    if (Status != STATUS_SUCCESS) {

                        // Either the transport initialization failed or the NEGOTIATE
                        // SMB could not be sent ....

                        SmbCeUpdateServerEntryState(pServerEntry,SMBCEDB_MARKED_FOR_DELETION);
                        SmbCeCompleteServerEntryInitialization(pServerEntry);
                        InterlockedIncrement(&MRxIfsStatistics.Reconnects);
                    }
                }
                break;

            case SMBCEDB_CONSTRUCTION_IN_PROGRESS :
                {
                    PSMBCEDB_REQUEST_ENTRY pRequestEntry;

                    pRequestEntry = (PSMBCEDB_REQUEST_ENTRY)
                                    SmbMmAllocateObject(SMBCEDB_OT_REQUEST);
                    if (pRequestEntry != NULL) {
                        // Enqueue the request entry.
                        pRequestEntry->ReconnectRequest.Type      = RECONNECT_REQUEST;
                        pRequestEntry->ReconnectRequest.pExchange = pExchange;
                        SmbCeAddRequestEntry(&pServerEntry->OutstandingRequests,pRequestEntry);
                        Status = STATUS_PENDING;
                    } else {
                        Status = STATUS_INSUFFICIENT_RESOURCES;
                    }
                }
                break;

            default :
                Status = STATUS_CONNECTION_DISCONNECTED;
                break;
            }

            if (ResourceAcquired) {
                SmbCeReleaseResource();
            }
        } else {
            Status = STATUS_CONNECTION_DISCONNECTED;
        }
    }

    if ((Status == STATUS_SUCCESS) || (Status == STATUS_PENDING)) {
        pExchange->SmbCeState = SMBCE_EXCHANGE_SERVER_INITIALIZED;
    }

    return Status;
}

NTSTATUS
SmbCeReferenceSession(
    PSMB_EXCHANGE   pExchange)
/*++

Routine Description:

    This routine initializes the session associated with an exchange.

Arguments:

    pExchange  - the exchange to be initialized.

Return Value:

    RXSTATUS - The return status for the operation

Notes:

    The initiation of an exchange proceeds in multiple steps. The first step involves
    referencing the corresponding server,session and netroot entries. Subsequently the
    exchange is placed in a SMB_EXCHANGE_START state and the exchange is dispatched to the
    Start method. The act of referencing the session or the net root may suspend the exchange.

    The session and net roots are aliased entities, i.e., there is more then one reference
    to it. It is conceivable that the construction is in progress when a reference is made.
    In such cases the exchange is suspended and resumed when the construction is complete.
--*/
{
    NTSTATUS Status;
    BOOLEAN  fReestablishSession;

    PSMBCEDB_SERVER_ENTRY  pServerEntry;
    PSMBCEDB_SESSION_ENTRY pSessionEntry;

    pServerEntry = pExchange->SmbCeContext.pServerEntry;
    pSessionEntry = pExchange->SmbCeContext.pSessionEntry;

    ASSERT(pSessionEntry != NULL);
    ASSERT(pServerEntry->Header.ObjectType == SMBCEDB_OT_SERVER);
    ASSERT(pExchange->SmbCeState == SMBCE_EXCHANGE_SERVER_INITIALIZED);
    ASSERT(SmbCeGetServerType(pServerEntry) == SMBCEDB_FILE_SERVER);

    // Acquire the resource
    SmbCeAcquireResource();

    Status = STATUS_USER_SESSION_DELETED;
    fReestablishSession = BooleanFlagOn(pExchange->SmbCeFlags,SMBCE_EXCHANGE_ATTEMPT_RECONNECTS);

    switch (pSessionEntry->Header.State) {
    case SMBCEDB_ACTIVE:
        Status = STATUS_SUCCESS;
        break;

    case SMBCEDB_INVALID:

        if (!fReestablishSession) {
           break;
        }

        RxDbgTrace( 0, Dbg, ("SmbCeReferenceSession: Reestablishing session\n"));
        pSessionEntry->Session.UserId = 0;
        // fall thru ...

    case SMBCEDB_START_CONSTRUCTION:
        ASSERT(SmbCeGetServerType(pServerEntry) == SMBCEDB_FILE_SERVER);
        pExchange->SmbCeFlags |= SMBCE_EXCHANGE_SESSION_CONSTRUCTOR;
        pSessionEntry->Header.State = SMBCEDB_CONSTRUCTION_IN_PROGRESS;
        Status = STATUS_SUCCESS;
        break;

    case SMBCEDB_CONSTRUCTION_IN_PROGRESS:
        if (fReestablishSession) {
            // The construction of the session is already in progress ....
            // Queue up the request to resume this exchange when the session
            // construction is complete.

            PSMBCEDB_REQUEST_ENTRY pRequestEntry;

            ASSERT(SmbCeGetServerType(pServerEntry) == SMBCEDB_FILE_SERVER);

            pRequestEntry = (PSMBCEDB_REQUEST_ENTRY)
                             SmbMmAllocateObject(SMBCEDB_OT_REQUEST);

            if (pRequestEntry != NULL) {
                pRequestEntry->Request.pExchange = pExchange;

                SmbCeAddRequestEntry(&pSessionEntry->Requests,pRequestEntry);
                Status = STATUS_PENDING;
            } else {
                Status = STATUS_INSUFFICIENT_RESOURCES;
            }
        }
        break;

    case SMBCEDB_MARKED_FOR_DELETION:
        Status = STATUS_USER_SESSION_DELETED;
        break;

    default:
        ASSERT(!"Valid Session State, SmbCe database corrupt");
        Status = STATUS_USER_SESSION_DELETED;
    }

    // Release the server resource ...

    SmbCeReleaseResource();


    if ((Status == STATUS_SUCCESS) || (Status == STATUS_PENDING)) {
        pExchange->SmbCeState = SMBCE_EXCHANGE_SESSION_INITIALIZED;
    }

    return Status;
}

NTSTATUS
SmbCeReferenceNetRoot(
    PSMB_EXCHANGE   pExchange)
/*++

Routine Description:

    This routine initializes the net root associated with an exchange.

Arguments:

    pExchange  - the exchange to be initialized.

Return Value:

    RXSTATUS - The return status for the operation

Notes:

    The initiation of an exchange proceeds in multiple steps. The first step involves
    referencing the corresponding server,session and netroot entries. Subsequently the
    exchange is placed in a SMB_EXCHANGE_START state and the exchange is dispatched to the
    Start method. The act of referencing the session or the net root may suspend the exchange.

    The session and net roots are aliased entities, i.e., there is more then one reference
    to it. It is conceivable that the construction is in progress when a reference is made.
    In such cases the exchange is suspended and resumed when the construction is complete.
--*/
{
    NTSTATUS Status;
    BOOLEAN  fReconnectNetRoot;

    PSMBCEDB_SERVER_ENTRY   pServerEntry;
    PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry;

    pServerEntry = pExchange->SmbCeContext.pServerEntry;
    pNetRootEntry = pExchange->SmbCeContext.pNetRootEntry;

    ASSERT(pNetRootEntry->Header.ObjectType == SMBCEDB_OT_NETROOT);
    ASSERT(pServerEntry->Header.ObjectType == SMBCEDB_OT_SERVER);
    ASSERT(pExchange->SmbCeState == SMBCE_EXCHANGE_SESSION_INITIALIZED);

    // Acquire the resource
    SmbCeAcquireResource();

    Status            = STATUS_CONNECTION_DISCONNECTED;
    fReconnectNetRoot = BooleanFlagOn(pExchange->SmbCeFlags,SMBCE_EXCHANGE_ATTEMPT_RECONNECTS);

    switch (pNetRootEntry->Header.State) {
    case SMBCEDB_ACTIVE:
        Status = STATUS_SUCCESS;
        break;

    case SMBCEDB_INVALID:
        RxDbgTrace( 0, Dbg, ("SmbCeReferenceNetRoot: Reestablishing net root\n"));
        if (!fReconnectNetRoot) {
            break;
        }
        pNetRootEntry->NetRoot.TreeId = 0;
        // fall thru

    case SMBCEDB_START_CONSTRUCTION:
        pExchange->SmbCeFlags |= SMBCE_EXCHANGE_NETROOT_CONSTRUCTOR;
        pNetRootEntry->Header.State = SMBCEDB_CONSTRUCTION_IN_PROGRESS;
        Status = STATUS_SUCCESS;
        break;

    case SMBCEDB_CONSTRUCTION_IN_PROGRESS:
        if (fReconnectNetRoot) {
            // The construction of the net root is already in progress ....
            // Queue up the request to resume this exchange when the session
            // construction is complete.
            PSMBCEDB_REQUEST_ENTRY pRequestEntry;

            pRequestEntry = (PSMBCEDB_REQUEST_ENTRY)
                             SmbMmAllocateObject(SMBCEDB_OT_REQUEST);

            if (pRequestEntry != NULL) {
                pRequestEntry->Request.pExchange = pExchange;

                SmbCeAddRequestEntry(&pNetRootEntry->Requests,pRequestEntry);
                Status = STATUS_PENDING;
            } else {
                Status = STATUS_INSUFFICIENT_RESOURCES;
            }
        }
        break;

    case SMBCEDB_MARKED_FOR_DELETION:
        break;

    default:
        ASSERT(!"Valid NetRoot State, SmbCe database corrupt");
        break;
    }

    // Release the resource ...
    SmbCeReleaseResource();

    if ((Status == STATUS_SUCCESS) || (Status == STATUS_PENDING)) {
        pExchange->SmbCeState = SMBCE_EXCHANGE_NETROOT_INITIALIZED;
    }

    return Status;
}

NTSTATUS
SmbCeInitiateExchange(
    PSMB_EXCHANGE pExchange)
/*++

Routine Description:

   This routine initiates a exchange.

Arguments:

    pExchange  - the exchange to be initiated.

Return Value:

    RXSTATUS - The return status for the operation

Notes:

    The initiation of an exchange proceeds in multiple steps. The first step involves
    referencing the corresponding server,session and netroot entries. Subsequently the
    exchange is placed in a SMB_EXCHANGE_START state and the exchange is dispatched to the
    Start method. The act of referencing the session or the net root may suspend the exchange.

--*/
{
    NTSTATUS Status = STATUS_SUCCESS;

    ASSERT(pExchange->SmbCeContext.pServerEntry != NULL);

    switch (SmbCeGetServerType(pExchange->SmbCeContext.pServerEntry)) {
    case SMBCEDB_FILE_SERVER:
        // Admin exchanges do not have these fields filled in. All the three
        // entries must be valid for all other exchanges.
        if ((pExchange->NodeTypeCode != SMB_EXCHANGE_NTC(ADMIN_EXCHANGE)) &&
            ((pExchange->SmbCeContext.pNetRootEntry == NULL) ||
            (pExchange->SmbCeContext.pSessionEntry == NULL))) {
            Status = STATUS_REQUEST_ABORTED;
            break;
        }
    case SMBCEDB_MAILSLOT_SERVER:
        break;
    default:
        // Prepare for aborting the request if either the server type is invalid
        // or if the netroot entry or the session entry is invalid.
        Status = STATUS_REQUEST_ABORTED;
    }

    if (Status != STATUS_SUCCESS) {
        RxDbgTrace( 0, Dbg, ("SmbCeInitiateExchange: Exchange %lx Status %lx\n",pExchange,Status));
        return Status;
    }

    if (pExchange->pSmbCeSynchronizationEvent != NULL) {
        KeInitializeEvent(
            pExchange->pSmbCeSynchronizationEvent,
            SynchronizationEvent,
            FALSE);
    }

    for (;;) {
        switch (pExchange->SmbCeState) {
        case SMBCE_EXCHANGE_INITIALIZATION_START:
            {
                RxDbgTrace( 0, Dbg, ("SmbCeInitiateExchange: Exchange %lx State %lx\n",pExchange,pExchange->SmbCeState));
                Status = SmbCeReferenceServer(pExchange);
                if (Status != STATUS_SUCCESS) {
                    // this covers the case when the SERVER_ENTRY is under construction
                    // and RxStatus(PENDING) is returned.
                    RxDbgTrace( 0, Dbg, ("SmbCeInitiateExchange: SmbCeReferenceServer returned %lx\n",Status));
                    break;
                }
            }
            // fall through

        case SMBCE_EXCHANGE_SERVER_INITIALIZED:
            if (SmbCeGetServerType(pExchange->SmbCeContext.pServerEntry) == SMBCEDB_MAILSLOT_SERVER) {
                // Mailslot servers do not have any netroot/session associated with them.
                pExchange->SmbCeState = SMBCE_EXCHANGE_INITIATED;
                Status                = STATUS_SUCCESS;
                break;
            } else {
                RxDbgTrace( 0, Dbg, ("SmbCeInitiateExchange: Exchange %lx State %lx\n",pExchange,pExchange->SmbCeState));
                Status = SmbCeReferenceSession(pExchange);
                if (!NT_SUCCESS(Status)) {
                    RxDbgTrace( 0, Dbg, ("SmbCeInitiateExchange: SmbCeReferenceSession returned %lx\n",Status));
                    break;
                } if ((Status == STATUS_PENDING) &&
                      !(pExchange->SmbCeFlags & SMBCE_EXCHANGE_SESSION_CONSTRUCTOR)) {
                    break;
                }
            }
            // fall through

        case SMBCE_EXCHANGE_SESSION_INITIALIZED:
            RxDbgTrace( 0, Dbg, ("SmbCeInitiateExchange: Exchange %lx State %lx\n",pExchange,pExchange->SmbCeState));

            Status = SmbCeReferenceNetRoot(pExchange);
            if (!NT_SUCCESS(Status)) {
                RxDbgTrace( 0, Dbg, ("SmbCeInitiateExchange: SmbCeReferenceNetRoot returned %lx\n",Status));
                break;
            } else if ((Status == STATUS_PENDING) &&
                           !(pExchange->SmbCeFlags & SMBCE_EXCHANGE_NETROOT_CONSTRUCTOR)) {
              break;
            }

            // else fall through

        case SMBCE_EXCHANGE_NETROOT_INITIALIZED:
            pExchange->SmbCeState = SMBCE_EXCHANGE_INITIATED;
            Status                = STATUS_SUCCESS;
            break;

        default:
            ASSERT(!"Valid State for a SMB exchange, exchange Initiation aborted");
            break;
        }

        if ((pExchange->pSmbCeSynchronizationEvent != NULL)     &&
            (pExchange->SmbCeState != SMBCE_EXCHANGE_INITIATED) &&
            (Status == STATUS_PENDING)) {

            KeWaitForSingleObject(
                pExchange->pSmbCeSynchronizationEvent,
                Executive,
                KernelMode,
                FALSE,
                NULL );

            ASSERT(pExchange->Status != STATUS_PENDING);
            Status = pExchange->Status;
            if (Status != STATUS_SUCCESS) {
                break;
            }
        } else {
            break;
        }
    }

    ASSERT((Status != STATUS_PENDING) ||
           (pExchange->pSmbCeSynchronizationEvent == NULL));

    RxDbgTrace(0,Dbg,("Exchange (%lx) Type (%lx) State(%lx) Status %lx \n",pExchange,pExchange->Type,pExchange->SmbCeState,Status));
    RxDbgTrace(0,Dbg,
        ("ServerEntry(%lx) SessionEntry(%lx) NetRootEntry(%lx) \n",
        pExchange->SmbCeContext.pServerEntry,
        pExchange->SmbCeContext.pSessionEntry,
        pExchange->SmbCeContext.pNetRootEntry));

    // Note: Once the exchange has been initiated no further reference of the exchange
    // can be done since the state of the exchange is non-deterministic, i.e., depends upon
    // the scheduler.
    if (Status == STATUS_SUCCESS) {
        // Start the exchange
        ASSERT(pExchange->SmbCeState == SMBCE_EXCHANGE_INITIATED);

        if ((pExchange->SmbCeContext.pServerEntry->Header.State == SMBCEDB_ACTIVE) ||
            (pExchange->NodeTypeCode == SMB_EXCHANGE_NTC(ADMIN_EXCHANGE))) {
            Status = SmbCeInitializeExchangeTransport(pExchange);
        } else {
            Status = STATUS_CONNECTION_DISCONNECTED;
        }

        if (Status == STATUS_SUCCESS) {
            pExchange->SmbStatus = STATUS_SUCCESS;
            pExchange->ServerVersion = pExchange->SmbCeContext.pServerEntry->Server.Version;
            Status = SMB_EXCHANGE_DISPATCH(pExchange,Start,((PSMB_EXCHANGE)pExchange));
            RxDbgTrace( 0, Dbg, ("SmbCeInitiateExchange: SMB_EXCHANGE_DISPATCH(Start) returned %lx\n",Status));
        }
    } else if (Status != STATUS_PENDING) {
        RxDbgTrace( 0, Dbg, ("SmbCeInitiateExchange: Exchange(%lx) Initiation failed %lx \n",pExchange,Status));
    }

    return Status;
}

NTSTATUS
SmbCeExchangeAbort(
    PSMB_EXCHANGE pExchange)
/*++

Routine Description:

   This routine aborts an exchange.

Arguments:

    pExchange  - the exchange to be aborted.

Return Value:

    RXSTATUS - The return status for the operation

Notes:


--*/
{
    RxDbgTrace( 0, Dbg, ("SmbCeExchangeAbort: Exchange %lx aborted\n",pExchange));
    SmbCeDiscardExchange(pExchange);
    return STATUS_SUCCESS;
}

NTSTATUS
SmbCeBuildSmbHeader(
    IN OUT PSMB_EXCHANGE     pExchange,
    IN OUT PVOID             pBuffer,
    IN     ULONG             BufferLength,
    OUT    PULONG            pBufferConsumed,
    OUT    PUCHAR            pLastCommandInHeader,
    OUT    PUCHAR            *pNextCommandPtr)
/*++

Routine Description:

   This routine constructs the SMB header associated with any SMB sent as part of
   an exchange.

Arguments:

    pExchange  - the exchange for which the SMB is to be constructed.

    pBuffer    - the buffer in which the SMB header is to be constructed

    BufferLength - length of the buffer

    pBufferConsumed - the buffer consumed

    pLastCommandInHeader - the last command in header, SMB_COM_NO_ANDX_COMMAND if none

    pNextCommandPtr - the ptr to the place in the buffer where the next command
                      code should be copied.


Return Value:

    STATUS_SUCCESS  - if the header construction was successful

Notes:

    This routine is called to build the SMB header. This centralization allows us to
    compound the SMB operation with other SMB's required for the maintenance of the
    SMB connection engine data structures. It also provides us with a centralized facility
    for profiling SMB's as well as a one place mechanism for filling in all the header
    fields associated with a SMB.

--*/
{
    NTSTATUS      Status = STATUS_SUCCESS;
    PSMB_HEADER   pSmbHeader = (PSMB_HEADER)pBuffer;
    PGENERIC_ANDX pSmbBuffer;
    ULONG         SmbBufferUnconsumed = BufferLength;
    PUCHAR        pSmbCommand;

    UCHAR         LastCommandInHeader = SMB_COM_NO_ANDX_COMMAND;
    UCHAR         Flags = 0;
    USHORT        Flags2 = 0;

    PSMBCE_SERVER pServer;

    if (BufferLength < sizeof(SMB_HEADER)) {
        RxDbgTrace( 0, Dbg, ("SmbCeBuildSmbHeader: BufferLength too small %d\n",BufferLength));
        ASSERT(!"Buffer too small");
        return STATUS_BUFFER_TOO_SMALL;
    }

    SmbBufferUnconsumed = BufferLength - sizeof(SMB_HEADER);
    pServer = &pExchange->SmbCeContext.pServerEntry->Server;

    if (pServer->Dialect == LANMAN21_DIALECT) {
        Flags = (SMB_FLAGS_CASE_INSENSITIVE | SMB_FLAGS_CANONICALIZED_PATHS);
    }

    //DOWNLEVEL.NOTCORE flags for lanman10

    RtlZeroMemory(pSmbHeader,sizeof(SMB_HEADER));

    *((PULONG)&pSmbHeader->Protocol) = SMB_HEADER_PROTOCOL;
    pSmbHeader->Flags      = Flags;
    pSmbHeader->Flags2     = Flags2;
    pSmbHeader->Pid        = MRXSMB_PROCESS_ID;
    pSmbHeader->Uid        = 0;
    pSmbHeader->Tid        = 0;
    pSmbHeader->ErrorClass = 0;
    pSmbHeader->Reserved   = 0;
    pSmbCommand            = &pSmbHeader->Command;
    SmbPutUshort(&pSmbHeader->Error,0);

    switch (SmbCeGetServerType(pExchange->SmbCeContext.pServerEntry)) {
    case SMBCEDB_MAILSLOT_SERVER :
        break;

    case SMBCEDB_FILE_SERVER:
        {
            if (pExchange->SmbCeContext.pSessionEntry != NULL) {
                pSmbHeader->Uid = pExchange->SmbCeContext.pSessionEntry->Session.UserId;
            }

            if (pExchange->SmbCeContext.pNetRootEntry != NULL) {
                pSmbHeader->Tid = pExchange->SmbCeContext.pNetRootEntry->NetRoot.TreeId;
            }

            pSmbBuffer = (PGENERIC_ANDX)(pSmbHeader + 1);

            if ((pExchange->SmbCeFlags & SMBCE_EXCHANGE_SESSION_CONSTRUCTOR) ||
                (pExchange->SmbCeFlags & SMBCE_EXCHANGE_NETROOT_CONSTRUCTOR)) {
                // There is an opportunity to compound some SessionSetup/TreeConnect SMB with the
                // given SMB command.
                if (pExchange->SmbCeContext.pSessionEntry->Header.State == SMBCEDB_CONSTRUCTION_IN_PROGRESS) {
                    if (( pServer->DialectFlags & DF_EXTENDNEGOT) ||
                        ( pServer->DialectFlags & DF_NTNEGOTIATE)) {
                        RxDbgTrace( 0, Dbg, ("SmbCeBuildSmbHeader: Building Session setup And X\n"));

                        *pSmbCommand = SMB_COM_SESSION_SETUP_ANDX;
                        LastCommandInHeader = *pSmbCommand;
                        pSmbCommand = &pSmbBuffer->AndXCommand;
                        pSmbHeader->Tid = 0;

                        Status = SMBCE_SERVER_DIALECT_DISPATCH(
                                     pServer,
                                     BuildSessionSetup,
                                     (pExchange,
                                      pSmbBuffer,
                                      &SmbBufferUnconsumed));
                        if (NT_SUCCESS(Status)) {
                            // Update the buffer for the construction of the following SMB.
                            SmbPutUshort(
                                &pSmbBuffer->AndXOffset,
                                (USHORT)(BufferLength - SmbBufferUnconsumed));
                            pSmbBuffer = (PGENERIC_ANDX)((PBYTE)pBuffer + BufferLength - SmbBufferUnconsumed);
                        }
                    }
                } else {
                    NOTHING; //no session for share level AT LEAST NOT FOR CORE!!!
                }

                if (NT_SUCCESS(Status) &&
                    pExchange->SmbCeContext.pNetRootEntry->Header.State == SMBCEDB_CONSTRUCTION_IN_PROGRESS) {
                    BOOLEAN BuildingTreeConnectAndX = BooleanFlagOn(pServer->DialectFlags,DF_LANMAN10);

                    if (BuildingTreeConnectAndX) {
                        RxDbgTrace( 0, Dbg, ("SmbCeBuildSmbHeader: Building Tree Connect And X\n"));
                        *pSmbCommand = SMB_COM_TREE_CONNECT_ANDX;
                        LastCommandInHeader = *pSmbCommand;
                    } else {
                        RxDbgTrace( 0, Dbg, ("SmbCeBuildSmbHeader: Building Tree Connect No X\n"));
                        *pSmbCommand = SMB_COM_TREE_CONNECT;
                        LastCommandInHeader = *pSmbCommand;
                    }


                    Status = SMBCE_SERVER_DIALECT_DISPATCH(
                                 pServer,
                                 BuildTreeConnect,
                                 (pExchange,
                                  pSmbBuffer,
                                  &SmbBufferUnconsumed));

                    if (NT_SUCCESS(Status)) {
                        // Update the buffer for the construction of the following SMB.
                        if (BuildingTreeConnectAndX) {
                            pSmbCommand = &pSmbBuffer->AndXCommand;
                            SmbPutUshort(&pSmbBuffer->AndXOffset,(USHORT)(BufferLength - SmbBufferUnconsumed));
                        } else {
                            pSmbCommand = NULL;
                        }
                    }
                }
            }
        }
        break;

    default:
        {
            ASSERT(!"Valid Server Type");
            Status = STATUS_INVALID_HANDLE;
        }
        break;
    }

    *pNextCommandPtr      = pSmbCommand;
    *pBufferConsumed      = BufferLength - SmbBufferUnconsumed;
    *pLastCommandInHeader = LastCommandInHeader;

    RxDbgTrace( 0, Dbg, ("SmbCeBuildSmbHeader: Buffer Consumed %lx\n",*pBufferConsumed));

    return Status;
}

typedef struct __Service_Name_Entry {
    NET_ROOT_TYPE NetRootType;
    USHORT NameLength;
    PBYTE  Name;
};
struct __Service_Name_Entry ServiceNameTable[] = {
    {NET_ROOT_DISK,sizeof(SHARE_TYPE_NAME_DISK),SHARE_TYPE_NAME_DISK},
    {NET_ROOT_PIPE,sizeof(SHARE_TYPE_NAME_PIPE),SHARE_TYPE_NAME_PIPE},
    {NET_ROOT_PRINT,sizeof(SHARE_TYPE_NAME_PRINT),SHARE_TYPE_NAME_PRINT},
    {NET_ROOT_COMM,sizeof(SHARE_TYPE_NAME_COMM),SHARE_TYPE_NAME_COMM}  //COMM must be last
    };

NTSTATUS
SmbCeParseSmbHeader(
    PSMB_EXCHANGE     pExchange,
    PSMB_HEADER       pSmbHeader,
    PGENERIC_ANDX     pCommandToProcess,
    NTSTATUS          *pSmbResponseStatus,
    ULONG             BytesAvailable,
    ULONG             BytesIndicated,
    PULONG            pBytesConsumed)
/*++

Routine Description:

   This routine validates the SMB header associated with any SMB received as part of
   an exchange.

Arguments:

    pExchange  - the exchange for which the SMB is to be constructed.

    pSmbHeader - the header of the SMB received

    pCommandToProcess - the SMB command to be processed after the header ( Can be NULL )

    pSmbResponseStatus - the status in the SMB response header (Can be NULL)

    BytesAvailable - the bytes available for processing but not necessarily indicated.

    BytesIndicated - the length of the SMB buffer available for perusal

    pBytesConsumed - the buffer consumed

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 sufficient data was not
          indicated to process the header.
          STATUS_SUCCESS -- the header was processed successfully. In such cases the GENERIC_ANDX
          if not NULL will contain the offset from the start of the buffer and the command
          to be processed.
          STATUS_* -- They indicate an error which would normally lead to the abortion of the
          exchange.

Notes:

    This routine is called to parse the SMB header. This centralization allows us to
    implement a one stop mechanism for updating/validating the header fields as well as
    resuming the exchanges waiting for the construction of session/net root entry
    associated with this exchange

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

    PBYTE    pSmbBuffer = (PBYTE)pSmbHeader;
    UCHAR    SmbCommand;

    BOOLEAN  fSessionSetupResponse = FALSE;
    BOOLEAN  fTreeConnectResponse  = FALSE;

    SMBCEDB_OBJECT_STATE SessionState;
    SMBCEDB_OBJECT_STATE NetRootState;

    // Return Immediately if bytes indicated is less then the size of a SMB header.
    if (BytesIndicated < sizeof(SMB_HEADER)) {
        *pBytesConsumed = BytesIndicated;
        return STATUS_INVALID_NETWORK_RESPONSE;
    }

    SmbResponseStatus = GetSmbResponseNtStatus(pSmbHeader);
    if (!NT_SUCCESS(SmbResponseStatus)) {
        RxDbgTrace( 0, Dbg, ("SmbCeParseSmbHeader::SMB Response Error %lx\n",SmbResponseStatus));
    }

    SmbCommand      = pSmbHeader->Command;
    *pBytesConsumed = sizeof(SMB_HEADER);
    pSmbBuffer     += *pBytesConsumed;

    // There are certain SMB's that effect the connection engine data structures as
    // well as the exchange that has been suspended. These are the SMB's used for tree
    // connect and session setup.
    // In all the other cases no special action is required for the maintenance of the
    // connection engine data structures. The Exchange that was suspended needs to be
    // resumed.
    if (SmbCommand == SMB_COM_SESSION_SETUP_ANDX) {
        if (SmbResponseStatus != STATUS_SUCCESS) {
            if ((FIELD_OFFSET(GENERIC_ANDX,AndXReserved) + *pBytesConsumed) <= BytesIndicated) {
                PGENERIC_ANDX pGenericAndX = (PGENERIC_ANDX)pSmbBuffer;

                if (pGenericAndX->WordCount == 0) {
                    Status = SmbResponseStatus;
                }
            }

            // Note that the case wherein sufficient bytes are not indicated for the
            // GENERIC_ANDX response is handled by the if statement below which
            // imposes a more stringent test.
        }

        if ((Status == STATUS_SUCCESS) &&
            (FIELD_OFFSET(RESP_SESSION_SETUP_ANDX,Buffer) + *pBytesConsumed) > BytesIndicated) {
            Status = STATUS_MORE_PROCESSING_REQUIRED;
        }

        if (Status == STATUS_SUCCESS) {
            PRESP_SESSION_SETUP_ANDX pSessionSetupResponse;
            ULONG                    SessionSetupResponseLength,ByteCount;

            RxDbgTrace( 0, Dbg, ("Processing Session Setup ANd X\n"));
            pSessionSetupResponse = (PRESP_SESSION_SETUP_ANDX)(pSmbBuffer);

            ByteCount = SmbGetUshort(&pSessionSetupResponse->ByteCount);
            if (pSessionSetupResponse->WordCount == 3) {
                SmbCommand = pSessionSetupResponse->AndXCommand;
                if (SmbCommand == SMB_COM_NO_ANDX_COMMAND) {
                    SessionSetupResponseLength =
                        FIELD_OFFSET(RESP_SESSION_SETUP_ANDX,Buffer) + ByteCount;
                    Status = SmbResponseStatus;
                } else {
                    SessionSetupResponseLength =
                        SmbGetUshort(&pSessionSetupResponse->AndXOffset) - *pBytesConsumed;
                }

            } else {
                Status = SmbResponseStatus;
            }

            if (NT_SUCCESS(Status)) {
                if (SessionSetupResponseLength + *pBytesConsumed <= BytesIndicated) {
                    *pBytesConsumed += SessionSetupResponseLength;
                    pSmbBuffer += SessionSetupResponseLength;
                    pExchange->SmbCeContext.pSessionEntry->Session.UserId = pSmbHeader->Uid;
                    fSessionSetupResponse = TRUE;
                    SessionState = SMBCEDB_ACTIVE;
                    InterlockedIncrement(&MRxIfsStatistics.Sessions);
                } else {
                    Status = STATUS_MORE_PROCESSING_REQUIRED;
                }
            } else {
                RxDbgTrace( 0, Dbg, ("SmbCeParseSmbHeader::Session setup and X Response %lx\n",Status));
                fSessionSetupResponse = TRUE;
                SessionState = SMBCEDB_MARKED_FOR_DELETION;
                InterlockedIncrement(&MRxIfsStatistics.FailedSessions);

                if ((SmbCommand == SMB_COM_TREE_CONNECT_ANDX) ||
                    (SmbCommand == SMB_COM_TREE_CONNECT)) {
                    RxDbgTrace( 0, Dbg, ("SmbCeParseSmbHeader:: Tearing down a tree connection\n"));
                    fTreeConnectResponse  = TRUE;
                    NetRootState = SMBCEDB_MARKED_FOR_DELETION;
                }
            }
        }
    }

    if ((SmbCommand == SMB_COM_TREE_CONNECT_ANDX) &&
        NT_SUCCESS(Status)) {
        if (SmbResponseStatus != STATUS_SUCCESS) {
            if ((FIELD_OFFSET(GENERIC_ANDX,AndXReserved) + *pBytesConsumed) <= BytesIndicated) {
                PGENERIC_ANDX pGenericAndX = (PGENERIC_ANDX)pSmbBuffer;

                if (pGenericAndX->WordCount == 0) {
                    Status = SmbResponseStatus;
                }
            }

            // Note that the case wherein sufficient bytes are not indicated for the
            // GENERIC_ANDX response is handled by the if statement below which
            // imposes a more stringent test.
        }

        if ((Status == STATUS_SUCCESS) &&
            (FIELD_OFFSET(RESP_21_TREE_CONNECT_ANDX,Buffer) + *pBytesConsumed) > BytesIndicated) {
            Status = STATUS_MORE_PROCESSING_REQUIRED;
        }

        if (Status == STATUS_SUCCESS) {
            ULONG TreeConnectResponseLength,TreeConnectByteCount,ServiceStringLength;
            PUCHAR pShareTypeResponseString = NULL;
            PRESP_21_TREE_CONNECT_ANDX p21TreeConnectAndXResponse;

            p21TreeConnectAndXResponse = (PRESP_21_TREE_CONNECT_ANDX)(pSmbBuffer);
            SmbCommand = p21TreeConnectAndXResponse->AndXCommand;

            RxDbgTrace( 0, Dbg, ("Processing Tree Connect and X\n"));

            // case out based on the actual response length. Lanman 21 clients or NT clients
            // have a longer response.....win95 negotiates NT dialect but uses a <lm21 response format
            switch (p21TreeConnectAndXResponse->WordCount) {
            case 0:
                Status = SmbResponseStatus;
                break;

            case 3:
                {
                    pShareTypeResponseString = (PUCHAR)&p21TreeConnectAndXResponse->Buffer;
                    TreeConnectByteCount  = SmbGetUshort(&p21TreeConnectAndXResponse->ByteCount);
                    if (SmbCommand == SMB_COM_NO_ANDX_COMMAND) {
                        TreeConnectResponseLength =
                            FIELD_OFFSET(RESP_21_TREE_CONNECT_ANDX,Buffer) + TreeConnectByteCount;

                        Status = SmbResponseStatus;
                    } else {
                        TreeConnectResponseLength =
                            SmbGetUshort(&p21TreeConnectAndXResponse->AndXOffset) -
                            *pBytesConsumed;
                    }
                }
                break;

            case 2:
                {
                    PRESP_TREE_CONNECT_ANDX pTreeConnectAndXResponse;

                    pTreeConnectAndXResponse = (PRESP_TREE_CONNECT_ANDX)(pSmbBuffer);
                    //SmbCommand = pTreeConnectAndXResponse->AndXCommand;
                    ASSERT(FIELD_OFFSET(RESP_TREE_CONNECT_ANDX,AndXCommand)
                           ==FIELD_OFFSET(RESP_21_TREE_CONNECT_ANDX,AndXCommand));
                    pShareTypeResponseString = (PUCHAR)&pTreeConnectAndXResponse->Buffer;
                    TreeConnectByteCount  = SmbGetUshort(&pTreeConnectAndXResponse->ByteCount);
                    if (SmbCommand == SMB_COM_NO_ANDX_COMMAND) {
                        TreeConnectResponseLength =
                            FIELD_OFFSET(RESP_TREE_CONNECT_ANDX,Buffer) + TreeConnectByteCount;

                        Status = SmbResponseStatus;
                    } else {
                        TreeConnectResponseLength =
                            SmbGetUshort(&pTreeConnectAndXResponse->AndXOffset) -
                            *pBytesConsumed;
                    }
                }
                break;

            default :
                Status = STATUS_INVALID_NETWORK_RESPONSE;
            }

            RxDbgTrace( 0, Dbg, ("SmbCeParseSmbHeader::Tree connect and X Response %lx\n",Status));
            if (NT_SUCCESS(Status)) {
                PSMBCE_NET_ROOT psmbNetRoot = &pExchange->SmbCeContext.pNetRootEntry->NetRoot;
                PSMBCE_SERVER psmbServer = &pExchange->SmbCeContext.pServerEntry->Server;

                if (TreeConnectResponseLength + *pBytesConsumed <= BytesIndicated) {
                    *pBytesConsumed += TreeConnectResponseLength;

                    // Update the NetRoot fields based on the response.

                    psmbNetRoot->TreeId = pSmbHeader->Tid;

                    {   struct __Service_Name_Entry *i;
                        for (i=ServiceNameTable;;i++) {
                            ServiceStringLength = i->NameLength;
                            if (RtlCompareMemory(
                                    pShareTypeResponseString,
                                    i->Name,
                                    ServiceStringLength)
                                == ServiceStringLength) {
                                psmbNetRoot->NetRootType = i->NetRootType;
                                if (FALSE) DbgPrint("FoundServiceStrng %s len %d type %d\n",i->Name,i->NameLength,i->NetRootType);
                                break;
                            }
                            if (i->NetRootType==NET_ROOT_COMM) {
                                ASSERT(!"Valid Share Type returned in TREE COnnect And X response");
                                psmbNetRoot->NetRootType = NET_ROOT_DISK;
                                ServiceStringLength = TreeConnectByteCount;
                                break;
                            }
                        }
                    }

                    if (psmbNetRoot->NetRootType == NET_ROOT_DISK) {
                        psmbNetRoot->MaximumReadBufferSize = psmbServer->MaximumDiskFileReadBufferSize;
                    } else {
                        psmbNetRoot->MaximumReadBufferSize = psmbServer->MaximumNonDiskFileReadBufferSize;
                    }

                    //if !(NT was negotiated) and bytecount>servicelength, we may have a NativeFs name
                    if (!FlagOn(psmbServer->DialectFlags,DF_NTNEGOTIATE)
                        && (TreeConnectByteCount>ServiceStringLength)) {
                        PBYTE NativeFs = pShareTypeResponseString+ServiceStringLength;
                        if (*NativeFs != 0) {
                            ULONG i;
                            ULONG maxlenpersmb = TreeConnectByteCount-ServiceStringLength;
                            ULONG maxlenperarraysize = SMB_MAXIMUM_SUPPORTED_VOLUME_LABEL;
                            PCHAR p = (PCHAR)(&psmbNetRoot->FileSystemNameA[0]);  //dont write into the 0th char
                            //DbgPrint("we may have one...\n");
                            for (i=1;;i++){
                                if (i==maxlenpersmb) {
                                    break;
                                }
                                if (i==maxlenperarraysize) {
                                    break;
                                }
                                if (NativeFs[i]==0) {
                                    break;
                                }
                            }
                            //save away the name for processing later

                            RtlCopyMemory(p,NativeFs,i);
                            p[i] = 0;
                            //DbgPrint("NativeFs = %s (%d)\n",p,i);
                            psmbNetRoot->FileSystemNameALength = (UCHAR)i;
                        }
                    }

                    pSmbBuffer += TreeConnectResponseLength;
                    fTreeConnectResponse = TRUE;
                    NetRootState         = SMBCEDB_ACTIVE;
                } else {
                    Status = STATUS_MORE_PROCESSING_REQUIRED;
                }
            } else {
                fTreeConnectResponse  = TRUE;
                NetRootState          = SMBCEDB_MARKED_FOR_DELETION;
            }
        }
    }

    if ((SmbCommand == SMB_COM_TREE_CONNECT) &&
        NT_SUCCESS(Status)) {
        PRESP_TREE_CONNECT   pTreeConnectResponse;
        ULONG                TreeConnectResponseLength;

        RxDbgTrace( 0, Dbg, ("Processing Tree Connect\n"));
        pTreeConnectResponse      = (PRESP_TREE_CONNECT)pSmbBuffer;
        TreeConnectResponseLength = FIELD_OFFSET(RESP_TREE_CONNECT,Buffer);

        SmbCommand = SMB_COM_NO_ANDX_COMMAND;

        if (NT_SUCCESS(SmbResponseStatus)) {
            if (TreeConnectResponseLength + *pBytesConsumed <= BytesIndicated) {
                pExchange->SmbCeContext.pNetRootEntry->NetRoot.TreeId
                    = SmbGetUshort(&pTreeConnectResponse->Tid);
                pExchange->SmbCeContext.pNetRootEntry->NetRoot.NetRootType = NET_ROOT_WILD;
                if (pExchange->SmbCeContext.pServerEntry->Server.MaximumBufferSize == 0){
                    RxDbgTrace( 0, Dbg, ("SmbCeParseSmbHeader:: setting srvmaxbufsize %ld\n",
                                         SmbGetUshort(&pTreeConnectResponse->MaxBufferSize)));
                    pExchange->SmbCeContext.pServerEntry->Server.MaximumBufferSize =
                        SmbGetUshort(&pTreeConnectResponse->MaxBufferSize);
                }

                *pBytesConsumed += TreeConnectResponseLength;

                pSmbBuffer += *pBytesConsumed;

                fTreeConnectResponse = TRUE;
                NetRootState         = SMBCEDB_ACTIVE;

                //for CORE, this counts as a successful session setup as well!
                pExchange->SmbCeContext.pSessionEntry->Session.UserId = pSmbHeader->Uid;
                fSessionSetupResponse = TRUE;
                SessionState = SMBCEDB_ACTIVE;
            } else {
                Status = STATUS_MORE_PROCESSING_REQUIRED;
            }
        } else {
            Status = SmbResponseStatus;
            fTreeConnectResponse  = TRUE;
            NetRootState          = SMBCEDB_MARKED_FOR_DELETION;
        }

        RxDbgTrace( 0, Dbg, ("SmbCeParseSmbHeader::Tree connect Response %lx\n",Status));
    }

    // Initiate further action if the status of the exchange/conenction engine can be
    // updated based on the data available.
    if ((pExchange->SmbCeFlags & SMBCE_EXCHANGE_SESSION_CONSTRUCTOR) ||
        (pExchange->SmbCeFlags & SMBCE_EXCHANGE_NETROOT_CONSTRUCTOR)) {
        if (fSessionSetupResponse) {
            SmbCeUpdateSessionEntryState(
                pExchange->SmbCeContext.pSessionEntry,
                SessionState);

            RxDbgTrace( 0, Dbg, ("Dispatching Session Entry Finalization\n"));
        }

        if (fTreeConnectResponse) {
            SmbCeUpdateNetRootEntryState(
                pExchange->SmbCeContext.pNetRootEntry,
                NetRootState);

            RxDbgTrace( 0, Dbg, ("Dispatching Net root Entry Finalization\n"));
        }
    } else {
        ASSERT(pSmbHeader->Uid == pExchange->SmbCeContext.pSessionEntry->Session.UserId);
        ASSERT(pSmbHeader->Tid == pExchange->SmbCeContext.pNetRootEntry->NetRoot.TreeId);
    }

    pExchange->SmbStatus = SmbResponseStatus;     //N.B. no spinlock!
    if (Status == STATUS_MORE_PROCESSING_REQUIRED) {
        *pBytesConsumed = 0;
    } else if (!NT_SUCCESS(Status)) {
        *pBytesConsumed = BytesAvailable;
    } else {
        if (pSmbResponseStatus != NULL) {
            *pSmbResponseStatus = SmbResponseStatus;
        }

        if (pCommandToProcess != NULL) {
            PGENERIC_ANDX pGenericAndX = (PGENERIC_ANDX)((PBYTE)pSmbHeader + *pBytesConsumed);

            pCommandToProcess->AndXCommand = SmbCommand;
            SmbPutUshort(&pCommandToProcess->AndXOffset, (USHORT)*pBytesConsumed);

            if ((sizeof(GENERIC_ANDX) + *pBytesConsumed) <= BytesAvailable) {
                pCommandToProcess->WordCount   = pGenericAndX->WordCount;
            } else {
                pCommandToProcess->WordCount = 0;
            }
        }
    }

    return Status;
}


NTSTATUS
SmbCeInitializeExchange(
    PSMB_EXCHANGE                 *pExchangePointer,
    PMRX_V_NET_ROOT               pVNetRoot,
    SMB_EXCHANGE_TYPE             Type,
    PSMB_EXCHANGE_DISPATCH_VECTOR pDispatchVector)
/*++

Routine Description:

   This routine initializes the given exchange instance

Arguments:

    pExchangePointer  - the placeholder for the exchange instance. If it is NULL a new one
    is allocated.

    pVirtualNetRoot   - the virtual net root

    pNetRoot          - the associated net root

    Type              - the type of the exchange

    pDispatchVector   - the dispatch vector associated with this instance.

Return Value:

    RXSTATUS - The return status for the operation

--*/
{
    NTSTATUS Status;

    RxDbgTrace( 0, Dbg, ("SmbCeInitializeExchange: Invoked\n"));

    if (*pExchangePointer == NULL) {
        // Allocate a new exchange instance.
        *pExchangePointer = SmbMmAllocateExchange(Type,NULL);
        if (*pExchangePointer == NULL) {
            return STATUS_INSUFFICIENT_RESOURCES;
        }
    }

    if ((Status = SmbCeIncrementActiveExchangeCount()) == STATUS_SUCCESS) {
        PSMB_EXCHANGE LocalExchangePointer = *pExchangePointer;

        LocalExchangePointer->SmbCeContext.pVNetRoot = pVNetRoot;

        LocalExchangePointer->SmbCeContext.pServerEntry  =
            SmbCeReferenceAssociatedServerEntry(pVNetRoot->pNetRoot->pSrvCall);

        LocalExchangePointer->SmbCeContext.pSessionEntry =
            SmbCeReferenceAssociatedSessionEntry(pVNetRoot);

        LocalExchangePointer->SmbCeContext.pNetRootEntry =
            SmbCeReferenceAssociatedNetRootEntry(pVNetRoot->pNetRoot);

        LocalExchangePointer->SmbCeState = SMBCE_EXCHANGE_INITIALIZATION_START;
        LocalExchangePointer->pDispatchVector = pDispatchVector;
        LocalExchangePointer->SmbCeFlags &= (SMBCE_EXCHANGE_FLAGS_TO_PRESERVE);
        LocalExchangePointer->SmbCeFlags |= (SMBCE_EXCHANGE_REUSE_MID | SMBCE_EXCHANGE_ATTEMPT_RECONNECTS);
    }

    return Status;
}

NTSTATUS
SmbCeTransformExchange(
    PSMB_EXCHANGE                 pExchange,
    SMB_EXCHANGE_TYPE             NewType,
    PSMB_EXCHANGE_DISPATCH_VECTOR pDispatchVector)
/*++

Routine Description:

   This routine transforms an exchange instance of one kind to an exchange instance
   of another kind ( A sophisticated form of casting )

Arguments:

    pExchange         - the exchange instance.

    Type              - the new type of the exchange

    pDispatchVector   - the dispatch vector associated with this instance.

Return Value:

    RXSTATUS - The return status for the operation

Notes:

    As it is currently implemented no restrictions are imposed. Once the number of exchanges
    have been established further restrictions will be imposed barring certain kinds of
    transformations. The transformation merely switches the dispatch vector associated
    with the exchange but the context is left intact.

--*/
{
    pExchange->Type = NewType;
    pExchange->pDispatchVector = pDispatchVector;
    return STATUS_SUCCESS;
}

NTSTATUS
SmbCePrepareExchangeForReuse(
    PSMB_EXCHANGE                 pExchange)
/*++

Routine Description:

   This routine transforms an exchange instance of one kind to an exchange instance
   of another kind ( A sophisticated form of casting )

Arguments:

    pExchange  - the exchange instance.

Return Value:

    RXSTATUS - The return status for the operation

--*/
{
    PSMBCEDB_SERVER_ENTRY   pServerEntry  = NULL;
    PSMBCEDB_SESSION_ENTRY  pSessionEntry = NULL;
    PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry = NULL;

    RxDbgTrace( 0, Dbg, ("SmbCePrepareExchangeForReuse: Invoked\n"));

    pNetRootEntry = pExchange->SmbCeContext.pNetRootEntry;
    pSessionEntry = pExchange->SmbCeContext.pSessionEntry;
    pServerEntry = pExchange->SmbCeContext.pServerEntry;

    if (pServerEntry != NULL) {
        // Disassociate the MID associated with the exchange
        if (pExchange->SmbCeFlags & SMBCE_EXCHANGE_MID_VALID) {
            SmbCeDisassociateMidFromExchange(pServerEntry,pExchange);
        }

        // Tear down all the copy data requests associated with this exchange
        SmbCePurgeBuffersAssociatedWithExchange(pServerEntry,pExchange);

        // Uninitialize the transport associated with the exchange
        SmbCeUninitializeExchangeTransport(pExchange);

        // If this exchange has been marked as a constructor for either a
        // session or netroot finalize the appropriate entries. ( mark
        // them for deletion so that other exchanges can be resumed )

        if (pExchange->SmbCeFlags & SMBCE_EXCHANGE_SESSION_CONSTRUCTOR) {
            ASSERT(pSessionEntry != NULL);
            RxDbgTrace( 0, Dbg, ("Dispatching Session Entry Finalization\n"));
            SmbCeReferenceSessionEntry(pSessionEntry);
            SmbCeCompleteSessionEntryInitialization(pSessionEntry);
            pExchange->SmbCeFlags &= ~SMBCE_EXCHANGE_SESSION_CONSTRUCTOR;
        }

        if (pExchange->SmbCeFlags & SMBCE_EXCHANGE_NETROOT_CONSTRUCTOR) {
            ASSERT(pNetRootEntry != NULL);
            RxDbgTrace( 0, Dbg, ("Dispatching Net root Entry Finalization\n"));
            // Finalize the construction of the net root entry.
            SmbCeReferenceNetRootEntry(pExchange->SmbCeContext.pNetRootEntry);
            SmbCeCompleteNetRootEntryInitialization(pNetRootEntry);
            pExchange->SmbCeFlags &= ~SMBCE_EXCHANGE_NETROOT_CONSTRUCTOR;
        }

        SmbCeDereferenceEntries(
            pServerEntry,
            pSessionEntry,
            pNetRootEntry);
    } else {
        ASSERT((pSessionEntry == NULL) && (pNetRootEntry == NULL));
    }

    return STATUS_SUCCESS;
}

VOID
SmbCeDiscardExchange(PVOID pExchange)
/*++

Routine Description:

   This routine discards an exchange.

Arguments:

    pExchange  - the exchange to be discarded.

Return Value:

    RXSTATUS - The return status for the operation

Notes:

    Even though this is simple, it cannot be inlined since the destruction of an
    exchange instance can be posted to a worker thread.

--*/
{
    PSMB_EXCHANGE pSmbExchange = pExchange;

    RxDbgTrace( 0, Dbg, ("SmbCeDiscardExchange: Invoked\n"));

    RxLog((">>>Discard %lx",pSmbExchange));

    // Destroy the context
    if (pSmbExchange->ReferenceCount == 0) {
        SmbCePrepareExchangeForReuse(pSmbExchange);

        SmbCeDecrementActiveExchangeCount();

        // Discard the memory associated with the exchange
        SmbMmFreeExchange(pSmbExchange);
    } else {
        RxDbgTrace(
            0,
            Dbg,
            ("SmbCeDiscardExchange: Exchange %lx not discarded %ld\n",
              pSmbExchange,pSmbExchange->ReferenceCount)
            );
    }
}

NTSTATUS
SmbCeIncrementPendingOperations(
   PSMB_EXCHANGE pExchange,
   ULONG         PendingOperationMask)
/*++

Routine Description:

   This routine increments the appropriate pending operation count

Arguments:

    pExchange  - the exchange to be finalized.

    PendingOperationsMask -- the pending operations to be incremented

Return Value:

    RxStatus(SUCCESS) if successful

--*/
{
    NTSTATUS Status;

    SmbCeAcquireSpinLock();

    if (!BooleanFlagOn(pExchange->SmbCeFlags,SMBCE_EXCHANGE_FINALIZED)) {
        if ((pExchange->SmbCeContext.pServerEntry != NULL) &&
            ((pExchange->SmbCeContext.pServerEntry->ServerStatus == STATUS_SUCCESS) ||
            (pExchange->NodeTypeCode == SMB_EXCHANGE_NTC(ADMIN_EXCHANGE)))) {

            if (PendingOperationMask & SMBCE_LOCAL_OPERATION) {
                pExchange->LocalPendingOperations++;
            }

            if (PendingOperationMask & SMBCE_SEND_COMPLETE_OPERATION) {
                pExchange->SendCompletePendingOperations++;
            }

            if (PendingOperationMask & SMBCE_COPY_DATA_OPERATION) {
                pExchange->CopyDataPendingOperations++;
            }

            if (PendingOperationMask & SMBCE_RECEIVE_OPERATION) {
                pExchange->ReceivePendingOperations++;
            }

            Status = STATUS_SUCCESS;
        } else {
            if ((PendingOperationMask & SMBCE_LOCAL_OPERATION) &&
                (PendingOperationMask & ~SMBCE_LOCAL_OPERATION) == 0) {

                pExchange->LocalPendingOperations++;
                Status = STATUS_SUCCESS;
            } else {
                Status = STATUS_CONNECTION_DISCONNECTED;
            }
        }
    } else {
        Status = STATUS_UNSUCCESSFUL;
    }

    SmbCeReleaseSpinLock();

    return Status;
}

VOID
SmbCeFinalizeExchangeWorkerThreadRoutine(
    PSMB_EXCHANGE  pExchange)
/*++

Routine Description:

   This is the worker thread exchange finalization routine.

Arguments:

    pExchange  - the exchange to be finalized.

--*/
{
    BOOLEAN  fPostFinalize;
    NTSTATUS Status;

    Status = SMB_EXCHANGE_DISPATCH(
                 pExchange,
                 Finalize,
                 (pExchange,&fPostFinalize));

    ASSERT(!fPostFinalize && (Status == STATUS_SUCCESS));
}

VOID
SmbCepFinalizeExchange(
    PSMB_EXCHANGE pExchange)
/*++

Routine Description:

   This is the common finalization routine used by both the routines below

Arguments:

    pExchange  - the exchange to be finalized.

--*/
{
    NTSTATUS Status;
    BOOLEAN fPostFinalize = FALSE;

    ASSERT(FlagOn(pExchange->SmbCeFlags,SMBCE_EXCHANGE_FINALIZED));

    if (!(pExchange->SmbCeFlags & SMBCE_EXCHANGE_RETAIN_MID)) {
        SmbCeDisassociateMidFromExchange(
            pExchange->SmbCeContext.pServerEntry,
            pExchange);
    }

    Status = SMB_EXCHANGE_DISPATCH(
                 pExchange,
                 Finalize,
                 (pExchange,&fPostFinalize));

    if ((Status == STATUS_SUCCESS) &&
        fPostFinalize)  {
        // Post the request to a worker thread so that the finalization can be completed
        // at a lower IRQL.
        RxPostToWorkerThread(
            MRxIfsDeviceObject,
            CriticalWorkQueue,
            &pExchange->WorkQueueItem,
            SmbCeFinalizeExchangeWorkerThreadRoutine,
            pExchange);
    }
}

SMBCE_EXCHANGE_STATUS
SmbCeFinalizeExchange(
    PSMB_EXCHANGE  pExchange)
/*++

Routine Description:

   This routine finalizes an exchange instance.

Arguments:

    pExchange  - the exchange to be finalized.

Return Value:

    appropriate exchange status

Notes:

    When an exchange is initiated and the start routine is invoked a number of
    SMB's are sent. This routine is invoked when all processing pertaining to the
    SMB's that have been sent has ceased.

    This routine encapsulates all the idiosyncratic behaviour associated with the
    transports.

--*/
{
    BOOLEAN               fFinalizeExchange = FALSE;
    SMBCE_EXCHANGE_STATUS ExchangeStatus;

    SmbCeAcquireSpinLock();

    if (!(pExchange->SmbCeFlags & SMBCE_EXCHANGE_FINALIZED)) {
        if ((pExchange->ReceivePendingOperations == 0) &&
            (pExchange->CopyDataPendingOperations == 0) &&
            (pExchange->SendCompletePendingOperations == 0) &&
            (pExchange->LocalPendingOperations == 0)) {

            fFinalizeExchange = TRUE;
            ExchangeStatus = SmbCeExchangeFinalized;
            pExchange->SmbCeFlags |= SMBCE_EXCHANGE_FINALIZED;
        } else {
            ExchangeStatus = SmbCeExchangeNotFinalized;
        }
    } else {
        ExchangeStatus = SmbCeExchangeAlreadyFinalized;
    }

    SmbCeReleaseSpinLock();

    if (fFinalizeExchange) {
        SmbCepFinalizeExchange(pExchange);
    }

    return ExchangeStatus;
}

SMBCE_EXCHANGE_STATUS
SmbCeDecrementPendingOperationsAndFinalize(
    PSMB_EXCHANGE pExchange,
    ULONG         PendingOperationMask)
/*++

Routine Description:

   This routine decrements the corresponding pending operation count
   and finalizes an exchange instance if required

Arguments:

    pExchange  - the exchange to be finalized.

    PendingOperationsMask -- the pending operations to be decremented.

Return Value:

    appropriate exchange status

Notes:

    When an exchange is initiated and the start routine is invoked a number of
    SMB's are sent. This routine is invoked when all processing pertaining to the
    SMB's that have been sent has ceased.

    This routine encapsulates all the idiosyncratic behaviour associated with the
    transports.

--*/
{
    BOOLEAN               fFinalizeExchange = FALSE;
    SMBCE_EXCHANGE_STATUS ExchangeStatus;

    SmbCeAcquireSpinLock();

    ASSERT(!BooleanFlagOn(pExchange->SmbCeFlags,SMBCE_EXCHANGE_FINALIZED));

    if (PendingOperationMask & SMBCE_LOCAL_OPERATION) {
        ASSERT(pExchange->LocalPendingOperations > 0);
        pExchange->LocalPendingOperations--;
    }

    if (PendingOperationMask & SMBCE_SEND_COMPLETE_OPERATION) {
        ASSERT(pExchange->SendCompletePendingOperations > 0);
        pExchange->SendCompletePendingOperations--;
    }

    if (PendingOperationMask & SMBCE_COPY_DATA_OPERATION) {
        ASSERT(pExchange->CopyDataPendingOperations > 0);
        pExchange->CopyDataPendingOperations--;
    }

    if ((PendingOperationMask & SMBCE_RECEIVE_OPERATION) &&
        (pExchange->ReceivePendingOperations > 0)) {
        pExchange->ReceivePendingOperations--;
    }

    if ((pExchange->ReceivePendingOperations == 0) &&
        (pExchange->CopyDataPendingOperations == 0) &&
        (pExchange->SendCompletePendingOperations == 0) &&
        (pExchange->LocalPendingOperations == 0)) {

        fFinalizeExchange = TRUE;
        ExchangeStatus = SmbCeExchangeFinalized;
        pExchange->SmbCeFlags |= SMBCE_EXCHANGE_FINALIZED;
    } else {
        ExchangeStatus = SmbCeExchangeNotFinalized;
    }

    SmbCeReleaseSpinLock();

    if (fFinalizeExchange) {
        SmbCepFinalizeExchange(pExchange);
    }

    return ExchangeStatus;
}


//
// Default handler implementation of exchange handler functions.
//

NTSTATUS
DefaultSmbExchangeIndError(
    IN PSMB_EXCHANGE pExchange)     // the SMB exchange instance
{
    UNREFERENCED_PARAMETER(pExchange);
    return STATUS_NOT_IMPLEMENTED;
}

NTSTATUS
DefaultSmbExchangeIndReceive(
    IN PSMB_EXCHANGE 	pExchange)    // The exchange instance
{
    UNREFERENCED_PARAMETER(pExchange);
    return STATUS_NOT_IMPLEMENTED;
}

NTSTATUS
DefaultSmbExchangeIndSendCallback(
    IN PSMB_EXCHANGE 	pExchange)    // The exchange instance
{
    UNREFERENCED_PARAMETER(pExchange);
    return STATUS_NOT_IMPLEMENTED;
}


