windows-nt/Source/XPSP1/NT/base/fs/rdr2/rdbss/smb.mrx/netroot.c
2020-09-26 16:20:57 +08:00

1370 lines
42 KiB
C

/*++
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;
}