/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

    netroot.c

Abstract:

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


--*/

#include "precomp.h"
#pragma hdrstop


//
//  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)

//
// 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);



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

Routine Description:

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

Arguments:

    pNetRoot - the net root instance.

Return Value:

    NTSTATUS - The return status for the operation

Notes:

    By diffrentiating the mini redirector state from the net root 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->Context == NULL) {
      pNetRoot->MRxNetRootState = MRX_NET_ROOT_STATE_ERROR;
   } else {
      PSMBCEDB_SERVER_ENTRY   pServerEntry;

      pServerEntry = SmbCeReferenceAssociatedServerEntry(pNetRoot->pSrvCall);

      if (pServerEntry != NULL) {

      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;

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


NTSTATUS
MRxIfsCreateVNetRoot(
    IN OUT PMRX_V_NET_ROOT        pVNetRoot,
    IN PMRX_CREATENETROOT_CONTEXT pCreateNetRootContext
    )
/*++

Routine Description:

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

Arguments:

    pVNetRoot - the virtual net root instance.

    pCreateNetRootContext - the net root context for calling back

Return Value:

    NTSTATUS - The return status for the operation

Notes:

--*/
{
   NTSTATUS  Status;
   PRX_CONTEXT pRxContext = pCreateNetRootContext->RxContext;

   PMRX_SRV_CALL pSrvCall;
   PMRX_NET_ROOT pNetRoot;

   PUNICODE_STRING pNetRootName,pSrvCallName;
   BOOLEAN  fTreeConnectOpen = TRUE; // RxContext->Create.ThisIsATreeConnectOpen;
   BOOLEAN  fInitializeNetRoot;
   BOOLEAN  fDereferenceSessionEntry = FALSE;
   BOOLEAN  fDereferenceNetRootEntry = FALSE;

   if (pRxContext->Create.ThisIsATreeConnectOpen){
       InterlockedIncrement(&MRxIfsStatistics.UseCount);
   }

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

   // The V_NET_ROOT is associated with a NET_ROOT. The two cases of interest are as
   // follows
   // 1) the V_NET_ROOT and the associated NET_ROOT are being newly created.
   // 2) a new V_NET_ROOT associated with an existing NET_ROOT is being created.
   //
   // These two cases can be distinguished by checking if the context associated with
   // NET_ROOT is NULL. Since the construction of NET_ROOT's/V_NET_ROOT's are serialized
   // by the wrapper this is a safe check.
   // ( The wrapper cannot have more then one thread tryingto initialize the same
   // NET_ROOT).

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

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

   Status = STATUS_SUCCESS;

   // update the net root state to be good.

   if (fInitializeNetRoot) {
      pNetRoot->MRxNetRootState = MRX_NET_ROOT_STATE_GOOD;
   }

   if (Status == STATUS_SUCCESS)
   {

       Status = SmbCeInitializeSessionEntry(pVNetRoot);
       fDereferenceSessionEntry = (Status == STATUS_SUCCESS);

      if (fInitializeNetRoot && (Status == STATUS_SUCCESS)) {
         Status = SmbCeInitializeNetRootEntry(pNetRoot);

         fDereferenceNetRootEntry = (Status == STATUS_SUCCESS);
         RxDbgTrace( 0, Dbg, ("SmbCeOpenNetRoot %lx\n",Status));
      }

      if (NT_SUCCESS(Status)) {
         if (fTreeConnectOpen &&
             (pNetRoot->Type != NET_ROOT_MAILSLOT)) {
            Status = SmbCeEstablishConnection(pVNetRoot,pCreateNetRootContext);
         } else {
            Status = STATUS_SUCCESS;
         }
      }
   }

   if (Status != STATUS_PENDING) {
      if (!NT_SUCCESS(Status)) {
         if (fInitializeNetRoot && fDereferenceNetRootEntry) {
            SmbCeDereferenceNetRootEntry(SmbCeGetAssociatedNetRootEntry(pNetRoot));
            pNetRoot->Context  = NULL;
         }

         if (fDereferenceSessionEntry) {
            SmbCeDereferenceSessionEntry(SmbCeGetAssociatedSessionEntry(pVNetRoot));
            pVNetRoot->Context = NULL;
         }
      }

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

      if (pRxContext->Create.ThisIsATreeConnectOpen){
          InterlockedIncrement(&MRxIfsStatistics.FailedUseCount);
      }

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

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

   return Status;
}


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

Routine Description:


Arguments:

    pVNetRoot - the virtual net root

    ForceDisconnect - disconnect is forced

Return Value:

    NTSTATUS - The return status for the operation

--*/
{
   PSMBCEDB_SESSION_ENTRY pSessionEntry;
   PSMBCEDB_SERVER_ENTRY  pServerEntry;
   PSMBCEDB_SESSION_ENTRY pDefaultSessionEntry;

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

   if (pVNetRoot->Context != NULL)
   {
      pSessionEntry = SmbCeGetAssociatedSessionEntry(pVNetRoot);
      pServerEntry  = SmbCeGetAssociatedServerEntry(pVNetRoot->pNetRoot->pSrvCall);

      pDefaultSessionEntry = SmbCeGetDefaultSessionEntry(pServerEntry);

      if ((pDefaultSessionEntry != NULL) && (pDefaultSessionEntry == pSessionEntry))
      {
         SmbCeAcquireSpinLock();

         if (pDefaultSessionEntry->pRdbssVNetRoot == pVNetRoot)
         {
            SmbCeSetDefaultSessionEntryLite(pServerEntry,NULL);
         }

         SmbCeReleaseSpinLock();
         DbgPrint("Resetting default session entry to NULL\n");
      }

      SmbCeDereferenceSessionEntry(pSessionEntry);
   }

   return STATUS_SUCCESS;
}


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

Routine Description:


Arguments:

    pVirtualNetRoot - the virtual net root

    ForceDisconnect - disconnect is forced

Return Value:

    NTSTATUS - The return status for the operation

--*/
{
   PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry;

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

   if (pNetRoot->Context != NULL)
   {
      pNetRootEntry = SmbCeGetAssociatedNetRootEntry(pNetRoot);
      SmbCeDereferenceNetRootEntry(pNetRootEntry);
   }

   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:

    NTSTATUS - 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:

    NTSTATUS - The return status for the operation

--*/
{
   NTSTATUS Status;

   PMRX_CREATENETROOT_CONTEXT pCreateNetRootContext;

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

   if (pCreateNetRootContext != NULL) {
      pCreateNetRootContext->NetRootStatus  = STATUS_SUCCESS;
      pCreateNetRootContext->VirtualNetRootStatus = STATUS_SUCCESS;
      pCreateNetRootContext->Callback       = SmbCeReconnectCallback;
      pCreateNetRootContext->RxContext      = NULL;
      KeInitializeEvent( &pCreateNetRootContext->FinishEvent, SynchronizationEvent, FALSE );

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

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

   return Status;
}


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

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:

    NTSTATUS - The return status for the operation

Notes:

--*/
{
   NTSTATUS                Status;

   PSMBCEDB_SERVER_ENTRY   pServerEntry;
   PSMBCEDB_SESSION_ENTRY  pSessionEntry;
   PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry;

   pServerEntry = SmbCeReferenceAssociatedServerEntry(pVNetRoot->pNetRoot->pSrvCall);
   if (pServerEntry != NULL) {
      pNetRootEntry = SmbCeReferenceAssociatedNetRootEntry(pVNetRoot->pNetRoot);
      if (pNetRootEntry != NULL) {
         pSessionEntry = SmbCeReferenceAssociatedSessionEntry(pVNetRoot);
         if (pSessionEntry != NULL) {
            Status = STATUS_SUCCESS;
         } else {
            SmbCeDereferenceNetRootEntry(pNetRootEntry);
            Status = STATUS_CONNECTION_DISCONNECTED;
         }
      } else {
         Status = STATUS_CONNECTION_DISCONNECTED;
      }

      if (Status != STATUS_SUCCESS) {
         SmbCeDereferenceServerEntry(pServerEntry);
      }
   } else {
      Status = STATUS_BAD_NETWORK_PATH;
   }

   if (Status != STATUS_SUCCESS) {
      return Status;
   }


   if ((Status == STATUS_SUCCESS) &&
       ((pNetRootEntry->Header.State != SMBCEDB_ACTIVE) ||
        ((pSessionEntry->Header.State != SMBCEDB_ACTIVE) &&
         !(pServerEntry->Server.DialectFlags & DF_KERBEROS)))) {
      // 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,
                        pVNetRoot,
                        CONSTRUCT_NETROOT_EXCHANGE,
                        &ConstructNetRootExchangeDispatch);

         if (Status == 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;
            // Initialize the continuation for resumption upon completion of the
            // tree connetcion.
            pNetRootExchange->NetRootCallback       = pCreateNetRootContext->Callback;
            pNetRootExchange->pCreateNetRootContext = pCreateNetRootContext;

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

            if (Status != STATUS_PENDING) {
               SmbCeDiscardExchange(pSmbExchange);
            }
         }
      } else {
         Status = STATUS_INSUFFICIENT_RESOURCES;
      }
   }

   SmbCeDereferenceEntries(pServerEntry,pSessionEntry,pNetRootEntry);

   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:

    NTSTATUS - The return status for the operation

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

   PVOID pSmbBuffer;
   UCHAR SmbCommand,LastCommandInHeader;
   ULONG SmbLength;

   PUCHAR pCommand;

   PMDL  pSmbRequestMdl,pSmbResponseMdl;
   ULONG SmbMdlSize;

   PSMB_CONSTRUCT_NETROOT_EXCHANGE pNetRootExchange;

   pNetRootExchange = (PSMB_CONSTRUCT_NETROOT_EXCHANGE)pExchange;

   ASSERT(pNetRootExchange->Type == CONSTRUCT_NETROOT_EXCHANGE);

   pSmbRequestMdl = pSmbResponseMdl = NULL;

   pSmbBuffer = RxAllocatePoolWithTag(
                    PagedPool | POOL_COLD_ALLOCATION,
                    CONSTRUCT_NETROOT_BUFFER_SIZE,
                    MRXSMB_NETROOT_POOLTAG);

   if (pSmbBuffer != NULL) {
      PSMBCE_SERVER pServer = &pExchange->SmbCeContext.pServerEntry->Server;

      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;
         }

         pSmbRequestMdl  = RxAllocateMdl(pSmbBuffer,CONSTRUCT_NETROOT_BUFFER_SIZE);
         pSmbResponseMdl = RxAllocateMdl(pSmbBuffer,CONSTRUCT_NETROOT_BUFFER_SIZE);

         if ((pSmbRequestMdl != NULL) &&
             (pSmbResponseMdl != NULL)) {
            RxProbeAndLockPages(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->pSmbBuffer      = pSmbBuffer;

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

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

         if ((Status != STATUS_PENDING) &&
             (Status != STATUS_SUCCESS)) {
            pNetRootExchange->pSmbResponseMdl = NULL;
            pNetRootExchange->pSmbRequestMdl  = NULL;
            pNetRootExchange->pSmbBuffer      = NULL;

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

               IoFreeMdl(pSmbResponseMdl);
            }

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

               IoFreeMdl(pSmbRequestMdl);
            }

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

   return Status;
}

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

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) {
      // 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       = STATUS_SUCCESS;
      } else {
         *pBytesTaken        = 0;
         *pDataBufferPointer = pNetRootExchange->pSmbResponseMdl;
         *pDataSize          = BytesAvailable;
         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,    // The exchange instance
    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;

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

   pSmbHeader = (PSMB_HEADER)MmGetSystemAddressForMdl(pNetRootExchange->pSmbResponseMdl);

   pExchange->Status = SmbCeParseConstructNetRootResponse(
                           pNetRootExchange,
                           pSmbHeader,
                           ResponseSize,
                           ResponseSize,
                           &ResponseBytesConsumed);

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

   return STATUS_SUCCESS;
}

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:

    NTSTATUS - The return status for the operation

--*/
{
   PSMB_CONSTRUCT_NETROOT_EXCHANGE pNetRootExchange;
   PMRX_CREATENETROOT_CONTEXT      pCreateNetRootContext;

   PMRX_V_NET_ROOT pVNetRoot;
   PMRX_NET_ROOT   pNetRoot;

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

   pVNetRoot = pExchange->SmbCeContext.pVNetRoot;
   pNetRoot  = pVNetRoot->pNetRoot;
   ASSERT((pVNetRoot == NULL) || (pVNetRoot->pNetRoot == pNetRoot));
   pNetRootExchange = (PSMB_CONSTRUCT_NETROOT_EXCHANGE)pExchange;

   ASSERT(pNetRootExchange->Type == CONSTRUCT_NETROOT_EXCHANGE);

   pCreateNetRootContext = pNetRootExchange->pCreateNetRootContext;

   pCreateNetRootContext->VirtualNetRootStatus = STATUS_SUCCESS;
   pCreateNetRootContext->NetRootStatus        = 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(&MRxIfsStatistics.FailedUseCount);
      }

      if (pExchange->SmbCeContext.pSessionEntry->Header.State != SMBCEDB_ACTIVE) {
         pCreateNetRootContext->VirtualNetRootStatus = pExchange->Status;
      }

      if (pExchange->SmbCeContext.pNetRootEntry->Header.State != SMBCEDB_ACTIVE) {
         pCreateNetRootContext->NetRootStatus = pExchange->Status;
      }
   } else {

      if (pExchange->SmbCeContext.pSessionEntry->Header.State == SMBCEDB_ACTIVE) {
         PSMBCEDB_SESSION_ENTRY pSessionEntry = pExchange->SmbCeContext.pSessionEntry;
         PSMBCEDB_SERVER_ENTRY  pServerEntry  = pSessionEntry->pServerEntry;
         PMRX_V_NET_ROOT        pVNetRoot     = pExchange->SmbCeContext.pVNetRoot;

         // Mark this session entry as the default entry if required.
         if (FlagOn(pExchange->SmbCeFlags,SMBCE_EXCHANGE_SESSION_CONSTRUCTOR) &&
             ((pVNetRoot->pUserName != NULL) || (pVNetRoot->pPassword != NULL))) {
            SmbCeAcquireSpinLock();

            if (SmbCeGetDefaultSessionEntry(pServerEntry) == NULL) {
               SmbCeSetDefaultSessionEntryLite(pServerEntry,pSessionEntry);
            }

            SmbCeReleaseSpinLock();
         }
      }

      //if the transaction can identify the netroottype, use it. otherwise, stay with what you have!

      if (pExchange->SmbCeContext.pNetRootEntry->NetRoot.NetRootType != NET_ROOT_WILD){
          pNetRoot->Type = pExchange->SmbCeContext.pNetRootEntry->NetRoot.NetRootType;
      }


      //
      // NOTE:  In this example minirdr, only remote disk access is allowed. This means
      //        that pipes, mailslots, printers, remote comm devices, are NOT supported.
      //        If the user mode caller tries to open a pipe, the open (or tree connect)
      //        will be sent over the wire, but we intercept the response here and
      //        mark it as unsuccessful (actually un-implemented). This after-the-fact
      //        approach allows us to keep existing error recover mechanisms in the
      //        code working.


      switch (pNetRoot->Type) {

         //
         // If the remote access is for a disk, mark the device as a disk and
         // go ahead.
         //

      case NET_ROOT_DISK:
         pNetRoot->DeviceType = RxDeviceType(DISK);
         break;


      case NET_ROOT_PIPE:
      case NET_ROOT_COMM:
      case NET_ROOT_PRINT:
      case NET_ROOT_MAILSLOT:

         //
         // The remote was NOT a disk, so mark the NetRootStatus and VNetRoot
         // status as bad. Code that follows will detect this and return
         // diagnostic information to the caller. The operation fails.
         //


         pCreateNetRootContext->VirtualNetRootStatus = STATUS_NOT_IMPLEMENTED;
         pCreateNetRootContext->NetRootStatus        = STATUS_NOT_IMPLEMENTED;

         RxDbgTrace( 0, (DEBUG_TRACE_ALWAYS), ("SmbConstructNetRootExchangeFinalize: Non-disk attempt forcibly denied\n"));
         break;

      case NET_ROOT_WILD:
         break;

      default:
         ASSERT(!"Valid Net Root Type");
      }
   }

   //
   // Now do the callback.
   //

   pNetRootExchange->NetRootCallback(pCreateNetRootContext);

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

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

   if (pNetRootExchange->pSmbBuffer != NULL) {
      RxFreePool(pNetRootExchange->pSmbBuffer);
   }

   //
   // Get rid of the exchange
   //

   SmbCeDiscardExchange(pExchange);

   return STATUS_SUCCESS;
}

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


VOID
MRxIfsExtractNetRootName(
    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;

    w += (SrvCall->pSrvCallName->Length/sizeof(WCHAR));
    NetRootName->Buffer = wlow = w;
    for (;;) {
        if (w>=wlimit) break;
        if ( (*w == OBJ_NAME_PATH_SEPARATOR) && (w!=wlow) ){
            break;
        }
        w++;
    }
    NetRootName->Length = NetRootName->MaximumLength
                = (PCHAR)w - (PCHAR)wlow;

    if (!RestOfName) RestOfName = &xRestOfName;
    RestOfName->Buffer = w;
    RestOfName->Length = RestOfName->MaximumLength
                       = (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;
}




