/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

    netroot.c

Abstract:

    This module implements the routines for creating the SMB net root.

Author:

    Balan Sethu Raman      [SethuR]      7-March-1995

Revision History:

--*/

#include "precomp.h"
#pragma hdrstop

#include "exsessup.h"
#include "dfsfsctl.h"

//
//  The Bug check file id for this module
//

#define BugCheckFileId  (RDBSS_BUG_CHECK_SMB_NETROOT)

//
//  The local debug trace level
//

#define Dbg (DEBUG_TRACE_DISPATCH)

#ifdef  ALLOC_PRAGMA
#pragma alloc_text(PAGE, MRxSmbUpdateNetRootState)
#pragma alloc_text(PAGE, MRxSmbGetDialectFlagsFromSrvCall)
#pragma alloc_text(PAGE, MRxSmbCreateVNetRoot)
#pragma alloc_text(PAGE, MRxSmbFinalizeNetRoot)
#pragma alloc_text(PAGE, SmbCeReconnect)
#pragma alloc_text(PAGE, SmbCeEstablishConnection)
#pragma alloc_text(PAGE, SmbConstructNetRootExchangeStart)
#pragma alloc_text(PAGE, MRxSmbExtractNetRootName)
#endif

//
// Forward declarations ...
//

extern NTSTATUS
SmbCeParseConstructNetRootResponse(
   PSMB_CONSTRUCT_NETROOT_EXCHANGE pNetRootExchange,
   PSMB_HEADER                     pSmbHeader,
   ULONG                           BytesAvailable,
   ULONG                           BytesIndicated,
   ULONG                           *pBytesTaken);

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

typedef struct _SMBCE_NETROOT_CONSTRUCTION_CONTEXT {
    NTSTATUS                      Status;

    PMRX_CREATENETROOT_CONTEXT    pCreateNetRootContext;
    PMRX_V_NET_ROOT               pVNetRoot;

    RX_WORK_QUEUE_ITEM            WorkQueueItem;
} SMBCE_NETROOT_CONSTRUCTION_CONTEXT,
  *PSMBCE_NETROOT_CONSTRUCTION_CONTEXT;

NTSTATUS
MRxSmbCreateVNetRootOffLine(
    IN PMRX_CREATENETROOT_CONTEXT pCreateNetRootContext);

NTSTATUS
MRxSmbUpdateNetRootState(
    IN OUT PMRX_NET_ROOT pNetRoot)
/*++

Routine Description:

   This routine update the mini redirector state associated with a net root.

Arguments:

    pNetRoot - the net root instance.

Return Value:

    RXSTATUS - The return status for the operation

Notes:

    By diffrentiating the mini redirewctor state from the net rot condition it is possible
    to permit a variety of reconnect strategies. It is conceivable that the RDBSS considers
    a net root to be good while the underlying mini redirector might mark it as invalid
    and reconnect on the fly.

--*/
{
    if (pNetRoot->MRxNetRootState == MRX_NET_ROOT_STATE_GOOD) {
        if (pNetRoot->Context == NULL) {
            pNetRoot->MRxNetRootState = MRX_NET_ROOT_STATE_ERROR;
        } else {
            PSMBCEDB_SERVER_ENTRY   pServerEntry;

            pServerEntry = SmbCeReferenceAssociatedServerEntry(pNetRoot->pSrvCall);
            if (pServerEntry != NULL) {
                if (pServerEntry->Server.CscState == ServerCscDisconnected) {
                    pNetRoot->MRxNetRootState = MRX_NET_ROOT_STATE_GOOD;
                } else {
                    switch (pServerEntry->Header.State) {
                    case SMBCEDB_ACTIVE:
                         pNetRoot->MRxNetRootState = MRX_NET_ROOT_STATE_GOOD;
                         break;
                    case SMBCEDB_INVALID:
                         pNetRoot->MRxNetRootState = MRX_NET_ROOT_STATE_DISCONNECTED;
                         break;
                    case SMBCEDB_CONSTRUCTION_IN_PROGRESS:
                         pNetRoot->MRxNetRootState = MRX_NET_ROOT_STATE_RECONN;
                         break;
                    default:
                         pNetRoot->MRxNetRootState = MRX_NET_ROOT_STATE_ERROR;
                         break;
                    }
                }

                SmbCeDereferenceServerEntry(pServerEntry);
            } else {
                pNetRoot->MRxNetRootState = MRX_NET_ROOT_STATE_ERROR;
            }
        }
    }

    return STATUS_SUCCESS;
}

ULONG
MRxSmbGetDialectFlagsFromSrvCall(
    PMRX_SRV_CALL SrvCall
    )
{
   ULONG DialectFlags;
   PSMBCEDB_SERVER_ENTRY pServerEntry;

   PAGED_CODE();

   pServerEntry = SmbCeReferenceAssociatedServerEntry(SrvCall);
   ASSERT(pServerEntry != NULL);
   DialectFlags = pServerEntry->Server.DialectFlags;
   SmbCeDereferenceServerEntry(pServerEntry);
   return(DialectFlags);
}

BOOLEAN
MRxSmbIsThisACscAgentOpen(
    IN PRX_CONTEXT RxContext
    )
/*++

Routine Description:

   This routine determines if the open was made by the user mode CSC agent.

Arguments:

    RxContext - the RDBSS context

Return Value:

    TRUE - if it is an agent open, FALSE otherwise

Notes:

    The agent opens are always satisfied by going to the server. They are never
    satisfied from the cached copies. This enables reintegration using snapshots
    even when the files are being currently used.

--*/
{
    BOOLEAN AgentOpen = FALSE;
    ULONG   EaInformationLength;

    PDFS_NAME_CONTEXT pDfsNameContext;

    if (RxContext->Create.EaLength > 0) {
        PFILE_FULL_EA_INFORMATION pEaEntry;

        pEaEntry = (PFILE_FULL_EA_INFORMATION)RxContext->Create.EaBuffer;
        ASSERT(pEaEntry != NULL);

        for(;;) {
            if (strcmp(pEaEntry->EaName, EA_NAME_CSCAGENT) == 0) {
                AgentOpen = TRUE;
                break;
            }

            if (pEaEntry->NextEntryOffset == 0) {
                break;
            } else {
                pEaEntry = (PFILE_FULL_EA_INFORMATION)
                           ((PCHAR) pEaEntry + pEaEntry->NextEntryOffset);
            }
        }
    }

    pDfsNameContext = RxContext->Create.NtCreateParameters.DfsNameContext;

    if ((pDfsNameContext != NULL) &&
        (pDfsNameContext->NameContextType == DFS_CSCAGENT_NAME_CONTEXT)) {
        AgentOpen = TRUE;
    }

    return AgentOpen;
}

NTSTATUS
MRxSmbCreateVNetRoot(
    IN PMRX_CREATENETROOT_CONTEXT pCreateNetRootContext
    )
/*++

Routine Description:

   This routine patches the RDBSS created net root instance with the information required
   by the mini redirector.

   In case the connection cannot be established, the mini redirector tries to transition
   the VNetRoot into disconnected mode and establishes the connection off-line. If the
   connection failes to establish in the synchronouse way, this routine will do the transition;
   Otherwise, SmbConstructNetRootExchangeFinalize routine will try the transition. In both
   cases, MRxSmbCreateVNetRoot will be called again to establish the connection in disconnected
   mode.

Arguments:

    pVNetRoot - the virtual net root instance.

    pCreateNetRootContext - the net root context for calling back

Return Value:

    RXSTATUS - The return status for the operation

Notes:
    CODE.IMPROVEMENT --  The net root create context must supply the open mode in order
    to enable the mini redirector to implement a wide variety of reconnect strategies.

--*/
{
    NTSTATUS  Status = STATUS_MORE_PROCESSING_REQUIRED;
    PRX_CONTEXT pRxContext = pCreateNetRootContext->RxContext;
    PMRX_V_NET_ROOT pVNetRoot = (PMRX_V_NET_ROOT)pCreateNetRootContext->pVNetRoot;

    PMRX_SRV_CALL pSrvCall;
    PMRX_NET_ROOT pNetRoot;

    PSMBCEDB_SERVER_ENTRY pServerEntry;

    PUNICODE_STRING pNetRootName,pSrvCallName;

    BOOLEAN  fInitializeNetRoot;
    BOOLEAN  fDeferNetworkInitialization = FALSE;
    BOOLEAN  fCscAgentOpen = FALSE;
    BOOLEAN  fDisconnectedOperation;
    BOOLEAN  CallBack = FALSE;
    extern DWORD   hShareReint;

    PAGED_CODE();

    pNetRoot = pVNetRoot->pNetRoot;
    pSrvCall = pNetRoot->pSrvCall;

    pServerEntry = SmbCeGetAssociatedServerEntry(pSrvCall);

    if ((pRxContext != NULL) &&
        (pRxContext->MajorFunction == IRP_MJ_CREATE)) {
        fCscAgentOpen = MRxSmbIsThisACscAgentOpen(pRxContext);

        if (pRxContext->Create.ThisIsATreeConnectOpen){
            // Determine if this tree connect was initiated by a CSC agent
            InterlockedIncrement(&MRxSmbStatistics.UseCount);
        }

        fDeferNetworkInitialization = pRxContext->Create.TreeConnectOpenDeferred;
    }

    SmbCeLog(("SmbCreateVNetRoot CscAgent %x %wZ \n",fCscAgentOpen,pNetRoot->pNetRootName));

    SmbLog(LOG,
           MRxSmbCreateVNetRoot,
           LOGUCHAR(fCscAgentOpen)
           LOGUSTR(*pNetRoot->pNetRootName));

    fInitializeNetRoot = (pNetRoot->Context == NULL);

    fDisconnectedOperation = (SmbCeIsServerInDisconnectedMode(pServerEntry) &&
                              !fCscAgentOpen);

    if (fCscAgentOpen &&
        SmbCeIsServerInDisconnectedMode(pServerEntry)) {
        // this is an EA open. we want this one to succeed but want others to still
        // stay offline till an ioctl comes down to say we want to transition

        CscPrepareServerEntryForOnlineOperationPartial(pServerEntry);
    }

    ASSERT((NodeType(pNetRoot) == RDBSS_NTC_NETROOT) &&
           (NodeType(pNetRoot->pSrvCall) == RDBSS_NTC_SRVCALL));

    if (pNetRoot->Type == NET_ROOT_MAILSLOT) {
        pVNetRoot->Context = NULL;
        Status = STATUS_SUCCESS;
        RxDbgTrace( 0, Dbg, ("Mailslot open\n"));
    } else if ((pNetRoot->Type == NET_ROOT_PIPE) &&
               (pServerEntry->Header.State == SMBCEDB_ACTIVE)) {
        if (fDisconnectedOperation) {
            pVNetRoot->Context = NULL;
            Status = STATUS_BAD_NETWORK_NAME;
        } else if (SmbCeGetServerType(pServerEntry) == SMBCEDB_FILE_SERVER &&
                   !FlagOn(MRxSmbGetDialectFlagsFromSrvCall(pSrvCall),DF_LANMAN10)) {
            pVNetRoot->Context = NULL;
            Status = STATUS_NOT_SUPPORTED;
            RxDbgTrace( 0, Dbg, ("pipe open to core server\n"));
        }
    }

    if (Status == STATUS_MORE_PROCESSING_REQUIRED) {
        Status = SmbCeFindOrConstructVNetRootContext(
                     pVNetRoot,
                     fDeferNetworkInitialization,
                     fCscAgentOpen);
    }

    if (Status == STATUS_MORE_PROCESSING_REQUIRED) {
        // Update the flags on the VNetRootContext to indicate if this is a
        // agent open

        Status = SmbCeEstablishConnection(
                     pVNetRoot,
                     pCreateNetRootContext,
                     fInitializeNetRoot);
    }

    if (Status != STATUS_PENDING) {
        if (fCscAgentOpen && Status == STATUS_RETRY) {
            Status = STATUS_CONNECTION_DISCONNECTED;
        }

        if (!NT_SUCCESS(Status)) {
            if (!fCscAgentOpen &&
                !SmbCeIsServerInDisconnectedMode(pServerEntry)) {
                // if it cannot establish the connect and didn't get chance to transition
                // into disconnected state, we should try the transition and if succeed,
                // establish the connect again in the disconnected state.

                Status = CscTransitionVNetRootForDisconnectedOperation(
                             pCreateNetRootContext->RxContext,
                             pVNetRoot,
                             Status);
            }

            if (fInitializeNetRoot &&
                (pNetRoot->Context != NULL)) {
                PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry;

                SmbCeAcquireResource();

                pNetRootEntry = SmbCeGetAssociatedNetRootEntry(pNetRoot);

                if (pNetRootEntry != NULL) {
                    pNetRootEntry->pRdbssNetRoot = NULL;
                    SmbCeDereferenceNetRootEntry(pNetRootEntry);
                }

                pNetRoot->Context = NULL;

                SmbCeReleaseResource();
            }

            SmbCeDestroyAssociatedVNetRootContext(pVNetRoot);

            if ((pRxContext != NULL) &&
                (pRxContext->MajorFunction == IRP_MJ_CREATE) &&
                (pRxContext->Create.ThisIsATreeConnectOpen)) {
                InterlockedIncrement(&MRxSmbStatistics.FailedUseCount);
            }
        }

        pCreateNetRootContext->VirtualNetRootStatus = Status;

        if (fInitializeNetRoot) {
            pCreateNetRootContext->NetRootStatus = Status;
        } else {
            pCreateNetRootContext->NetRootStatus = STATUS_SUCCESS;
        }

        if (Status == STATUS_RETRY) {
            // STATUS_RETRY is returned from CscTransitionVNetRootForDisconnectedOperation if the
            // server entry is transitioned into disconnected mode.
            Status = MRxSmbCreateVNetRootOffLine(pCreateNetRootContext);

            if (Status != STATUS_SUCCESS) {
                // Callback the RDBSS for resumption if create VNetRootOffLine fails
                CallBack = TRUE;
            }
        } else {
            CallBack = TRUE;
        }

        // Map the error code to STATUS_PENDING since this triggers the synchronization
        // mechanism in the RDBSS.
        Status = STATUS_PENDING;
    }

    if (CallBack) {
        IF_NOT_MRXSMB_CSC_ENABLED{
            NOTHING;
        } else {
            if (pCreateNetRootContext->NetRootStatus == STATUS_SUCCESS){
                if ((pRxContext != NULL) &&
                    (pRxContext->MajorFunction == IRP_MJ_CREATE) &&
                    (pNetRoot->Type == NET_ROOT_DISK)) {
                    MRxSmbCscPartOfCreateVNetRoot(pRxContext,pNetRoot);
                }
            }
        }

        if (pServerEntry->Server.IsRemoteBootServer &&
            pCreateNetRootContext->NetRootStatus != STATUS_SUCCESS) {
            pCreateNetRootContext->NetRootStatus = STATUS_RETRY;
            pCreateNetRootContext->VirtualNetRootStatus = STATUS_RETRY;
        }

        // Callback the RDBSS for resumption.
        pCreateNetRootContext->Callback(pCreateNetRootContext);
    }

    return Status;
}

NTSTATUS
MRxSmbFinalizeVNetRoot(
    IN PMRX_V_NET_ROOT pVNetRoot,
    IN PBOOLEAN        ForceDisconnect)
/*++

Routine Description:


Arguments:

    pVNetRoot - the virtual net root

    ForceDisconnect - disconnect is forced

Return Value:

    RXSTATUS - The return status for the operation

--*/
{
    PSMBCE_V_NET_ROOT_CONTEXT pVNetRootContext;
    PSMBCEDB_SESSION_ENTRY pDefaultSessionEntry;

    // This cannot be paged code since we meed to protect the default session list with the lock

    RxDbgTrace( 0, Dbg, ("MRxSmbFinalizeVNetRoot %lx\n",pVNetRoot));

    if (pVNetRoot->Context != NULL) {
        SmbCeDestroyAssociatedVNetRootContext(pVNetRoot);
    }

    return STATUS_SUCCESS;
}


NTSTATUS
MRxSmbFinalizeNetRoot(
    IN PMRX_NET_ROOT   pNetRoot,
    IN PBOOLEAN        ForceDisconnect)
/*++

Routine Description:


Arguments:

    pVirtualNetRoot - the virtual net root

    ForceDisconnect - disconnect is forced

Return Value:

    RXSTATUS - The return status for the operation

--*/
{
    PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry;

    PAGED_CODE();

    RxDbgTrace( 0, Dbg, ("MRxSmbFinalizeNetRoot %lx\n",pNetRoot));

    if (pNetRoot->Context != NULL) {
        SmbCeAcquireResource();

        pNetRootEntry = SmbCeGetAssociatedNetRootEntry(pNetRoot);

        InterlockedCompareExchangePointer(
            &pNetRootEntry->pRdbssNetRoot,
            NULL,
            pNetRoot);

        SmbCeDereferenceNetRootEntry(pNetRootEntry);

        ASSERT(!FlagOn(pNetRoot->Flags,NETROOT_FLAG_FINALIZE_INVOKED));
        SetFlag(pNetRoot->Flags,NETROOT_FLAG_FINALIZE_INVOKED);

        SmbCeReleaseResource();
    }

    return STATUS_SUCCESS;
}

VOID
SmbCeReconnectCallback(
   PMRX_CREATENETROOT_CONTEXT pCreateNetRootContext)
/*++

Routine Description:

   This routine signals the completion of a reconnect attempt

Arguments:

    pCreateNetRootContext - the net root context

Return Value:

    RXSTATUS - The return status for the operation

--*/
{
   KeSetEvent(&pCreateNetRootContext->FinishEvent, IO_NETWORK_INCREMENT, FALSE );
}

NTSTATUS
SmbCeReconnect(
    IN PMRX_V_NET_ROOT            pVNetRoot)
/*++

Routine Description:

   This routine reconnects, i.e, establishes a new session and tree connect to a previously
   connected serverb share

Arguments:

    pVNetRoot - the virtual net root instance.

Return Value:

    RXSTATUS - The return status for the operation

--*/
{
    NTSTATUS Status;
    PSMBCEDB_SERVER_ENTRY     pServerEntry;
    PSMBCEDB_SESSION_ENTRY    pSessionEntry;
    PSMBCEDB_NET_ROOT_ENTRY   pNetRootEntry;
    PSMBCE_V_NET_ROOT_CONTEXT pVNetRootContext = (PSMBCE_V_NET_ROOT_CONTEXT)pVNetRoot->Context;

    PMRX_CREATENETROOT_CONTEXT pCreateNetRootContext;

    PAGED_CODE();

    if ((pVNetRootContext != NULL) &&
        (pVNetRootContext->Header.State == SMBCEDB_ACTIVE)) {
        pServerEntry  = pVNetRootContext->pServerEntry;
        pSessionEntry = pVNetRootContext->pSessionEntry;
        pNetRootEntry = pVNetRootContext->pNetRootEntry;

        if ((pServerEntry->Header.State == SMBCEDB_ACTIVE) &&
            (pSessionEntry->Header.State == SMBCEDB_ACTIVE) &&
            (pNetRootEntry->Header.State == SMBCEDB_ACTIVE)) {
            return STATUS_SUCCESS;
        }
    }

    pCreateNetRootContext = (PMRX_CREATENETROOT_CONTEXT)
                            RxAllocatePoolWithTag(
                                NonPagedPool,
                                sizeof(MRX_CREATENETROOT_CONTEXT),
                                MRXSMB_NETROOT_POOLTAG);

    if (pCreateNetRootContext != NULL) {
        for (;;) {
            pCreateNetRootContext->pVNetRoot  = (PV_NET_ROOT)pVNetRoot;
            pCreateNetRootContext->NetRootStatus  = STATUS_SUCCESS;
            pCreateNetRootContext->VirtualNetRootStatus = STATUS_SUCCESS;
            pCreateNetRootContext->Callback       = SmbCeReconnectCallback;
            pCreateNetRootContext->RxContext      = NULL;

            KeInitializeEvent(
                &pCreateNetRootContext->FinishEvent,
                SynchronizationEvent,
                FALSE );

            // Since this is a reconnect instance the net root initialization is not required
            Status = SmbCeEstablishConnection(
                         pVNetRoot,
                         pCreateNetRootContext,
                         FALSE);

            if (Status == STATUS_PENDING) {
                // Wait for the construction to be completed.
                KeWaitForSingleObject(
                    &pCreateNetRootContext->FinishEvent,
                    Executive,
                    KernelMode,
                    FALSE,
                    NULL);

                Status = pCreateNetRootContext->VirtualNetRootStatus;
            }

            if (Status != STATUS_LINK_FAILED) {
                break;
            }
        }

        RxFreePool(pCreateNetRootContext);
    } else {
        Status = STATUS_INSUFFICIENT_RESOURCES;
    }

    return Status;
}

NTSTATUS
SmbCeEstablishConnection(
    IN OUT PMRX_V_NET_ROOT        pVNetRoot,
    IN PMRX_CREATENETROOT_CONTEXT pCreateNetRootContext,
    IN BOOLEAN                    fInitializeNetRoot
    )
/*++

Routine Description:

   This routine triggers off the connection attempt for initial establishment of a
   connection as well as subsequent reconnect attempts.

Arguments:

    pVNetRoot - the virtual net root instance.

    pCreateNetRootContext - the net root context for calling back

Return Value:

    RXSTATUS - The return status for the operation

Notes:
    CODE.IMPROVEMENT --  The net root create context must supply the open mode in order
    to enable the mini redirector to implement a wide variety of reconnect strategies.

--*/
{
    NTSTATUS Status;

    PSMBCEDB_SERVER_ENTRY     pServerEntry;
    PSMBCEDB_SESSION_ENTRY    pSessionEntry;
    PSMBCEDB_NET_ROOT_ENTRY   pNetRootEntry;
    PSMBCE_V_NET_ROOT_CONTEXT pVNetRootContext;

    PAGED_CODE();

    pVNetRootContext = SmbCeGetAssociatedVNetRootContext(pVNetRoot);
    
    if (pVNetRootContext == NULL) {
        Status = STATUS_BAD_NETWORK_PATH;
    } else {
        pServerEntry  = pVNetRootContext->pServerEntry;
        pSessionEntry = pVNetRootContext->pSessionEntry;
        pNetRootEntry = pVNetRootContext->pNetRootEntry;

        Status = STATUS_SUCCESS;
    }

    if (Status == STATUS_SUCCESS) {
        //
        // The following code initializes the NetRootEntry, VNetRootContext and
        // the session entry under certain cases.
        //
        // The session entry to a doenlevel server needs to be initialized. This
        // is not handled by the previous code since the session  entry and the
        // net root entry initialization can be combined into one exchange.
        //
        // The net root entry has not been initialized, i.e., this corresponds to
        // the construction of the first SMBCE_V_NET_ROOT_CONTEXT instance for a
        // given NetRootEntry.
        //
        // Subsequent SMBCE_V_NET_ROOT context constructions. In these cases the
        // construction of each context must obtain a new TID
        //

        BOOLEAN fNetRootExchangeRequired;

        fNetRootExchangeRequired = ((pSessionEntry->Header.State != SMBCEDB_ACTIVE) ||
                                   !BooleanFlagOn(pVNetRootContext->Flags,SMBCE_V_NET_ROOT_CONTEXT_FLAG_VALID_TID));

        if (fNetRootExchangeRequired) {
            // This is a tree connect open which needs to be triggered immediately.
            PSMB_EXCHANGE                  pSmbExchange;
            PSMB_CONSTRUCT_NETROOT_EXCHANGE pNetRootExchange;

            pSmbExchange = SmbMmAllocateExchange(CONSTRUCT_NETROOT_EXCHANGE,NULL);
            if (pSmbExchange != NULL) {
                Status = SmbCeInitializeExchange(
                             &pSmbExchange,
                             NULL,
                             pVNetRoot,
                             CONSTRUCT_NETROOT_EXCHANGE,
                             &ConstructNetRootExchangeDispatch);

                if (Status == RX_MAP_STATUS(SUCCESS)) {
                    pNetRootExchange = (PSMB_CONSTRUCT_NETROOT_EXCHANGE)pSmbExchange;

                    // Attempt to reconnect( In this case it amounts to establishing the
                    // connection/session)
                    pNetRootExchange->SmbCeFlags |= (SMBCE_EXCHANGE_ATTEMPT_RECONNECTS |
                                                   SMBCE_EXCHANGE_TIMED_RECEIVE_OPERATION);

                    // Initialize the continuation for resumption upon completion of the
                    // tree connetcion.
                    pNetRootExchange->NetRootCallback       = pCreateNetRootContext->Callback;
                    pNetRootExchange->pCreateNetRootContext = pCreateNetRootContext;
                    pNetRootExchange->RxContext = pCreateNetRootContext->RxContext;

                    pNetRootExchange->fInitializeNetRoot =  fInitializeNetRoot;

                    IF_NOT_MRXSMB_BUILD_FOR_DISCONNECTED_CSC {
                        // Initiate the exchange.
                        Status = SmbCeInitiateExchange(pSmbExchange);

                        if (Status != STATUS_PENDING) {
                            SmbCeDiscardExchangeWorkerThreadRoutine(pSmbExchange);
                        }
                    } else {
                        if (!SmbCeIsServerInDisconnectedMode(pServerEntry) ||
                            FlagOn(
                                pVNetRootContext->Flags,
                                SMBCE_V_NET_ROOT_CONTEXT_CSCAGENT_INSTANCE)) {
                            // Initiate the exchange.
                            Status = SmbCeInitiateExchange(pSmbExchange);

                            if (Status != STATUS_PENDING) {
                                SmbCeDiscardExchangeWorkerThreadRoutine(pSmbExchange);
                            }
                        }  else {
                            //dont really initiate...just set up to call the completion
                            //routine which BTW discards the exchange
                            Status = MRxSmbCscDisconnectedConnect(pNetRootExchange);
                        }
                    }
                }
            } else {
                Status = STATUS_INSUFFICIENT_RESOURCES;
            }
        }
    }

    return Status;
}

//
// The net roots are normally constructed as part of some other exchange, i.e., the SMB for
// Tree connect is compounded with other operations. However, there is one situation in which
// the tree connect SMB needs to be sent by itself. This case refers to the prefix claim
// situation ( net use command ). This is handled by the construct net root exchange.
//

#define CONSTRUCT_NETROOT_BUFFER_SIZE (4096)

NTSTATUS
SmbConstructNetRootExchangeStart(
      PSMB_EXCHANGE  pExchange)
/*++

Routine Description:

    This is the start routine for net root construction 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

--*/
{
    NTSTATUS Status;
    NTSTATUS RequestLockStatus = STATUS_UNSUCCESSFUL;
    NTSTATUS ResponseLockStatus = STATUS_UNSUCCESSFUL;

    PVOID pSmbActualBuffer;
    PVOID pSmbBuffer;
    UCHAR SmbCommand,LastCommandInHeader;
    ULONG SmbLength;

    PUCHAR pCommand;

    PMDL  pSmbRequestMdl,pSmbResponseMdl;
    ULONG SmbMdlSize;

    PSMB_CONSTRUCT_NETROOT_EXCHANGE pNetRootExchange;
    PSMBCEDB_SERVER_ENTRY pServerEntry = SmbCeGetExchangeServerEntry(pExchange);
    PMRX_NET_ROOT pNetRoot = pExchange->SmbCeContext.pVNetRoot->pNetRoot;

    PAGED_CODE();

    pNetRootExchange = (PSMB_CONSTRUCT_NETROOT_EXCHANGE)pExchange;

    ASSERT(pNetRootExchange->Type == CONSTRUCT_NETROOT_EXCHANGE);

    if ((pNetRoot->Type == NET_ROOT_PIPE) &&
        !FlagOn(pServerEntry->Server.DialectFlags,DF_LANMAN10)) {
        RxDbgTrace( 0, Dbg, ("pipe open to core server\n"));
        return STATUS_NOT_SUPPORTED;
    }

    pSmbRequestMdl = pSmbResponseMdl = NULL;

    pSmbActualBuffer = RxAllocatePoolWithTag(
                           PagedPool | POOL_COLD_ALLOCATION,
                           (CONSTRUCT_NETROOT_BUFFER_SIZE + TRANSPORT_HEADER_SIZE),
                           MRXSMB_NETROOT_POOLTAG);

    if (pSmbActualBuffer != NULL) {
        PSMBCE_SERVER pServer = SmbCeGetExchangeServer(pExchange);

        (PCHAR) pSmbBuffer = (PCHAR) pSmbActualBuffer + TRANSPORT_HEADER_SIZE;

        Status = SmbCeBuildSmbHeader(
                     pExchange,
                     pSmbBuffer,
                     CONSTRUCT_NETROOT_BUFFER_SIZE,
                     &SmbLength,
                     &LastCommandInHeader,
                     &pCommand);

        // Ensure that the NET_ROOT/SESSION still needs to be constructed before
        // sending it. It is likely that they were costructed by an earlier exchange
        if (NT_SUCCESS(Status) &&
            (SmbLength > sizeof(SMB_HEADER))) {

            if (LastCommandInHeader != SMB_COM_TREE_CONNECT){
                *pCommand = SMB_COM_NO_ANDX_COMMAND;
            }

            RxAllocateHeaderMdl(
                pSmbBuffer,
                SmbLength,
                pSmbRequestMdl
                );

            pSmbResponseMdl = RxAllocateMdl(pSmbBuffer,CONSTRUCT_NETROOT_BUFFER_SIZE);

            if ((pSmbRequestMdl != NULL) &&
                (pSmbResponseMdl != NULL)) {

                RxProbeAndLockHeaderPages(
                    pSmbRequestMdl,
                    KernelMode,
                    IoModifyAccess,
                    RequestLockStatus);

                RxProbeAndLockPages(
                    pSmbResponseMdl,
                    KernelMode,
                    IoModifyAccess,
                    ResponseLockStatus);

                if ((Status  == STATUS_SUCCESS) &&
                    ((Status = RequestLockStatus)  == STATUS_SUCCESS) &&
                    ((Status = ResponseLockStatus) == STATUS_SUCCESS)) {

                    pNetRootExchange->pSmbResponseMdl = pSmbResponseMdl;
                    pNetRootExchange->pSmbRequestMdl  = pSmbRequestMdl;
                    pNetRootExchange->pSmbActualBuffer = pSmbActualBuffer;
                    pNetRootExchange->pSmbBuffer      = pSmbBuffer;

                    Status = SmbCeTranceive(
                                 pExchange,
                                 (RXCE_SEND_PARTIAL | RXCE_SEND_SYNCHRONOUS),
                                 pNetRootExchange->pSmbRequestMdl,
                                 SmbLength);

                    RxDbgTrace( 0, Dbg, ("Net Root SmbCeTranceive returned %lx\n",Status));
                }
            } else {
                Status = STATUS_INSUFFICIENT_RESOURCES;
            }

            if ((Status != STATUS_PENDING) &&
                (Status != STATUS_SUCCESS)) {

                pNetRootExchange->pSmbResponseMdl = NULL;
                pNetRootExchange->pSmbRequestMdl  = NULL;
                pNetRootExchange->pSmbActualBuffer = NULL;
                pNetRootExchange->pSmbBuffer      = NULL;

                if (pSmbResponseMdl != NULL) {
                    if (ResponseLockStatus == STATUS_SUCCESS) {
                        MmUnlockPages(pSmbResponseMdl);
                    }

                    IoFreeMdl(pSmbResponseMdl);
                }

                if (pSmbRequestMdl != NULL) {
                    if (RequestLockStatus == STATUS_SUCCESS) {
                        RxUnlockHeaderPages(pSmbRequestMdl);
                    }

                    IoFreeMdl(pSmbRequestMdl);
                }

                RxFreePool(pSmbActualBuffer);
            }
        } else {

            RxFreePool(pSmbActualBuffer);
        }
    } else {
        Status = STATUS_INSUFFICIENT_RESOURCES;
    }

    return Status;
}

NTSTATUS
SmbConstructNetRootExchangeReceive(
    IN struct _SMB_EXCHANGE *pExchange,
    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 net root construction 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:

    NTSTATUS - The return status for the operation

Notes:

    This routine is called at DPC level.

--*/
{
    NTSTATUS Status;

    PSMB_CONSTRUCT_NETROOT_EXCHANGE pNetRootExchange;

    pNetRootExchange = (PSMB_CONSTRUCT_NETROOT_EXCHANGE)pExchange;

    RxDbgTrace( 0, (DEBUG_TRACE_ALWAYS), ("ParseSmbHeader BytesIndicated/Available %ld %ld\n",BytesIndicated,BytesAvailable));

    if (BytesAvailable > BytesIndicated ||
        !FlagOn(ReceiveFlags,TDI_RECEIVE_ENTIRE_MESSAGE)) {
        // The SMB response was not completely returned. Post a copy data request to
        // get the remainder of the response. If the response is greater than the original
        // buffer size, abort this connection request and consume the bytes available.

        if (BytesAvailable > CONSTRUCT_NETROOT_BUFFER_SIZE) {
            ASSERT(!"not enough bytes in parsesmbheader.....sigh.............."); // To be removed soon ...
            pExchange->Status = STATUS_NOT_IMPLEMENTED;
            *pBytesTaken = BytesAvailable;
            Status       = RX_MAP_STATUS(SUCCESS);
        } else {
            *pBytesTaken        = 0;
            *pDataBufferPointer = pNetRootExchange->pSmbResponseMdl;
            *pDataSize          = CONSTRUCT_NETROOT_BUFFER_SIZE;
            Status              = STATUS_MORE_PROCESSING_REQUIRED;
        }
    } else {
        // The SMB exchange completed without an error.
        pExchange->Status = SmbCeParseConstructNetRootResponse(
                                 pNetRootExchange,
                                 pSmbHeader,
                                 BytesAvailable,
                                 BytesIndicated,
                                 pBytesTaken);

        RxDbgTrace( 0, (DEBUG_TRACE_ALWAYS), ("ParseSmbHeader BytesTaken %ld\n",*pBytesTaken));
        RxDbgTrace( 0, (DEBUG_TRACE_ALWAYS), ("ParseSmbHeader Return Status %lx\n",pExchange->Status));
        Status = STATUS_SUCCESS;
    }

    return Status;
}

NTSTATUS
SmbConstructNetRootExchangeCopyDataHandler(
    IN PSMB_EXCHANGE    pExchange,
    IN PMDL       pCopyDataBuffer,
    IN ULONG            DataSize)
/*++

Routine Description:

    This is the copy data handling routine for net root construction exchanges

Arguments:

    pExchange - the exchange instance

Return Value:

    NTSTATUS - The return status for the operation

--*/
{
    PSMB_CONSTRUCT_NETROOT_EXCHANGE pNetRootExchange;

    PSMB_HEADER pSmbHeader;
    ULONG       ResponseSize = DataSize;
    ULONG       ResponseBytesConsumed = 0;
    NTSTATUS    Status = STATUS_SUCCESS;

    pNetRootExchange = (PSMB_CONSTRUCT_NETROOT_EXCHANGE)pExchange;
    ASSERT(pCopyDataBuffer == pNetRootExchange->pSmbResponseMdl);

    pSmbHeader = (PSMB_HEADER)MmGetSystemAddressForMdlSafe(pNetRootExchange->pSmbResponseMdl,LowPagePriority);

    if (pSmbHeader != NULL) {
        pExchange->Status = SmbCeParseConstructNetRootResponse(
                               pNetRootExchange,
                               pSmbHeader,
                               ResponseSize,
                               ResponseSize,
                               &ResponseBytesConsumed);
    } else {
        Status = STATUS_INSUFFICIENT_RESOURCES;
    }

    RxDbgTrace( 0, (DEBUG_TRACE_ALWAYS), ("ParseSmbHeader BytesTaken %ld\n",ResponseBytesConsumed));

    return Status;
}

NTSTATUS
SmbCeParseConstructNetRootResponse(
    PSMB_CONSTRUCT_NETROOT_EXCHANGE pNetRootExchange,
    PSMB_HEADER                     pSmbHeader,
    ULONG                           BytesAvailable,
    ULONG                           BytesIndicated,
    ULONG                           *pBytesTaken)
{
    NTSTATUS     Status,SmbResponseStatus;
    GENERIC_ANDX CommandToProcess;

    RxDbgTrace( 0, (DEBUG_TRACE_ALWAYS), ("ParseSmbHeader BytesIndicated %ld\n",BytesIndicated));
    Status = SmbCeParseSmbHeader(
                 (PSMB_EXCHANGE)pNetRootExchange,
                 pSmbHeader,
                 &CommandToProcess,
                 &SmbResponseStatus,
                 BytesAvailable,
                 BytesIndicated,
                 pBytesTaken);

    if (Status == STATUS_SUCCESS) {
        *pBytesTaken = BytesIndicated;
    }

    return Status;
}


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

Routine Description:

    This routine finalizes the construct net root exchange. It resumes the RDBSS by invoking
    the call back and discards the exchange

Arguments:

    pExchange - the exchange instance

    CurrentIrql - the current interrupt request level

    pPostFinalize - a pointer to a BOOLEAN if the request should be posted

Return Value:

    RXSTATUS - The return status for the operation

--*/
{
    PSMB_CONSTRUCT_NETROOT_EXCHANGE pNetRootExchange;
    PMRX_CREATENETROOT_CONTEXT      pCreateNetRootContext;
    PMRX_NETROOT_CALLBACK           pNetRootCallback;

    PMRX_V_NET_ROOT pVNetRoot;
    PMRX_NET_ROOT   pNetRoot;
    PSMBCEDB_SERVER_ENTRY pServerEntry = SmbCeGetExchangeServerEntry(pExchange);

    PSMBCE_V_NET_ROOT_CONTEXT pVNetRootContext;
    NTSTATUS Status = pExchange->Status;

    if (RxShouldPostCompletion()) {
        *pPostFinalize = TRUE;
        return STATUS_SUCCESS;
    } else {
        *pPostFinalize = FALSE;
    }

    pVNetRoot = SmbCeGetExchangeVNetRoot(pExchange);
    pNetRoot  = pVNetRoot->pNetRoot;

    pVNetRootContext = SmbCeGetAssociatedVNetRootContext(pVNetRoot);

    ASSERT((pVNetRoot == NULL) || (pVNetRoot->pNetRoot == pNetRoot));
    pNetRootExchange = (PSMB_CONSTRUCT_NETROOT_EXCHANGE)pExchange;
    pNetRootCallback = pNetRootExchange->NetRootCallback;

    ASSERT(pNetRootExchange->Type == CONSTRUCT_NETROOT_EXCHANGE);

    pCreateNetRootContext = pNetRootExchange->pCreateNetRootContext;

    pCreateNetRootContext->VirtualNetRootStatus = RX_MAP_STATUS(SUCCESS);
    pCreateNetRootContext->NetRootStatus        = RX_MAP_STATUS(SUCCESS);

    RxDbgTrace(0,Dbg,("SmbConstructNetRootExchangeFinalize: Net Root Exchange Status %lx\n", pExchange->Status));
    if (!NT_SUCCESS(pExchange->Status)) {
        if (pCreateNetRootContext->RxContext &&
            pCreateNetRootContext->RxContext->Create.ThisIsATreeConnectOpen){
            InterlockedIncrement(&MRxSmbStatistics.FailedUseCount);
        }

        if (!SmbCeIsServerInDisconnectedMode(pServerEntry) &&
            !FlagOn(pVNetRootContext->Flags,SMBCE_V_NET_ROOT_CONTEXT_CSCAGENT_INSTANCE)) {
            // if it cannot establish the connect and didn't get chance to transition
            // into disconnected state, we should try the transition and if succeed,
            // establish the connect again in the disconnected state.

            Status = CscTransitionVNetRootForDisconnectedOperation(
                                   pCreateNetRootContext->RxContext,
                                   pVNetRoot,
                                   pExchange->Status);
        }

        pCreateNetRootContext->VirtualNetRootStatus = Status;

        if (pCreateNetRootContext->VirtualNetRootStatus == STATUS_INVALID_HANDLE) {
            pCreateNetRootContext->VirtualNetRootStatus = STATUS_UNEXPECTED_NETWORK_ERROR;
        }

        if (pNetRootExchange->fInitializeNetRoot) {
            pCreateNetRootContext->NetRootStatus = Status;

            if (pCreateNetRootContext->NetRootStatus == STATUS_INVALID_HANDLE) {
                pCreateNetRootContext->NetRootStatus = STATUS_UNEXPECTED_NETWORK_ERROR;
            }
        }

        SmbCeUpdateVNetRootContextState(
            pVNetRootContext,
            SMBCEDB_MARKED_FOR_DELETION);
    } else {
        PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry;

        pNetRootEntry = SmbCeGetExchangeNetRootEntry(pExchange);

        if(pCreateNetRootContext->RxContext)
        {
            MRxSmbCscPartOfCreateVNetRoot(pCreateNetRootContext->RxContext, pNetRoot);
        }

        // Update the associated wrapper data structures.
        SmbCeUpdateNetRoot(pNetRootEntry,pNetRoot);
    }

    SmbCeReferenceVNetRootContext(pVNetRootContext);
    SmbCeCompleteVNetRootContextInitialization(pVNetRootContext);
    pExchange->SmbCeFlags &= ~SMBCE_EXCHANGE_NETROOT_CONSTRUCTOR;

    ASSERT((pCreateNetRootContext->VirtualNetRootStatus != STATUS_SUCCESS) || (pVNetRoot->Context != NULL));

    if ((pCreateNetRootContext->NetRootStatus == STATUS_CONNECTION_RESET)||(pCreateNetRootContext->NetRootStatus == STATUS_IO_TIMEOUT))
    {
        SmbCeLog(("!!Remote Reset Status=%x\n", pCreateNetRootContext->NetRootStatus));
        SmbLogError(pCreateNetRootContext->NetRootStatus,
                    LOG,
                    SmbConstructNetRootExchangeFinalize,
                    LOGULONG(pCreateNetRootContext->NetRootStatus)
                    LOGPTR(pNetRoot)
                    LOGUSTR(*pNetRoot->pNetRootName));
    }

    if (pNetRootExchange->pSmbResponseMdl != NULL) {
        MmUnlockPages(pNetRootExchange->pSmbResponseMdl);
        IoFreeMdl(pNetRootExchange->pSmbResponseMdl);
    }

    if (pNetRootExchange->pSmbRequestMdl != NULL) {
        RxUnlockHeaderPages(pNetRootExchange->pSmbRequestMdl);
        IoFreeMdl(pNetRootExchange->pSmbRequestMdl);
    }

    if (pNetRootExchange->pSmbActualBuffer != NULL) {

        RxFreePool(pNetRootExchange->pSmbActualBuffer);
    }

    // Tear down the exchange instance ...
    SmbCeDiscardExchangeWorkerThreadRoutine(pExchange);

    if (Status == STATUS_RETRY) {
        // create VNetRoot offline if server entry is transitioned into disconnected state
        Status = MRxSmbCreateVNetRootOffLine(pCreateNetRootContext);

        if (Status != STATUS_SUCCESS) {
            // Callback the RDBSS for resumption if create VNetRoot offline fails
            pNetRootCallback(pCreateNetRootContext);
        }
    } else {
        if (pServerEntry->Server.IsRemoteBootServer &&
            pCreateNetRootContext->NetRootStatus != STATUS_SUCCESS) {
            pCreateNetRootContext->NetRootStatus = STATUS_RETRY;
            pCreateNetRootContext->VirtualNetRootStatus = STATUS_RETRY;
        }   
        
        // Callback the RDBSS for resumption
        pNetRootCallback(pCreateNetRootContext);
    }

    return STATUS_SUCCESS;
}

NTSTATUS
MRxSmbCreateVNetRootOffLine(
    PMRX_CREATENETROOT_CONTEXT pCreateNetRootContext)
{
    NTSTATUS Status;

    Status = RxPostToWorkerThread(
                 MRxSmbDeviceObject,
                 CriticalWorkQueue,
                 &pCreateNetRootContext->WorkQueueItem,
                 MRxSmbCreateVNetRoot,
                 pCreateNetRootContext);

    return Status;
}




SMB_EXCHANGE_DISPATCH_VECTOR
ConstructNetRootExchangeDispatch = {
                                       SmbConstructNetRootExchangeStart,
                                       SmbConstructNetRootExchangeReceive,
                                       SmbConstructNetRootExchangeCopyDataHandler,
                                       NULL,  // No SendCompletionHandler
                                       SmbConstructNetRootExchangeFinalize,
                                       NULL
                                   };


VOID
MRxSmbExtractNetRootName(
    IN PUNICODE_STRING FilePathName,
    IN PMRX_SRV_CALL   SrvCall,
    OUT PUNICODE_STRING NetRootName,
    OUT PUNICODE_STRING RestOfName OPTIONAL
    )
/*++

Routine Description:

    This routine parses the input name into srv, netroot, and the
    rest.

Arguments:


--*/
{
    UNICODE_STRING xRestOfName;

    ULONG length = FilePathName->Length;
    PWCH w = FilePathName->Buffer;
    PWCH wlimit = (PWCH)(((PCHAR)w)+length);
    PWCH wlow;

    PAGED_CODE();

    w += (SrvCall->pSrvCallName->Length/sizeof(WCHAR));
    NetRootName->Buffer = wlow = w;
    for (;;) {
        if (w>=wlimit) break;
        if ( (*w == OBJ_NAME_PATH_SEPARATOR) && (w!=wlow) ){
#if ZZZ_MODE
            if (*(w-1) == L'z') {
                w++;
                continue;
            }
#endif //if ZZZ_MODE
            break;
        }
        w++;
    }
    NetRootName->Length = NetRootName->MaximumLength
                = (USHORT)((PCHAR)w - (PCHAR)wlow);

    if (!RestOfName) RestOfName = &xRestOfName;
    RestOfName->Buffer = w;
    RestOfName->Length = RestOfName->MaximumLength
                       = (USHORT)((PCHAR)wlimit - (PCHAR)w);

    RxDbgTrace( 0,Dbg,("  MRxSmbExtractNetRootName FilePath=%wZ\n",FilePathName));
    RxDbgTrace(0,Dbg,("         Srv=%wZ,Root=%wZ,Rest=%wZ\n",
                        SrvCall->pSrvCallName,NetRootName,RestOfName));

    return;
}



