3126 lines
104 KiB
C
3126 lines
104 KiB
C
/*++
|
||
Copyright (c) 1987-1999 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
smbcxchng.c
|
||
|
||
Abstract:
|
||
|
||
This is the include file that implements the SMB_*_EXCHANGE creation, deletion and
|
||
dispatch routines.
|
||
|
||
Notes:
|
||
|
||
The exchange engine supports two kinds of changes, timed and untimed exhanges.
|
||
The timed exchanges are distinguished by the SMBCE_EXCHANGE_TIMED_RECEIVE_OPERATION.
|
||
|
||
In addition all exchanges are finalized if the transport is not able to push out
|
||
the data within a specific period of time. This enables us to throttle back
|
||
traffic to a overloaded server. Currently this is a global constant for all exchanges
|
||
and is set to 300 seconds.
|
||
|
||
This time limit only comes into play only when a send complete operation is outstanding
|
||
|
||
The exchanges are put on a timed exchange list ( one for each type of exchange)
|
||
when it is initiated. When a network operation, i.e., tranceive/send/copydata is
|
||
initiated the corresponding expiry time in the exchange is updated by invoking the
|
||
routine SmbCeSetExpiryTime.
|
||
|
||
The echo probes are initiated is invoked through the context of a recurrent service
|
||
(recursvc.c/recursvc.h). Every time this service is invoked (SmbCeProbeServers) it
|
||
in turn invokes SmbCeDetectAndResumeExpiredExchanges. This routine detects those
|
||
exchanges for which the wait for a response has exceeded the time limit and marks
|
||
them for finalization.
|
||
|
||
The finalization is done by SmbCeScavengeTimedOutExchanges in the context of a worker
|
||
thread. Notice that due to the granularity mismatch we treat timeout intervals as
|
||
soft deadlines.
|
||
|
||
--*/
|
||
|
||
#include "precomp.h"
|
||
#pragma hdrstop
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, MRxSmbInitializeSmbCe)
|
||
#pragma alloc_text(PAGE, SmbCeInitiateExchange)
|
||
#pragma alloc_text(PAGE, SmbCeInitiateAssociatedExchange)
|
||
#pragma alloc_text(PAGE, SmbCeExchangeAbort)
|
||
#pragma alloc_text(PAGE, SmbCeBuildSmbHeader)
|
||
#pragma alloc_text(PAGE, SmbCeResumeExchange)
|
||
#pragma alloc_text(PAGE, SmbCepInitializeExchange)
|
||
#pragma alloc_text(PAGE, SmbCeInitializeAssociatedExchange)
|
||
#pragma alloc_text(PAGE, SmbCeTransformExchange)
|
||
#pragma alloc_text(PAGE, SmbCePrepareExchangeForReuse)
|
||
#pragma alloc_text(PAGE, SmbCeDiscardExchange)
|
||
#pragma alloc_text(PAGE, SmbCeFinalizeExchangeWorkerThreadRoutine)
|
||
#pragma alloc_text(PAGE, SmbCeFinalizeExchangeOnDisconnect)
|
||
#pragma alloc_text(PAGE, SmbCeDetectExpiredExchanges)
|
||
#pragma alloc_text(PAGE, DefaultSmbExchangeIndError)
|
||
#pragma alloc_text(PAGE, DefaultSmbExchangeIndReceive)
|
||
#pragma alloc_text(PAGE, DefaultSmbExchangeIndSendCallback)
|
||
#endif
|
||
|
||
ULONG SmbCeTraceExchangeReferenceCount = 0;
|
||
|
||
RXDT_DefineCategory(SMBXCHNG);
|
||
#define Dbg (DEBUG_TRACE_SMBXCHNG)
|
||
|
||
// The exchange engine in the mini redirector requires to maintain enough state
|
||
// to ensure that all the active exchanges are completed correctly when a shut down
|
||
// occurs. Since the exchanges can be finalized by different threads, including
|
||
// posted completions the exchange engine on startup initializes an event upon startup
|
||
// which is subsequently used to signal the terminating condition.
|
||
//
|
||
// The count of active changes has to be tracked continously and the signalling
|
||
// of the event depends upon the number of active exchanges reaching the count of
|
||
// zero and the exchange engine being in a stopped state.
|
||
|
||
SMBCE_STARTSTOP_CONTEXT SmbCeStartStopContext;
|
||
|
||
NTSTATUS
|
||
MRxSmbInitializeSmbCe()
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine initializes the connection engine
|
||
|
||
Return Value:
|
||
|
||
NXSTATUS - The return status for the operation
|
||
|
||
Notes:
|
||
|
||
--*/
|
||
{
|
||
LONG i;
|
||
|
||
PAGED_CODE();
|
||
|
||
KeInitializeEvent(
|
||
&SmbCeStartStopContext.StopEvent,
|
||
NotificationEvent,
|
||
FALSE);
|
||
|
||
SmbCeStartStopContext.ActiveExchanges = 0;
|
||
SmbCeStartStopContext.State = SMBCE_STARTED;
|
||
SmbCeStartStopContext.pServerEntryTearDownEvent = NULL;
|
||
|
||
InitializeListHead(
|
||
&SmbCeStartStopContext.SessionSetupRequests);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
MRxSmbTearDownSmbCe()
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine tears down the connection engine
|
||
|
||
Return Value:
|
||
|
||
NXSTATUS - The return status for the operation
|
||
|
||
Notes:
|
||
|
||
--*/
|
||
{
|
||
BOOLEAN fWait;
|
||
|
||
if (SmbCeStartStopContext.State == SMBCE_STARTED) {
|
||
SmbCeAcquireSpinLock();
|
||
SmbCeStartStopContext.State = SMBCE_STOPPED;
|
||
fWait = (SmbCeStartStopContext.ActiveExchanges > 0);
|
||
SmbCeReleaseSpinLock();
|
||
|
||
if (fWait) {
|
||
KeWaitForSingleObject(
|
||
&SmbCeStartStopContext.StopEvent,
|
||
Executive,
|
||
KernelMode,
|
||
FALSE,
|
||
NULL);
|
||
}
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
SmbCeIncrementActiveExchangeCount()
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine increments the active exchange count
|
||
|
||
Return Value:
|
||
|
||
NXSTATUS - The return status for the operation
|
||
|
||
Notes:
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
SmbCeAcquireSpinLock();
|
||
if (SmbCeStartStopContext.State != SMBCE_STARTED) {
|
||
Status = STATUS_UNSUCCESSFUL;
|
||
} else {
|
||
InterlockedIncrement(&SmbCeStartStopContext.ActiveExchanges);
|
||
}
|
||
SmbCeReleaseSpinLock();
|
||
|
||
return Status;
|
||
}
|
||
|
||
VOID
|
||
SmbCeDecrementActiveExchangeCount()
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine decrements the active exchange count
|
||
|
||
Return Value:
|
||
|
||
NXSTATUS - The return status for the operation
|
||
|
||
Notes:
|
||
|
||
--*/
|
||
{
|
||
LONG FinalRefCount;
|
||
|
||
ASSERT(SmbCeStartStopContext.ActiveExchanges > 0);
|
||
if (InterlockedDecrement(&SmbCeStartStopContext.ActiveExchanges) == 0) {
|
||
SmbCeAcquireSpinLock();
|
||
if (SmbCeStartStopContext.State == SMBCE_STOPPED) {
|
||
KeSetEvent(&SmbCeStartStopContext.StopEvent,0,FALSE);
|
||
}
|
||
SmbCeReleaseSpinLock();
|
||
}
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
SmbCeReferenceServer(
|
||
PSMB_EXCHANGE pExchange)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine initializes the server associated with an exchange.
|
||
|
||
Arguments:
|
||
|
||
pExchange - the exchange to be initialized.
|
||
|
||
Return Value:
|
||
|
||
RXSTATUS - The return status for the operation
|
||
|
||
Notes:
|
||
|
||
The initiation of an exchange proceeds in multiple steps. The first step involves
|
||
referencing the corresponding server,session and netroot entries. Subsequently the
|
||
exchange is placed in a SMB_EXCHANGE_START state and the exchange is dispatched to the
|
||
Start method. The act of referencing the session or the net root may suspend the exchange.
|
||
|
||
The session and net roots are aliased entities, i.e., there is more then one reference
|
||
to it. It is conceivable that the construction is in progress when a reference is made.
|
||
In such cases the exchange is suspended and resumed when the construction is complete.
|
||
|
||
On some transports a reconnect is possible without having to tear down an existing
|
||
connection, i.e. attempting to send a packet reestablishes the connection at the
|
||
lower level. Since this is not supported by all the transports ( with the exception
|
||
of TCP/IP) the reference server entry initiates this process by tearing down the
|
||
existing transport and reinitialising it.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
||
|
||
pServerEntry = SmbCeGetExchangeServerEntry(pExchange);
|
||
|
||
ASSERT(SmbCeIsResourceOwned());
|
||
ASSERT(pExchange->SmbCeState == SMBCE_EXCHANGE_INITIALIZATION_START);
|
||
|
||
if (pServerEntry->Header.State != SMBCEDB_ACTIVE) {
|
||
if (pExchange->SmbCeFlags & SMBCE_EXCHANGE_ATTEMPT_RECONNECTS) {
|
||
switch (pServerEntry->Header.State) {
|
||
case SMBCEDB_INVALID:
|
||
{
|
||
SMBCEDB_OBJECT_STATE State;
|
||
|
||
ASSERT(!pServerEntry->NegotiateInProgress);
|
||
pServerEntry->NegotiateInProgress = TRUE;
|
||
|
||
SmbCeUpdateServerEntryState(
|
||
pServerEntry,
|
||
SMBCEDB_CONSTRUCTION_IN_PROGRESS);
|
||
|
||
SmbCeReleaseResource();
|
||
|
||
// Initialize the transport associated with the server
|
||
Status = SmbCeInitializeServerTransport(pServerEntry,NULL,NULL);
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
|
||
PSMBCEDB_SESSION_ENTRY pSessionEntry =
|
||
SmbCeGetExchangeSessionEntry(pExchange);
|
||
|
||
Status = SmbCeNegotiate(
|
||
pServerEntry,
|
||
pServerEntry->pRdbssSrvCall
|
||
);
|
||
}
|
||
|
||
SmbCeCompleteServerEntryInitialization(pServerEntry,Status);
|
||
|
||
if (Status != STATUS_SUCCESS) {
|
||
// Either the transport initialization failed or the NEGOTIATE
|
||
// SMB could not be sent ....
|
||
|
||
InterlockedIncrement(&MRxSmbStatistics.Reconnects);
|
||
}
|
||
|
||
SmbCeAcquireResource();
|
||
}
|
||
break;
|
||
|
||
case SMBCEDB_CONSTRUCTION_IN_PROGRESS :
|
||
{
|
||
PSMBCEDB_REQUEST_ENTRY pRequestEntry;
|
||
|
||
pRequestEntry = (PSMBCEDB_REQUEST_ENTRY)
|
||
SmbMmAllocateObject(SMBCEDB_OT_REQUEST);
|
||
if (pRequestEntry != NULL) {
|
||
// Enqueue the request entry.
|
||
pRequestEntry->ReconnectRequest.Type = RECONNECT_REQUEST;
|
||
pRequestEntry->ReconnectRequest.pExchange = pExchange;
|
||
|
||
SmbCeIncrementPendingLocalOperations(pExchange);
|
||
SmbCeAddRequestEntry(&pServerEntry->OutstandingRequests,pRequestEntry);
|
||
|
||
Status = STATUS_PENDING;
|
||
} else {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
}
|
||
break;
|
||
|
||
default :
|
||
Status = STATUS_CONNECTION_DISCONNECTED;
|
||
break;
|
||
}
|
||
} else {
|
||
Status = STATUS_CONNECTION_DISCONNECTED;
|
||
}
|
||
}
|
||
|
||
if ((Status == STATUS_SUCCESS) || (Status == STATUS_PENDING)) {
|
||
pExchange->SmbCeState = SMBCE_EXCHANGE_SERVER_INITIALIZED;
|
||
}
|
||
|
||
ASSERT(SmbCeIsResourceOwned());
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
SmbCeReferenceSession(
|
||
PSMB_EXCHANGE pExchange)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine initializes the session associated with an exchange.
|
||
|
||
Arguments:
|
||
|
||
pExchange - the exchange to be initialized.
|
||
|
||
Return Value:
|
||
|
||
RXSTATUS - The return status for the operation
|
||
|
||
Notes:
|
||
|
||
The initiation of an exchange proceeds in multiple steps. The first step involves
|
||
referencing the corresponding server,session and netroot entries. Subsequently the
|
||
exchange is placed in a SMB_EXCHANGE_START state and the exchange is dispatched to the
|
||
Start method. The act of referencing the session or the net root may suspend the exchange.
|
||
|
||
The session and net roots are aliased entities, i.e., there is more then one reference
|
||
to it. It is conceivable that the construction is in progress when a reference is made.
|
||
In such cases the exchange is suspended and resumed when the construction is complete.
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
BOOLEAN fReestablishSession;
|
||
BOOLEAN UnInitializeSecurityContext = FALSE;
|
||
|
||
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
||
PSMBCEDB_SESSION_ENTRY pSessionEntry;
|
||
|
||
fReestablishSession = BooleanFlagOn(
|
||
pExchange->SmbCeFlags,
|
||
SMBCE_EXCHANGE_ATTEMPT_RECONNECTS);
|
||
|
||
for (;;) {
|
||
|
||
pServerEntry = SmbCeGetExchangeServerEntry(pExchange);
|
||
pSessionEntry = SmbCeGetExchangeSessionEntry(pExchange);
|
||
|
||
ASSERT(pServerEntry->Header.ObjectType == SMBCEDB_OT_SERVER);
|
||
ASSERT(pExchange->SmbCeState == SMBCE_EXCHANGE_SERVER_INITIALIZED);
|
||
ASSERT(SmbCeGetServerType(pServerEntry) == SMBCEDB_FILE_SERVER);
|
||
ASSERT(SmbCeIsResourceOwned());
|
||
|
||
Status = STATUS_USER_SESSION_DELETED;
|
||
|
||
if (pSessionEntry == NULL) {
|
||
break;
|
||
}
|
||
|
||
switch (pSessionEntry->Header.State) {
|
||
case SMBCEDB_ACTIVE:
|
||
Status = STATUS_SUCCESS;
|
||
break;
|
||
|
||
case SMBCEDB_INVALID:
|
||
if (!fReestablishSession) {
|
||
break;
|
||
}
|
||
|
||
RxDbgTrace( 0, Dbg, ("SmbCeReferenceSession: Reestablishing session\n"));
|
||
// fall thru ...
|
||
|
||
case SMBCEDB_START_CONSTRUCTION:
|
||
{
|
||
RxDbgTrace( 0, Dbg, ("SmbCeReferenceSession: Reestablishing session\n"));
|
||
pSessionEntry->Session.UserId = 0;
|
||
|
||
ASSERT(SmbCeGetServerType(pServerEntry) == SMBCEDB_FILE_SERVER);
|
||
pExchange->SmbCeFlags |= SMBCE_EXCHANGE_SESSION_CONSTRUCTOR;
|
||
pSessionEntry->pExchange = pExchange;
|
||
pSessionEntry->Header.State = SMBCEDB_CONSTRUCTION_IN_PROGRESS;
|
||
Status = STATUS_SUCCESS;
|
||
|
||
}
|
||
break;
|
||
|
||
case SMBCEDB_CONSTRUCTION_IN_PROGRESS:
|
||
if (fReestablishSession) {
|
||
// The construction of the session is already in progress ....
|
||
// Queue up the request to resume this exchange when the session
|
||
// construction is complete.
|
||
|
||
PSMBCEDB_REQUEST_ENTRY pRequestEntry;
|
||
|
||
ASSERT(SmbCeGetServerType(pServerEntry) == SMBCEDB_FILE_SERVER);
|
||
|
||
pRequestEntry = (PSMBCEDB_REQUEST_ENTRY)
|
||
SmbMmAllocateObject(SMBCEDB_OT_REQUEST);
|
||
|
||
if (pRequestEntry != NULL) {
|
||
pRequestEntry->Request.pExchange = pExchange;
|
||
|
||
SmbCeIncrementPendingLocalOperations(pExchange);
|
||
SmbCeAddRequestEntry(&pSessionEntry->Requests,pRequestEntry);
|
||
|
||
Status = STATUS_PENDING;
|
||
} else {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
fReestablishSession = FALSE;
|
||
}
|
||
break;
|
||
|
||
case SMBCEDB_MARKED_FOR_DELETION:
|
||
Status = STATUS_USER_SESSION_DELETED;
|
||
break;
|
||
|
||
default:
|
||
ASSERT(!"Valid Session State, SmbCe database corrupt");
|
||
Status = STATUS_USER_SESSION_DELETED;
|
||
}
|
||
|
||
{
|
||
if (UnInitializeSecurityContext) {
|
||
SmbCeReleaseResource();
|
||
UninitializeSecurityContextsForSession(&pSessionEntry->Session);
|
||
SmbCeAcquireResource();
|
||
}
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
if ((Status == STATUS_SUCCESS) || (Status == STATUS_PENDING)) {
|
||
pExchange->SmbCeState = SMBCE_EXCHANGE_SESSION_INITIALIZED;
|
||
}
|
||
|
||
ASSERT(SmbCeIsResourceOwned());
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
SmbCeReferenceNetRoot(
|
||
PSMB_EXCHANGE pExchange)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine initializes the net root associated with an exchange.
|
||
|
||
Arguments:
|
||
|
||
pExchange - the exchange to be initialized.
|
||
|
||
Return Value:
|
||
|
||
RXSTATUS - The return status for the operation
|
||
|
||
Notes:
|
||
|
||
The initiation of an exchange proceeds in multiple steps. The first step involves
|
||
referencing the corresponding server,session and netroot entries. Subsequently the
|
||
exchange is placed in a SMB_EXCHANGE_START state and the exchange is dispatched to the
|
||
Start method. The act of referencing the session or the net root may suspend the exchange.
|
||
|
||
The session and net roots are aliased entities, i.e., there is more then one reference
|
||
to it. It is conceivable that the construction is in progress when a reference is made.
|
||
In such cases the exchange is suspended and resumed when the construction is complete.
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
BOOLEAN fReconnectNetRoot;
|
||
|
||
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
||
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry;
|
||
|
||
PSMBCE_V_NET_ROOT_CONTEXT pVNetRootContext;
|
||
|
||
pVNetRootContext = SmbCeGetAssociatedVNetRootContext(pExchange->SmbCeContext.pVNetRoot);
|
||
|
||
pServerEntry = SmbCeGetExchangeServerEntry(pExchange);
|
||
pNetRootEntry = SmbCeGetExchangeNetRootEntry(pExchange);
|
||
|
||
ASSERT(pExchange->SmbCeState == SMBCE_EXCHANGE_SESSION_INITIALIZED);
|
||
ASSERT(SmbCeIsResourceOwned());
|
||
|
||
Status = STATUS_CONNECTION_DISCONNECTED;
|
||
fReconnectNetRoot = BooleanFlagOn(pExchange->SmbCeFlags,SMBCE_EXCHANGE_ATTEMPT_RECONNECTS);
|
||
|
||
switch (pVNetRootContext->Header.State) {
|
||
case SMBCEDB_ACTIVE:
|
||
ASSERT(pNetRootEntry->Header.ObjectType == SMBCEDB_OT_NETROOT);
|
||
ASSERT(pServerEntry->Header.ObjectType == SMBCEDB_OT_SERVER);
|
||
Status = STATUS_SUCCESS;
|
||
break;
|
||
|
||
case SMBCEDB_INVALID:
|
||
RxDbgTrace( 0, Dbg, ("SmbCeReferenceNetRoot: Reestablishing net root\n"));
|
||
if (!fReconnectNetRoot) {
|
||
break;
|
||
}
|
||
ClearFlag(
|
||
pVNetRootContext->Flags,
|
||
SMBCE_V_NET_ROOT_CONTEXT_FLAG_VALID_TID);
|
||
|
||
pVNetRootContext->TreeId = 0;
|
||
// fall thru
|
||
|
||
case SMBCEDB_START_CONSTRUCTION:
|
||
pExchange->SmbCeFlags |= SMBCE_EXCHANGE_NETROOT_CONSTRUCTOR;
|
||
pVNetRootContext->pExchange = pExchange;
|
||
pVNetRootContext->Header.State = SMBCEDB_CONSTRUCTION_IN_PROGRESS;
|
||
Status = STATUS_SUCCESS;
|
||
break;
|
||
|
||
case SMBCEDB_CONSTRUCTION_IN_PROGRESS:
|
||
if (fReconnectNetRoot) {
|
||
// The construction of the net root is already in progress ....
|
||
// Queue up the request to resume this exchange when the session
|
||
// construction is complete.
|
||
PSMBCEDB_REQUEST_ENTRY pRequestEntry;
|
||
|
||
pRequestEntry = (PSMBCEDB_REQUEST_ENTRY)
|
||
SmbMmAllocateObject(SMBCEDB_OT_REQUEST);
|
||
|
||
if (pRequestEntry != NULL) {
|
||
pRequestEntry->Request.pExchange = pExchange;
|
||
|
||
SmbCeIncrementPendingLocalOperations(pExchange);
|
||
SmbCeAddRequestEntry(&pVNetRootContext->Requests,pRequestEntry);
|
||
|
||
Status = STATUS_PENDING;
|
||
} else {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case SMBCEDB_MARKED_FOR_DELETION:
|
||
break;
|
||
|
||
default:
|
||
ASSERT(!"Valid NetRoot State, SmbCe database corrupt");
|
||
break;
|
||
}
|
||
|
||
if ((Status == STATUS_SUCCESS) || (Status == STATUS_PENDING)) {
|
||
pExchange->SmbCeState = SMBCE_EXCHANGE_NETROOT_INITIALIZED;
|
||
}
|
||
|
||
ASSERT(SmbCeIsResourceOwned());
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
SmbCeInitiateExchange(
|
||
PSMB_EXCHANGE pExchange)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine inititaes a exchange.
|
||
|
||
Arguments:
|
||
|
||
pExchange - the exchange to be initiated.
|
||
|
||
Return Value:
|
||
|
||
RXSTATUS - The return status for the operation
|
||
|
||
Notes:
|
||
|
||
The initiation of an exchange proceeds in multiple steps. The first step involves
|
||
referencing the corresponding server,session and netroot entries. Subsequently the
|
||
exchange is placed in a SMB_EXCHANGE_START state and the exchange is dispatched to the
|
||
Start method. The act of referencing the session or the net root may suspend the exchange.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
||
PSMBCEDB_SESSION_ENTRY pSessionEntry;
|
||
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry;
|
||
|
||
PKEVENT pSmbCeSynchronizationEvent;
|
||
|
||
PAGED_CODE();
|
||
|
||
pServerEntry = SmbCeGetExchangeServerEntry(pExchange);
|
||
pSessionEntry = SmbCeGetExchangeSessionEntry(pExchange);
|
||
pNetRootEntry = SmbCeGetExchangeNetRootEntry(pExchange);
|
||
|
||
ASSERT(pServerEntry != NULL);
|
||
ASSERT(!FlagOn(pExchange->SmbCeFlags,SMBCE_ASSOCIATED_EXCHANGE));
|
||
|
||
switch (SmbCeGetServerType(pServerEntry)) {
|
||
case SMBCEDB_FILE_SERVER:
|
||
// Admin exchanges do not have these fields filled in. All the three
|
||
// entries must be valid for all other exchanges.
|
||
if ((pExchange->NodeTypeCode != SMB_EXCHANGE_NTC(ADMIN_EXCHANGE)) &&
|
||
((pNetRootEntry == NULL) ||
|
||
(pSessionEntry == NULL))) {
|
||
Status = STATUS_REQUEST_ABORTED;
|
||
break;
|
||
}
|
||
break;
|
||
|
||
default:
|
||
// Prepare for aborting the request if either the server type is invalid
|
||
// or if the netroot entry or the session entry is invalid.
|
||
Status = STATUS_REQUEST_ABORTED;
|
||
}
|
||
|
||
if (Status != STATUS_SUCCESS) {
|
||
RxDbgTrace( 0, Dbg, ("SmbCeInitiateExchange: Exchange %lx Status %lx\n",pExchange,Status));
|
||
return Status;
|
||
}
|
||
|
||
pSmbCeSynchronizationEvent = pExchange->pSmbCeSynchronizationEvent;
|
||
if (pSmbCeSynchronizationEvent != NULL) {
|
||
KeInitializeEvent(
|
||
pSmbCeSynchronizationEvent,
|
||
SynchronizationEvent,
|
||
FALSE);
|
||
}
|
||
|
||
for (;;) {
|
||
SmbCeAcquireResource();
|
||
|
||
switch (pExchange->SmbCeState) {
|
||
case SMBCE_EXCHANGE_INITIALIZATION_START:
|
||
{
|
||
RxDbgTrace( 0, Dbg, ("SmbCeInitiateExchange: Exchange %lx State %lx\n",pExchange,pExchange->SmbCeState));
|
||
Status = SmbCeReferenceServer(pExchange);
|
||
if (Status != STATUS_SUCCESS) {
|
||
// this covers the case when the SERVER_ENTRY is under construction
|
||
// and RxStatus(PENDING) is returned.
|
||
RxDbgTrace( 0, Dbg, ("SmbCeInitiateExchange: SmbCeReferenceServer returned %lx\n",Status));
|
||
break;
|
||
}
|
||
}
|
||
// fall through
|
||
|
||
case SMBCE_EXCHANGE_SERVER_INITIALIZED:
|
||
if (Status == STATUS_SUCCESS) {
|
||
RxDbgTrace( 0, Dbg, ("SmbCeInitiateExchange: Exchange %lx State %lx\n",pExchange,pExchange->SmbCeState));
|
||
Status = SmbCeReferenceSession(pExchange);
|
||
if (!NT_SUCCESS(Status)) {
|
||
RxDbgTrace( 0, Dbg, ("SmbCeInitiateExchange: SmbCeReferenceSession returned %lx\n",Status));
|
||
break;
|
||
} if ((Status == STATUS_PENDING) &&
|
||
!(pExchange->SmbCeFlags & SMBCE_EXCHANGE_SESSION_CONSTRUCTOR)) {
|
||
break;
|
||
}
|
||
} else {
|
||
break;
|
||
}
|
||
// fall through
|
||
|
||
case SMBCE_EXCHANGE_SESSION_INITIALIZED:
|
||
RxDbgTrace( 0, Dbg, ("SmbCeInitiateExchange: Exchange %lx State %lx\n",pExchange,pExchange->SmbCeState));
|
||
Status = SmbCeReferenceNetRoot(pExchange);
|
||
if (!NT_SUCCESS(Status)) {
|
||
RxDbgTrace( 0, Dbg, ("SmbCeInitiateExchange: SmbCeReferenceNetRoot returned %lx\n",Status));
|
||
break;
|
||
} else if ((Status == STATUS_PENDING) &&
|
||
!(pExchange->SmbCeFlags & SMBCE_EXCHANGE_NETROOT_CONSTRUCTOR)) {
|
||
break;
|
||
}
|
||
// else fall through
|
||
|
||
case SMBCE_EXCHANGE_NETROOT_INITIALIZED:
|
||
{
|
||
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry;
|
||
|
||
RxDbgTrace( 0, Dbg, ("SmbCeInitiateExchange: Exchange %lx State %lx\n",pExchange,pExchange->SmbCeState));
|
||
pNetRootEntry = SmbCeGetExchangeNetRootEntry(pExchange);
|
||
|
||
if ((pNetRootEntry != NULL) &&
|
||
!BooleanFlagOn(pExchange->SmbCeFlags,SMBCE_EXCHANGE_INDEFINITE_DELAY_IN_RESPONSE)) {
|
||
pExchange->SmbCeFlags |= SMBCE_EXCHANGE_TIMED_RECEIVE_OPERATION;
|
||
}
|
||
|
||
pExchange->SmbCeState = SMBCE_EXCHANGE_INITIATED;
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
break;
|
||
|
||
default:
|
||
ASSERT(!"Valid State for a SMB exchange, exchange Initiation aborted");
|
||
break;
|
||
}
|
||
|
||
SmbCeReleaseResource();
|
||
|
||
if ((pSmbCeSynchronizationEvent != NULL) &&
|
||
(pExchange->SmbCeState != SMBCE_EXCHANGE_INITIATED) &&
|
||
(Status == STATUS_PENDING)) {
|
||
|
||
KeWaitForSingleObject(
|
||
pSmbCeSynchronizationEvent,
|
||
Executive,
|
||
KernelMode,
|
||
FALSE,
|
||
NULL );
|
||
|
||
ASSERT(pExchange->Status != STATUS_PENDING);
|
||
Status = pExchange->Status;
|
||
if (Status != STATUS_SUCCESS) {
|
||
break;
|
||
}
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
|
||
ASSERT((Status != STATUS_PENDING) ||
|
||
(pSmbCeSynchronizationEvent == NULL));
|
||
|
||
RxDbgTrace(0,Dbg,("Exchange (%lx) Type (%lx) State(%lx) Status %lx \n",pExchange,pExchange->Type,pExchange->SmbCeState,Status));
|
||
RxDbgTrace(0,Dbg,
|
||
("ServerEntry(%lx) SessionEntry(%lx) NetRootEntry(%lx) \n",
|
||
pServerEntry,
|
||
pSessionEntry,
|
||
pNetRootEntry));
|
||
|
||
// Note: Once the exchange has been initiated no further reference of the exchange
|
||
// can be done since the state of the exchange is non-deterministic, i.e., depends upon
|
||
// the scheduler.
|
||
if (Status == STATUS_SUCCESS) {
|
||
BOOLEAN ResourceReleased = FALSE;
|
||
|
||
// Start the exchange
|
||
ASSERT(pExchange->SmbCeState == SMBCE_EXCHANGE_INITIATED);
|
||
|
||
SmbCeAcquireResource();
|
||
|
||
if ((pServerEntry->Header.State == SMBCEDB_ACTIVE) ||
|
||
(pExchange->NodeTypeCode == SMB_EXCHANGE_NTC(ADMIN_EXCHANGE))) {
|
||
Status = SmbCeInitializeExchangeTransport(pExchange);
|
||
} else {
|
||
Status = STATUS_CONNECTION_DISCONNECTED;
|
||
}
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
if (pExchange->RxContext != NULL) {
|
||
PMRXSMB_RX_CONTEXT pMRxSmbContext;
|
||
|
||
// Set up the cancellation routine ..
|
||
|
||
pMRxSmbContext = MRxSmbGetMinirdrContext(pExchange->RxContext);
|
||
pMRxSmbContext->pCancelContext = pExchange;
|
||
|
||
Status = RxSetMinirdrCancelRoutine(
|
||
pExchange->RxContext,
|
||
SmbCeCancelExchange);
|
||
}
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
if (!IsListEmpty(&pExchange->ExchangeList)) {
|
||
RemoveEntryList(&pExchange->ExchangeList);
|
||
}
|
||
|
||
InsertTailList(
|
||
&pServerEntry->ActiveExchanges,
|
||
&pExchange->ExchangeList);
|
||
|
||
SmbCeReleaseResource();
|
||
ResourceReleased = TRUE;
|
||
|
||
pExchange->SmbStatus = STATUS_SUCCESS;
|
||
pExchange->ServerVersion = pServerEntry->Server.Version;
|
||
Status = SMB_EXCHANGE_DISPATCH(pExchange,Start,((PSMB_EXCHANGE)pExchange));
|
||
}
|
||
|
||
RxDbgTrace( 0, Dbg, ("SmbCeInitiateExchange: SMB_EXCHANGE_DISPATCH(Start) returned %lx\n",Status));
|
||
}
|
||
|
||
if (!ResourceReleased) {
|
||
SmbCeReleaseResource();
|
||
}
|
||
} else if (Status != STATUS_PENDING) {
|
||
RxDbgTrace( 0, Dbg, ("SmbCeInitiateExchange: Exchange(%lx) Initiation failed %lx \n",pExchange,Status));
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
SmbCeInitiateAssociatedExchange(
|
||
PSMB_EXCHANGE pExchange,
|
||
BOOLEAN EnableCompletionHandlerInMasterExchange)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine inititaes an associated exchange.
|
||
|
||
Arguments:
|
||
|
||
pExchange - the exchange to be initiated.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The return status for the operation
|
||
|
||
Notes:
|
||
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
PSMB_EXCHANGE pMasterExchange;
|
||
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
||
|
||
PAGED_CODE();
|
||
|
||
ASSERT(pExchange->SmbCeState == SMBCE_EXCHANGE_INITIATED);
|
||
ASSERT(FlagOn(pExchange->SmbCeFlags,SMBCE_ASSOCIATED_EXCHANGE));
|
||
|
||
pMasterExchange = pExchange->Associated.pMasterExchange;
|
||
pServerEntry = SmbCeGetExchangeServerEntry(pExchange);
|
||
ASSERT(pServerEntry != NULL);
|
||
|
||
// Note: Once the exchange has been initiated no further reference of the exchange
|
||
// can be done since the state of the exchange is non-deterministic, i.e., depends upon
|
||
// the scheduler.
|
||
|
||
Status = SmbCeInitializeExchangeTransport(pExchange);
|
||
|
||
SmbCeAcquireResource();
|
||
|
||
if (!IsListEmpty(&pExchange->ExchangeList)) {
|
||
RemoveEntryList(&pExchange->ExchangeList);
|
||
}
|
||
|
||
InsertTailList(
|
||
&pServerEntry->ActiveExchanges,
|
||
&pExchange->ExchangeList);
|
||
|
||
if (EnableCompletionHandlerInMasterExchange) {
|
||
ASSERT(!FlagOn(
|
||
pMasterExchange->SmbCeFlags,
|
||
SMBCE_ASSOCIATED_EXCHANGES_COMPLETION_HANDLER_ACTIVATED));
|
||
SetFlag(
|
||
pMasterExchange->SmbCeFlags,
|
||
SMBCE_ASSOCIATED_EXCHANGES_COMPLETION_HANDLER_ACTIVATED);
|
||
}
|
||
|
||
pExchange->SmbStatus = STATUS_SUCCESS;
|
||
pExchange->ServerVersion = pServerEntry->Server.Version;
|
||
|
||
SmbCeReleaseResource();
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
Status = SMB_EXCHANGE_DISPATCH(pExchange,Start,((PSMB_EXCHANGE)pExchange));
|
||
RxDbgTrace( 0, Dbg, ("SmbCeInitiateExchange: SMB_EXCHANGE_DISPATCH(Start) returned %lx\n",Status));
|
||
} else {
|
||
SmbCeFinalizeExchange(pExchange);
|
||
Status = STATUS_PENDING;
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
SmbCeExchangeAbort(
|
||
PSMB_EXCHANGE pExchange)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine aborts an exchange.
|
||
|
||
Arguments:
|
||
|
||
pExchange - the exchange to be aborted.
|
||
|
||
Return Value:
|
||
|
||
RXSTATUS - The return status for the operation
|
||
|
||
Notes:
|
||
|
||
|
||
--*/
|
||
{
|
||
PAGED_CODE();
|
||
|
||
RxDbgTrace( 0, Dbg, ("SmbCeExchangeAbort: Exchange %lx aborted\n",pExchange));
|
||
SmbCeDiscardExchange(pExchange);
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
SmbCeBuildSmbHeader(
|
||
IN OUT PSMB_EXCHANGE pExchange,
|
||
IN OUT PVOID pBuffer,
|
||
IN ULONG BufferLength,
|
||
OUT PULONG pBufferConsumed,
|
||
OUT PUCHAR pLastCommandInHeader,
|
||
OUT PUCHAR *pNextCommandPtr)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine constructs the SMB header associated with any SMB sent as part of
|
||
an exchange.
|
||
|
||
Arguments:
|
||
|
||
pExchange - the exchange for which the SMB is to be constructed.
|
||
|
||
pBuffer - the buffer in whihc the SMB header is to be constructed
|
||
|
||
BufferLength - length of the buffer
|
||
|
||
pBufferConsumed - the buffer consumed
|
||
|
||
pLastCommandInHeader - the last command in header, SMB_COM_NO_ANDX_COMMAND if none
|
||
|
||
pNextCommandPtr - the ptr to the place in the buffer where the next command
|
||
code should be copied.
|
||
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - if the header construction was successful
|
||
|
||
Notes:
|
||
|
||
This routine is called to build the SMB header. This centralization allows us to
|
||
compound the SMB operation with other SMB's required for the maintenance of the
|
||
SMB connection engine data structures. It also provides us with a centralized facility
|
||
for profiling SMB's as well as a one place mechanism for filling in all the header
|
||
fields associated with a SMB.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
PSMB_HEADER pSmbHeader = (PSMB_HEADER)pBuffer;
|
||
PGENERIC_ANDX pSmbBuffer;
|
||
ULONG SmbBufferUnconsumed = BufferLength;
|
||
PUCHAR pSmbCommand;
|
||
|
||
UCHAR LastCommandInHeader = SMB_COM_NO_ANDX_COMMAND;
|
||
UCHAR Flags = SMB_FLAGS_CASE_INSENSITIVE | SMB_FLAGS_CANONICALIZED_PATHS;
|
||
USHORT Flags2 = 0;
|
||
|
||
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
||
PSMBCEDB_SESSION_ENTRY pSessionEntry;
|
||
|
||
PSMBCE_SERVER pServer;
|
||
|
||
PAGED_CODE();
|
||
|
||
if (BufferLength < sizeof(SMB_HEADER)) {
|
||
RxDbgTrace( 0, Dbg, ("SmbCeBuildSmbHeader: BufferLength too small %d\n",BufferLength));
|
||
ASSERT(!"Buffer too small");
|
||
return STATUS_BUFFER_TOO_SMALL;
|
||
}
|
||
|
||
SmbBufferUnconsumed = BufferLength - sizeof(SMB_HEADER);
|
||
|
||
pServerEntry = SmbCeGetExchangeServerEntry(pExchange);
|
||
pSessionEntry = SmbCeGetExchangeSessionEntry(pExchange);
|
||
|
||
pServer = SmbCeGetExchangeServer(pExchange);
|
||
|
||
if (pServer->Dialect == NTLANMAN_DIALECT) {
|
||
|
||
if (FlagOn(pServer->DialectFlags,DF_NT_SMBS)) {
|
||
Flags2 |= (SMB_FLAGS2_KNOWS_EAS );
|
||
|
||
if (FlagOn(pServer->DialectFlags,DF_UNICODE)) {
|
||
Flags2 |= SMB_FLAGS2_UNICODE;
|
||
}
|
||
}
|
||
|
||
if (FlagOn(pServer->DialectFlags,DF_NT_STATUS)) {
|
||
Flags2 |= SMB_FLAGS2_NT_STATUS;
|
||
}
|
||
}
|
||
|
||
if (FlagOn(pServer->DialectFlags,DF_LONGNAME)) {
|
||
Flags2 |= SMB_FLAGS2_KNOWS_LONG_NAMES;
|
||
}
|
||
|
||
if (FlagOn(pServer->DialectFlags,DF_SUPPORTEA)) {
|
||
Flags2 |= SMB_FLAGS2_KNOWS_EAS;
|
||
}
|
||
|
||
RtlZeroMemory(pSmbHeader,sizeof(SMB_HEADER));
|
||
|
||
*((PULONG)&pSmbHeader->Protocol) = SMB_HEADER_PROTOCOL;
|
||
pSmbHeader->Flags = Flags;
|
||
pSmbHeader->Flags2 = Flags2;
|
||
pSmbHeader->Pid = MRXSMB_PROCESS_ID;
|
||
pSmbHeader->Uid = 0;
|
||
pSmbHeader->Tid = 0;
|
||
pSmbHeader->ErrorClass = 0;
|
||
pSmbHeader->Reserved = 0;
|
||
pSmbCommand = &pSmbHeader->Command;
|
||
SmbPutUshort(&pSmbHeader->Error,0);
|
||
|
||
switch (SmbCeGetServerType(pServerEntry)) {
|
||
case SMBCEDB_FILE_SERVER:
|
||
{
|
||
BOOLEAN fValidTid;
|
||
|
||
if (pSessionEntry != NULL) {
|
||
pSmbHeader->Uid = pSessionEntry->Session.UserId;
|
||
}
|
||
|
||
if (pExchange->SmbCeContext.pVNetRoot != NULL) {
|
||
PSMBCE_V_NET_ROOT_CONTEXT pVNetRootContext;
|
||
|
||
pVNetRootContext = SmbCeGetAssociatedVNetRootContext(
|
||
pExchange->SmbCeContext.pVNetRoot);
|
||
|
||
fValidTid = BooleanFlagOn(
|
||
pVNetRootContext->Flags,
|
||
SMBCE_V_NET_ROOT_CONTEXT_FLAG_VALID_TID);
|
||
|
||
pSmbHeader->Tid = pVNetRootContext->TreeId;
|
||
} else {
|
||
fValidTid = TRUE;
|
||
}
|
||
|
||
pSmbBuffer = (PGENERIC_ANDX)(pSmbHeader + 1);
|
||
|
||
if ((pExchange->SmbCeFlags & SMBCE_EXCHANGE_SESSION_CONSTRUCTOR) ||
|
||
(pExchange->SmbCeFlags & SMBCE_EXCHANGE_NETROOT_CONSTRUCTOR)) {
|
||
// There is an oppurtunity to compound some SessionSetup/TreeConnect SMB with the
|
||
// given SMB command.
|
||
if ((pExchange->SmbCeFlags & SMBCE_EXCHANGE_SESSION_CONSTRUCTOR) &&
|
||
(pSessionEntry->Header.State == SMBCEDB_CONSTRUCTION_IN_PROGRESS)) {
|
||
if (( pServer->DialectFlags & DF_EXTENDNEGOT) ||
|
||
( pServer->DialectFlags & DF_NTNEGOTIATE)) {
|
||
RxDbgTrace( 0, Dbg, ("SmbCeBuildSmbHeader: Building Session setup And X\n"));
|
||
|
||
*pSmbCommand = SMB_COM_SESSION_SETUP_ANDX;
|
||
LastCommandInHeader = *pSmbCommand;
|
||
pSmbCommand = &pSmbBuffer->AndXCommand;
|
||
pSmbHeader->Tid = 0;
|
||
|
||
Status = SMBCE_SERVER_DIALECT_DISPATCH(
|
||
pServer,
|
||
BuildSessionSetup,
|
||
(pExchange,
|
||
pSmbBuffer,
|
||
&SmbBufferUnconsumed));
|
||
if (NT_SUCCESS(Status)) {
|
||
// Update the buffer for the construction of the following SMB.
|
||
SmbPutUshort(
|
||
&pSmbBuffer->AndXOffset,
|
||
(USHORT)(BufferLength - SmbBufferUnconsumed));
|
||
pSmbBuffer = (PGENERIC_ANDX)((PBYTE)pBuffer + BufferLength - SmbBufferUnconsumed);
|
||
}
|
||
}
|
||
} else {
|
||
NOTHING; //no sess for share level AT LEAST NOT FOR CORE!!!
|
||
}
|
||
|
||
if (NT_SUCCESS(Status) &&
|
||
(pExchange->SmbCeFlags & SMBCE_EXCHANGE_NETROOT_CONSTRUCTOR) &&
|
||
!fValidTid) {
|
||
BOOLEAN BuildingTreeConnectAndX = BooleanFlagOn(pServer->DialectFlags,DF_LANMAN10);
|
||
if (BuildingTreeConnectAndX) {
|
||
RxDbgTrace( 0, Dbg, ("SmbCeBuildSmbHeader: Building Tree Connect And X\n"));
|
||
*pSmbCommand = SMB_COM_TREE_CONNECT_ANDX;
|
||
LastCommandInHeader = *pSmbCommand;
|
||
} else {
|
||
RxDbgTrace( 0, Dbg, ("SmbCeBuildSmbHeader: Building Tree Connect No X\n"));
|
||
*pSmbCommand = SMB_COM_TREE_CONNECT;
|
||
LastCommandInHeader = *pSmbCommand;
|
||
}
|
||
|
||
Status = SMBCE_SERVER_DIALECT_DISPATCH(
|
||
pServer,
|
||
BuildTreeConnect,
|
||
(pExchange,
|
||
pSmbBuffer,
|
||
&SmbBufferUnconsumed));
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
// Update the buffer for the construction of the following SMB.
|
||
if (BuildingTreeConnectAndX) {
|
||
pSmbCommand = &pSmbBuffer->AndXCommand;
|
||
SmbPutUshort(&pSmbBuffer->AndXOffset,(USHORT)(BufferLength - SmbBufferUnconsumed));
|
||
} else {
|
||
pSmbCommand = NULL;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
|
||
default:
|
||
{
|
||
ASSERT(!"Valid Server Type");
|
||
Status = STATUS_INVALID_HANDLE;
|
||
}
|
||
break;
|
||
}
|
||
|
||
*pNextCommandPtr = pSmbCommand;
|
||
*pBufferConsumed = BufferLength - SmbBufferUnconsumed;
|
||
*pLastCommandInHeader = LastCommandInHeader;
|
||
|
||
RxDbgTrace( 0, Dbg, ("SmbCeBuildSmbHeader: Buffer Consumed %lx\n",*pBufferConsumed));
|
||
|
||
if (Status != STATUS_SUCCESS) {
|
||
if (pExchange->SmbCeFlags & SMBCE_EXCHANGE_SESSION_CONSTRUCTOR) {
|
||
pExchange->SessionSetupStatus = Status;
|
||
}
|
||
|
||
if (pExchange->SmbCeFlags & SMBCE_EXCHANGE_NETROOT_CONSTRUCTOR) {
|
||
PSMBCE_V_NET_ROOT_CONTEXT pVNetRootContext;
|
||
|
||
pVNetRootContext = SmbCeGetExchangeVNetRootContext(pExchange);
|
||
|
||
SmbCeUpdateVNetRootContextState(
|
||
pVNetRootContext,
|
||
SMBCEDB_INVALID);
|
||
}
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
typedef struct __Service_Name_Entry {
|
||
NET_ROOT_TYPE NetRootType;
|
||
USHORT NameLength;
|
||
PBYTE Name;
|
||
};
|
||
struct __Service_Name_Entry ServiceNameTable[] = {
|
||
{NET_ROOT_DISK,sizeof(SHARE_TYPE_NAME_DISK),SHARE_TYPE_NAME_DISK},
|
||
{NET_ROOT_PIPE,sizeof(SHARE_TYPE_NAME_PIPE),SHARE_TYPE_NAME_PIPE},
|
||
{NET_ROOT_PRINT,sizeof(SHARE_TYPE_NAME_PRINT),SHARE_TYPE_NAME_PRINT},
|
||
{NET_ROOT_COMM,sizeof(SHARE_TYPE_NAME_COMM),SHARE_TYPE_NAME_COMM} //COMM must be last
|
||
};
|
||
|
||
UNICODE_STRING FileSystem_NTFS_UNICODE = {8,8,L"NTFS"};
|
||
UNICODE_STRING FileSystem_FAT_UNICODE = {6,6,L"FAT"};
|
||
CHAR FileSystem_NTFS[] = "NTFS";
|
||
CHAR FileSystem_FAT[] = "FAT";
|
||
|
||
NTSTATUS
|
||
SmbCeParseSmbHeader(
|
||
PSMB_EXCHANGE pExchange,
|
||
PSMB_HEADER pSmbHeader,
|
||
PGENERIC_ANDX pCommandToProcess,
|
||
NTSTATUS *pSmbResponseStatus,
|
||
ULONG BytesAvailable,
|
||
ULONG BytesIndicated,
|
||
PULONG pBytesConsumed)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine validates the SMB header associated with any SMB received as part of
|
||
an exchange.
|
||
|
||
Arguments:
|
||
|
||
pExchange - the exchange for which the SMB is to be constructed.
|
||
|
||
pSmbHeader - the header of the SMB received
|
||
|
||
pCommandToProcess - the SMB command to be processed after the header ( Can be NULL )
|
||
|
||
pSmbResponseStatus - the status in the SMB response header (Can be NULL)
|
||
|
||
BytesAvailable - the bytes available for processing but not necesarily indicated.
|
||
|
||
BytesIndicated - the length of the SMB buffer avcailable for perusal
|
||
|
||
pBytesConsumed - the buffer consumed
|
||
|
||
Return Value:
|
||
|
||
RXSTATUS - The return status for the operation
|
||
STATUS_MORE_PROCESSING_REQUIRED -- if a copy of the data needs to be done before
|
||
processing can be completed. This occurs because sufficient data was not
|
||
indicated to process the header.
|
||
STATUS_SUCCESS -- the header was processed succesfully. In such cases the GENERIC_ANDX
|
||
if not NULL will contain the offset from the start of the buffer and the command
|
||
to be processed.
|
||
STATUS_* -- They indicate an error which would normally lead to the abortion of the
|
||
exchange.
|
||
|
||
Notes:
|
||
|
||
This routine is called to parse the SMB header. This centralization allows us to
|
||
implement a one stop mechanism for updateing/validating the header fields as well as
|
||
resuming the exchanges waiting for the construction of session/net root entry
|
||
associated with this exchange
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
NTSTATUS SmbResponseStatus;
|
||
|
||
PBYTE pSmbBuffer = (PBYTE)pSmbHeader;
|
||
UCHAR SmbCommand;
|
||
|
||
BOOLEAN fUpdateVNetRootContext = FALSE;
|
||
|
||
SMBCEDB_OBJECT_STATE SessionState;
|
||
SMBCEDB_OBJECT_STATE NetRootState;
|
||
|
||
PMRX_V_NET_ROOT pVNetRoot;
|
||
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
||
PSMBCEDB_SESSION_ENTRY pSessionEntry;
|
||
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry;
|
||
PSMBCE_V_NET_ROOT_CONTEXT pVNetRootContext;
|
||
|
||
pVNetRoot = SmbCeGetExchangeVNetRoot(pExchange);
|
||
pServerEntry = SmbCeGetExchangeServerEntry(pExchange);
|
||
pSessionEntry = SmbCeGetExchangeSessionEntry(pExchange);
|
||
pNetRootEntry = SmbCeGetExchangeNetRootEntry(pExchange);
|
||
|
||
pVNetRootContext = SmbCeGetExchangeVNetRootContext(pExchange);
|
||
|
||
// Return Immediately if bytes indicated is less then the size of a SMB header.
|
||
if (BytesIndicated < sizeof(SMB_HEADER)) {
|
||
*pBytesConsumed = BytesIndicated;
|
||
return STATUS_INVALID_NETWORK_RESPONSE;
|
||
}
|
||
|
||
SmbResponseStatus = GetSmbResponseNtStatus(pSmbHeader,pExchange);
|
||
if (!NT_SUCCESS(SmbResponseStatus)) {
|
||
RxDbgTrace( 0, Dbg, ("SmbCeParseSmbHeader::SMB Response Error %lx\n",SmbResponseStatus));
|
||
}
|
||
|
||
SmbCommand = pSmbHeader->Command;
|
||
*pBytesConsumed = sizeof(SMB_HEADER);
|
||
pSmbBuffer += *pBytesConsumed;
|
||
|
||
// There are certain SMB's that effect the connection engine data structures as
|
||
// well as the exchange that has been suspended. These are the SMB's used for tree
|
||
// connect and session setup.
|
||
// In all the other cases no special action is required for the maintenance of the
|
||
// connection engine data structures. The Exchange that was suspended needs to be
|
||
// resumed.
|
||
if (SmbCommand == SMB_COM_SESSION_SETUP_ANDX) {
|
||
if (SmbResponseStatus != STATUS_SUCCESS) {
|
||
if ((FIELD_OFFSET(GENERIC_ANDX,AndXReserved) + *pBytesConsumed) <= BytesIndicated) {
|
||
PGENERIC_ANDX pGenericAndX = (PGENERIC_ANDX)pSmbBuffer;
|
||
|
||
if (pGenericAndX->WordCount == 0) {
|
||
Status = SmbResponseStatus;
|
||
}
|
||
|
||
pExchange->SessionSetupStatus = SmbResponseStatus;
|
||
}
|
||
|
||
// Note that the case wherein sufficient bytes are not indicated for the
|
||
// GENERIC_ANDX response is handled by the if statement below which
|
||
// imposes a more stringent test.
|
||
}
|
||
|
||
if ((Status == STATUS_SUCCESS) &&
|
||
(FIELD_OFFSET(RESP_SESSION_SETUP_ANDX,Buffer) + *pBytesConsumed) > BytesIndicated) {
|
||
Status = STATUS_MORE_PROCESSING_REQUIRED;
|
||
}
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
PRESP_SESSION_SETUP_ANDX pSessionSetupResponse;
|
||
ULONG SessionSetupResponseLength,ByteCount;
|
||
|
||
RxDbgTrace( 0, Dbg, ("Processing Session Setup ANd X\n"));
|
||
pSessionSetupResponse = (PRESP_SESSION_SETUP_ANDX)(pSmbBuffer);
|
||
|
||
ByteCount = SmbGetUshort(&pSessionSetupResponse->ByteCount);
|
||
if (pSessionSetupResponse->WordCount == 3) {
|
||
SmbCommand = pSessionSetupResponse->AndXCommand;
|
||
if (SmbCommand == SMB_COM_NO_ANDX_COMMAND) {
|
||
SessionSetupResponseLength =
|
||
FIELD_OFFSET(RESP_SESSION_SETUP_ANDX,Buffer) + ByteCount;
|
||
Status = SmbResponseStatus;
|
||
} else {
|
||
SessionSetupResponseLength =
|
||
SmbGetUshort(&pSessionSetupResponse->AndXOffset) - *pBytesConsumed;
|
||
}
|
||
|
||
//if (ByteCount == 0) {
|
||
// //bytecount==0 and NTDIALECT means that this is really w95...change the flags
|
||
// PSMBCE_SERVER pServer = &pExchange->SmbCeContext.pServerEntry->Server;
|
||
// if (FlagOn(pServer->DialectFlags,DF_NTPROTOCOL)) {
|
||
// pServer->DialectFlags &= ~(DF_MIXEDCASEPW);
|
||
// pServer->DialectFlags |= DF_W95;
|
||
// }
|
||
//}
|
||
} else {
|
||
Status = SmbResponseStatus;
|
||
}
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
if (SessionSetupResponseLength + *pBytesConsumed <= BytesIndicated) {
|
||
*pBytesConsumed += SessionSetupResponseLength;
|
||
pSmbBuffer += SessionSetupResponseLength;
|
||
pSessionEntry->Session.UserId = pSmbHeader->Uid;
|
||
|
||
if (FlagOn(SmbGetUshort(&pSessionSetupResponse->Action), SMB_SETUP_USE_LANMAN_KEY)) {
|
||
pSessionEntry->Session.Flags |=
|
||
SMBCE_SESSION_FLAGS_LANMAN_SESSION_KEY_USED;
|
||
}
|
||
|
||
if (FlagOn(SmbGetUshort(&pSessionSetupResponse->Action), SMB_SETUP_GUEST)) {
|
||
pSessionEntry->Session.Flags |=
|
||
SMBCE_SESSION_FLAGS_GUEST_SESSION;
|
||
}
|
||
|
||
pExchange->SessionSetupStatus = STATUS_SUCCESS;
|
||
InterlockedIncrement(&MRxSmbStatistics.Sessions);
|
||
} else {
|
||
Status = STATUS_MORE_PROCESSING_REQUIRED;
|
||
}
|
||
} else {
|
||
RxDbgTrace( 0, Dbg, ("SmbCeParseSmbHeader::Session setup and X Response %lx\n",Status));
|
||
pExchange->SessionSetupStatus = Status;
|
||
|
||
InterlockedIncrement(&MRxSmbStatistics.FailedSessions);
|
||
|
||
if ((SmbCommand == SMB_COM_TREE_CONNECT_ANDX) ||
|
||
(SmbCommand == SMB_COM_TREE_CONNECT)) {
|
||
RxDbgTrace( 0, Dbg, ("SmbCeParseSmbHeader:: Tearing down a tree connection\n"));
|
||
fUpdateVNetRootContext = TRUE;
|
||
NetRootState = SMBCEDB_INVALID;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if ((SmbCommand == SMB_COM_TREE_CONNECT_ANDX) &&
|
||
NT_SUCCESS(Status)) {
|
||
if (SmbResponseStatus != STATUS_SUCCESS) {
|
||
if ((FIELD_OFFSET(GENERIC_ANDX,AndXReserved) + *pBytesConsumed) <= BytesIndicated) {
|
||
PGENERIC_ANDX pGenericAndX = (PGENERIC_ANDX)pSmbBuffer;
|
||
|
||
if (pGenericAndX->WordCount == 0) {
|
||
Status = SmbResponseStatus;
|
||
}
|
||
|
||
fUpdateVNetRootContext = TRUE;
|
||
NetRootState = SMBCEDB_INVALID;
|
||
}
|
||
|
||
// Note that the case wherein sufficient bytes are not indicated for the
|
||
// GENERIC_ANDX response is handled by the if statement below which
|
||
// imposes a more stringent test.
|
||
}
|
||
|
||
if ((Status == STATUS_SUCCESS) &&
|
||
(FIELD_OFFSET(RESP_21_TREE_CONNECT_ANDX,Buffer) + *pBytesConsumed) > BytesIndicated) {
|
||
Status = STATUS_MORE_PROCESSING_REQUIRED;
|
||
}
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
USHORT ResponseWordCount;
|
||
ULONG TreeConnectResponseLength,TreeConnectByteCount,ServiceStringLength;
|
||
PUCHAR pShareTypeResponseString = NULL;
|
||
PRESP_21_TREE_CONNECT_ANDX p21TreeConnectAndXResponse;
|
||
PUCHAR NativeFileSystem = NULL;
|
||
|
||
p21TreeConnectAndXResponse = (PRESP_21_TREE_CONNECT_ANDX)(pSmbBuffer);
|
||
SmbCommand = p21TreeConnectAndXResponse->AndXCommand;
|
||
TreeConnectByteCount = 0;
|
||
|
||
RxDbgTrace( 0, Dbg, ("Processing Tree Connect and X\n"));
|
||
|
||
// case out based on the actual response length. Lanman 21 clients or NT clients
|
||
// have a longer response.....win95 negotiates NT dialect but uses a <lm21 response format
|
||
ResponseWordCount = p21TreeConnectAndXResponse->WordCount;
|
||
|
||
switch (ResponseWordCount) {
|
||
case 0:
|
||
Status = SmbResponseStatus;
|
||
break;
|
||
|
||
case 3:
|
||
{
|
||
pNetRootEntry->MaximalAccessRights = FILE_ALL_ACCESS;
|
||
pNetRootEntry->GuestMaximalAccessRights = 0;
|
||
|
||
pShareTypeResponseString = (PUCHAR)&p21TreeConnectAndXResponse->Buffer;
|
||
|
||
TreeConnectByteCount = SmbGetUshort(&p21TreeConnectAndXResponse->ByteCount);
|
||
|
||
TreeConnectResponseLength =
|
||
FIELD_OFFSET(RESP_21_TREE_CONNECT_ANDX,Buffer) + TreeConnectByteCount;
|
||
|
||
NativeFileSystem = &p21TreeConnectAndXResponse->Buffer[3];
|
||
|
||
// Parse and update the optional support bits returned by
|
||
// the server
|
||
|
||
if (pServerEntry->Server.Dialect >= NTLANMAN_DIALECT ) {
|
||
USHORT OptionalSupport;
|
||
|
||
OptionalSupport = SmbGetUshort(
|
||
&p21TreeConnectAndXResponse->OptionalSupport);
|
||
|
||
if (FlagOn(OptionalSupport,SMB_SHARE_IS_IN_DFS)) {
|
||
PMRX_NET_ROOT pNetRoot = pVNetRoot->pNetRoot;
|
||
|
||
pNetRootEntry->NetRoot.DfsAware = TRUE;
|
||
SetFlag(pNetRoot->Flags,NETROOT_FLAG_DFS_AWARE_NETROOT);
|
||
}
|
||
}
|
||
|
||
if (SmbCommand == SMB_COM_NO_ANDX_COMMAND) {
|
||
Status = SmbResponseStatus;
|
||
} else {
|
||
TreeConnectResponseLength =
|
||
SmbGetUshort(&p21TreeConnectAndXResponse->AndXOffset) -
|
||
*pBytesConsumed;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case 2:
|
||
{
|
||
PRESP_TREE_CONNECT_ANDX pTreeConnectAndXResponse;
|
||
|
||
pTreeConnectAndXResponse = (PRESP_TREE_CONNECT_ANDX)(pSmbBuffer);
|
||
|
||
ASSERT(FIELD_OFFSET(RESP_TREE_CONNECT_ANDX,AndXCommand)
|
||
==FIELD_OFFSET(RESP_21_TREE_CONNECT_ANDX,AndXCommand));
|
||
|
||
pShareTypeResponseString = (PUCHAR)&pTreeConnectAndXResponse->Buffer;
|
||
TreeConnectByteCount = SmbGetUshort(&pTreeConnectAndXResponse->ByteCount);
|
||
TreeConnectResponseLength =
|
||
FIELD_OFFSET(RESP_TREE_CONNECT_ANDX,Buffer) + TreeConnectByteCount;
|
||
|
||
if (SmbCommand == SMB_COM_NO_ANDX_COMMAND) {
|
||
Status = SmbResponseStatus;
|
||
} else {
|
||
TreeConnectResponseLength =
|
||
SmbGetUshort(&pTreeConnectAndXResponse->AndXOffset) -
|
||
*pBytesConsumed;
|
||
}
|
||
|
||
// win9x server, returns wordcount of 2 yet has the dialect of NTLANMAN
|
||
// which is a bug, but we will work around it.
|
||
if (pServerEntry->Server.Dialect >= NTLANMAN_DIALECT ) {
|
||
pNetRootEntry->MaximalAccessRights = FILE_ALL_ACCESS;
|
||
pNetRootEntry->GuestMaximalAccessRights = 0;
|
||
}
|
||
|
||
}
|
||
break;
|
||
|
||
default :
|
||
Status = STATUS_INVALID_NETWORK_RESPONSE;
|
||
}
|
||
|
||
RxDbgTrace( 0, Dbg, ("SmbCeParseSmbHeader::Tree connect and X Response %lx\n",Status));
|
||
if (NT_SUCCESS(Status)) {
|
||
PSMBCE_NET_ROOT psmbNetRoot = &(pNetRootEntry->NetRoot);
|
||
PSMBCE_SERVER psmbServer = &(pServerEntry->Server);
|
||
|
||
if (TreeConnectResponseLength + *pBytesConsumed <= BytesIndicated) {
|
||
*pBytesConsumed += TreeConnectResponseLength;
|
||
|
||
// Update the NetRoot fields based on the response.
|
||
SetFlag(
|
||
pVNetRootContext->Flags,
|
||
SMBCE_V_NET_ROOT_CONTEXT_FLAG_VALID_TID);
|
||
|
||
RtlCopyMemory(
|
||
&pVNetRootContext->TreeId,
|
||
&pSmbHeader->Tid,
|
||
sizeof(pSmbHeader->Tid));
|
||
|
||
{ struct __Service_Name_Entry *i;
|
||
for (i=ServiceNameTable;;i++) {
|
||
ServiceStringLength = i->NameLength;
|
||
if (TreeConnectByteCount >= ServiceStringLength) {
|
||
if (RtlCompareMemory(
|
||
pShareTypeResponseString,
|
||
i->Name,
|
||
ServiceStringLength)
|
||
== ServiceStringLength) {
|
||
psmbNetRoot->NetRootType = i->NetRootType;
|
||
if (FALSE) DbgPrint("FoundServiceStrng %s len %d type %d\n",i->Name,i->NameLength,i->NetRootType);
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (i->NetRootType==NET_ROOT_COMM) {
|
||
ASSERT(!"Valid Share Type returned in TREE COnnect And X response");
|
||
psmbNetRoot->NetRootType = NET_ROOT_DISK;
|
||
ServiceStringLength = TreeConnectByteCount;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (psmbNetRoot->NetRootType == NET_ROOT_DISK) {
|
||
if (NativeFileSystem != NULL) {
|
||
if (BooleanFlagOn(pServerEntry->Server.DialectFlags,DF_UNICODE)) {
|
||
if (RtlCompareMemory(
|
||
NativeFileSystem,
|
||
FileSystem_NTFS_UNICODE.Buffer,
|
||
FileSystem_NTFS_UNICODE.Length)
|
||
== FileSystem_NTFS_UNICODE.Length) {
|
||
psmbNetRoot->NetRootFileSystem = NET_ROOT_FILESYSTEM_NTFS;
|
||
} else if (RtlCompareMemory(
|
||
NativeFileSystem,
|
||
FileSystem_FAT_UNICODE.Buffer,
|
||
FileSystem_FAT_UNICODE.Length)
|
||
== FileSystem_FAT_UNICODE.Length) {
|
||
psmbNetRoot->NetRootFileSystem = NET_ROOT_FILESYSTEM_FAT;
|
||
}
|
||
} else {
|
||
if (RtlCompareMemory(
|
||
NativeFileSystem,
|
||
FileSystem_NTFS,
|
||
4*sizeof(CHAR))
|
||
== 4*sizeof(CHAR)) {
|
||
psmbNetRoot->NetRootFileSystem = NET_ROOT_FILESYSTEM_NTFS;
|
||
} else if (RtlCompareMemory(
|
||
NativeFileSystem,
|
||
FileSystem_FAT,
|
||
3*sizeof(CHAR))
|
||
== 3*sizeof(CHAR)) {
|
||
psmbNetRoot->NetRootFileSystem = NET_ROOT_FILESYSTEM_FAT;
|
||
}
|
||
}
|
||
}
|
||
|
||
psmbNetRoot->MaximumReadBufferSize = psmbServer->MaximumDiskFileReadBufferSize;
|
||
psmbNetRoot->MaximumWriteBufferSize = psmbServer->MaximumDiskFileWriteBufferSize;
|
||
} else {
|
||
psmbNetRoot->MaximumWriteBufferSize = psmbServer->MaximumNonDiskFileWriteBufferSize;
|
||
psmbNetRoot->MaximumReadBufferSize = psmbServer->MaximumNonDiskFileReadBufferSize;
|
||
}
|
||
|
||
//if !(NT was negotiated) and bytecount>servicelength, we may have a NativeFs name
|
||
if (!FlagOn(psmbServer->DialectFlags,DF_NTNEGOTIATE)
|
||
&& (TreeConnectByteCount>ServiceStringLength)) {
|
||
PBYTE NativeFs = pShareTypeResponseString+ServiceStringLength;
|
||
if (*NativeFs != 0) {
|
||
ULONG i;
|
||
ULONG maxlenpersmb = TreeConnectByteCount-ServiceStringLength;
|
||
ULONG maxlenperarraysize = SMB_MAXIMUM_SUPPORTED_VOLUME_LABEL;
|
||
PCHAR p = (PCHAR)(&psmbNetRoot->FileSystemNameA[0]); //dont write into the 0th char
|
||
//DbgPrint("we may have one...\n");
|
||
for (i=1;;i++){
|
||
if (i==maxlenpersmb) {
|
||
break;
|
||
}
|
||
if (i==maxlenperarraysize) {
|
||
break;
|
||
}
|
||
if (NativeFs[i]==0) {
|
||
break;
|
||
}
|
||
}
|
||
//save away the name for processing later
|
||
|
||
RtlCopyMemory(p,NativeFs,i);
|
||
p[i] = 0;
|
||
//DbgPrint("NativeFs = %s (%d)\n",p,i);
|
||
psmbNetRoot->FileSystemNameALength = (UCHAR)i;
|
||
}
|
||
}
|
||
|
||
pSmbBuffer += TreeConnectResponseLength;
|
||
fUpdateVNetRootContext = TRUE;
|
||
NetRootState = SMBCEDB_ACTIVE;
|
||
} else {
|
||
Status = STATUS_MORE_PROCESSING_REQUIRED;
|
||
}
|
||
} else {
|
||
fUpdateVNetRootContext = TRUE;
|
||
NetRootState = SMBCEDB_INVALID;
|
||
}
|
||
}
|
||
}
|
||
|
||
if ((SmbCommand == SMB_COM_TREE_CONNECT) &&
|
||
NT_SUCCESS(Status)) {
|
||
PRESP_TREE_CONNECT pTreeConnectResponse;
|
||
ULONG TreeConnectResponseLength;
|
||
|
||
RxDbgTrace( 0, Dbg, ("Processing Tree Connect\n"));
|
||
pTreeConnectResponse = (PRESP_TREE_CONNECT)pSmbBuffer;
|
||
TreeConnectResponseLength = FIELD_OFFSET(RESP_TREE_CONNECT,Buffer);
|
||
|
||
SmbCommand = SMB_COM_NO_ANDX_COMMAND;
|
||
|
||
if (NT_SUCCESS(SmbResponseStatus)) {
|
||
PSMBCE_NET_ROOT psmbNetRoot = &(pNetRootEntry->NetRoot);
|
||
PSMBCE_SERVER psmbServer = &(pServerEntry->Server);
|
||
|
||
if (TreeConnectResponseLength + *pBytesConsumed <= BytesIndicated) {
|
||
// Update the NetRoot fields based on the response.
|
||
SetFlag(
|
||
pVNetRootContext->Flags,
|
||
SMBCE_V_NET_ROOT_CONTEXT_FLAG_VALID_TID);
|
||
|
||
RtlCopyMemory(
|
||
&pVNetRootContext->TreeId,
|
||
&pTreeConnectResponse->Tid,
|
||
sizeof(pTreeConnectResponse->Tid));
|
||
|
||
if (psmbServer->Dialect == PCNET1_DIALECT) {
|
||
psmbNetRoot->NetRootType = NET_ROOT_DISK;
|
||
}
|
||
else {
|
||
psmbNetRoot->NetRootType = NET_ROOT_WILD;
|
||
}
|
||
|
||
if (psmbServer->MaximumBufferSize == 0){
|
||
ULONG MaxBuf = SmbGetUshort(&pTreeConnectResponse->MaxBufferSize);
|
||
RxDbgTrace( 0, Dbg, ("SmbCeParseSmbHeader:: setting srvmaxbufsize %ld\n", MaxBuf));
|
||
psmbServer->MaximumBufferSize = MaxBuf;
|
||
//psmbServer->MaximumDiskFileReadBufferSize =
|
||
psmbNetRoot->MaximumWriteBufferSize =
|
||
psmbNetRoot->MaximumReadBufferSize =
|
||
MaxBuf -
|
||
QuadAlign(
|
||
sizeof(SMB_HEADER) +
|
||
FIELD_OFFSET(
|
||
RESP_READ,
|
||
Buffer[0]));
|
||
}
|
||
|
||
*pBytesConsumed += TreeConnectResponseLength;
|
||
|
||
pSmbBuffer += *pBytesConsumed;
|
||
|
||
fUpdateVNetRootContext = TRUE;
|
||
NetRootState = SMBCEDB_ACTIVE;
|
||
|
||
//for CORE, this counts as a successful session setup as well!
|
||
pSessionEntry->Session.UserId = pSmbHeader->Uid;
|
||
pExchange->SessionSetupStatus = STATUS_SUCCESS;
|
||
} else {
|
||
Status = STATUS_MORE_PROCESSING_REQUIRED;
|
||
}
|
||
} else {
|
||
Status = SmbResponseStatus;
|
||
fUpdateVNetRootContext = TRUE;
|
||
NetRootState = SMBCEDB_MARKED_FOR_DELETION;
|
||
}
|
||
|
||
RxDbgTrace( 0, Dbg, ("SmbCeParseSmbHeader::Tree connect Response %lx\n",Status));
|
||
}
|
||
|
||
if ((SmbResponseStatus == STATUS_USER_SESSION_DELETED) ||
|
||
(SmbResponseStatus == STATUS_NETWORK_NAME_DELETED)) {
|
||
ClearFlag(
|
||
pVNetRootContext->Flags,
|
||
SMBCE_V_NET_ROOT_CONTEXT_FLAG_VALID_TID);
|
||
|
||
InterlockedCompareExchange(
|
||
&(pVNetRootContext->Header.State),
|
||
SMBCEDB_INVALID,
|
||
SMBCEDB_ACTIVE);
|
||
|
||
InterlockedCompareExchange(
|
||
&(pSessionEntry->Header.State),
|
||
SMBCEDB_INVALID,
|
||
SMBCEDB_ACTIVE);
|
||
|
||
fUpdateVNetRootContext = TRUE;
|
||
NetRootState = SMBCEDB_INVALID;
|
||
}
|
||
|
||
// Initiate further action if the status of the exchange/conenction engine can be
|
||
// updated based on the data available.
|
||
|
||
if (fUpdateVNetRootContext) {
|
||
PMRX_NET_ROOT pNetRoot = pExchange->SmbCeContext.pVNetRoot->pNetRoot;
|
||
|
||
SmbCeUpdateVNetRootContextState(
|
||
pVNetRootContext,
|
||
NetRootState);
|
||
|
||
switch (NetRootState) {
|
||
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;
|
||
}
|
||
|
||
RxDbgTrace( 0, Dbg, ("Dispatching Net root Entry Finalization\n"));
|
||
}
|
||
|
||
IF_DEBUG {
|
||
if (!(pExchange->SmbCeFlags & SMBCE_EXCHANGE_SESSION_CONSTRUCTOR) &&
|
||
!(pExchange->SmbCeFlags & SMBCE_EXCHANGE_NETROOT_CONSTRUCTOR)) {
|
||
if ((pSmbHeader->Uid != pSessionEntry->Session.UserId) ||
|
||
(pSmbHeader->Tid != pVNetRootContext->TreeId)) {
|
||
RxLog(("Srvr %lx Xchg %lx RUid %ld RTid %ld\n SUid %ld STid %ld\n",
|
||
pServerEntry,pExchange,
|
||
pSmbHeader->Uid,pSmbHeader->Tid,
|
||
pSessionEntry->Session.UserId,pVNetRootContext->TreeId));
|
||
}
|
||
}
|
||
}
|
||
|
||
pExchange->SmbStatus = SmbResponseStatus; //N.B. no spinlock!
|
||
if (Status == STATUS_MORE_PROCESSING_REQUIRED) {
|
||
*pBytesConsumed = 0;
|
||
} else if (!NT_SUCCESS(Status)) {
|
||
*pBytesConsumed = BytesAvailable;
|
||
} else {
|
||
if (pSmbResponseStatus != NULL) {
|
||
*pSmbResponseStatus = SmbResponseStatus;
|
||
}
|
||
|
||
if (pCommandToProcess != NULL) {
|
||
PGENERIC_ANDX pGenericAndX = (PGENERIC_ANDX)((PBYTE)pSmbHeader + *pBytesConsumed);
|
||
|
||
pCommandToProcess->AndXCommand = SmbCommand;
|
||
SmbPutUshort(&pCommandToProcess->AndXOffset, (USHORT)*pBytesConsumed);
|
||
|
||
if ((sizeof(GENERIC_ANDX) + *pBytesConsumed) <= BytesAvailable) {
|
||
pCommandToProcess->WordCount = pGenericAndX->WordCount;
|
||
} else {
|
||
pCommandToProcess->WordCount = 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
SmbCeResumeExchange(
|
||
PSMB_EXCHANGE pExchange)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine resumes an exchange that was suspended in the connection
|
||
engine
|
||
|
||
Arguments:
|
||
|
||
pExchange - the exchange Instance
|
||
|
||
Return Value:
|
||
|
||
The return status for the operation
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
PAGED_CODE();
|
||
|
||
SmbCeIncrementPendingLocalOperations(pExchange);
|
||
|
||
// Initiate the exchange
|
||
Status = SmbCeInitiateExchange(pExchange);
|
||
|
||
SmbCeDecrementPendingLocalOperationsAndFinalize(pExchange);
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
SmbCepInitializeExchange(
|
||
PSMB_EXCHANGE *pExchangePointer,
|
||
PRX_CONTEXT pRxContext,
|
||
PSMBCEDB_SERVER_ENTRY pServerEntry,
|
||
PMRX_V_NET_ROOT pVNetRoot,
|
||
SMB_EXCHANGE_TYPE Type,
|
||
PSMB_EXCHANGE_DISPATCH_VECTOR pDispatchVector)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine initializes the given exchange instanece
|
||
|
||
Arguments:
|
||
|
||
pExchangePointer - the placeholder for the exchange instance. If it is NULL a new one
|
||
is allocated.
|
||
|
||
pRxContext - the associated RxContext
|
||
|
||
pServerEntry - the associated server entry
|
||
|
||
pVirtualNetRoot - the virtual net root
|
||
|
||
Type - the type of the exchange
|
||
|
||
pDispatchVector - the dispatch vector asscoiated with this instance.
|
||
|
||
Return Value:
|
||
|
||
RXSTATUS - The return status for the operation
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
PSMB_EXCHANGE pExchange = NULL;
|
||
|
||
PAGED_CODE();
|
||
|
||
RxDbgTrace( 0, Dbg, ("SmbCeInitializeExchange: Invoked\n"));
|
||
|
||
if (*pExchangePointer == NULL) {
|
||
// Allocate a new exchange instance.
|
||
pExchange = SmbMmAllocateExchange(Type,NULL);
|
||
if (pExchange == NULL) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
*pExchangePointer = pExchange;
|
||
}
|
||
|
||
if ((Status = SmbCeIncrementActiveExchangeCount()) == STATUS_SUCCESS) {
|
||
PSMB_EXCHANGE LocalExchangePointer = *pExchangePointer;
|
||
PSMBCE_V_NET_ROOT_CONTEXT pVNetRootContext;
|
||
|
||
LocalExchangePointer->CancellationStatus = SMBCE_EXCHANGE_NOT_CANCELLED;
|
||
LocalExchangePointer->RxContext = pRxContext;
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
if (pVNetRoot != NULL) {
|
||
pVNetRootContext = SmbCeGetAssociatedVNetRootContext(pVNetRoot);
|
||
|
||
LocalExchangePointer->SmbCeContext.pVNetRoot = pVNetRoot;
|
||
pServerEntry = SmbCeGetAssociatedServerEntry(pVNetRoot->pNetRoot->pSrvCall);
|
||
} else {
|
||
ASSERT(pServerEntry != NULL);
|
||
pVNetRootContext = NULL;
|
||
}
|
||
|
||
if (pVNetRootContext != NULL) {
|
||
SmbCeReferenceVNetRootContext(pVNetRootContext);
|
||
|
||
LocalExchangePointer->SmbCeContext.pVNetRootContext =
|
||
pVNetRootContext;
|
||
LocalExchangePointer->SmbCeContext.pServerEntry =
|
||
pVNetRootContext->pServerEntry;
|
||
} else {
|
||
SmbCeReferenceServerEntry(pServerEntry);
|
||
|
||
LocalExchangePointer->SmbCeContext.pServerEntry =
|
||
pServerEntry;
|
||
|
||
LocalExchangePointer->SmbCeContext.pVNetRootContext = NULL;
|
||
}
|
||
|
||
LocalExchangePointer->SmbCeState = SMBCE_EXCHANGE_INITIALIZATION_START;
|
||
LocalExchangePointer->pDispatchVector = pDispatchVector;
|
||
LocalExchangePointer->SmbCeFlags &= (SMBCE_EXCHANGE_FLAGS_TO_PRESERVE);
|
||
LocalExchangePointer->SmbCeFlags |= (SMBCE_EXCHANGE_REUSE_MID | SMBCE_EXCHANGE_ATTEMPT_RECONNECTS);
|
||
}
|
||
|
||
if (Status != STATUS_SUCCESS) {
|
||
SmbCeDecrementActiveExchangeCount();
|
||
}
|
||
} else {
|
||
(*pExchangePointer)->SmbCeFlags |= SMBCE_EXCHANGE_SMBCE_STOPPED;
|
||
}
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
if (pExchange != NULL) {
|
||
SmbMmFreeExchange(pExchange);
|
||
*pExchangePointer = NULL;
|
||
}
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
SmbCeInitializeAssociatedExchange(
|
||
PSMB_EXCHANGE *pAssociatedExchangePointer,
|
||
PSMB_EXCHANGE pMasterExchange,
|
||
SMB_EXCHANGE_TYPE Type,
|
||
PSMB_EXCHANGE_DISPATCH_VECTOR pDispatchVector)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine initializes the given exchange instanece
|
||
|
||
Arguments:
|
||
|
||
pAssociatedExchangePointer - the placeholder for the exchange instance. If it is NULL a new one
|
||
is allocated.
|
||
|
||
pMasterExchange - the master exchange
|
||
|
||
Type - the type of the exchange
|
||
|
||
pDispatchVector - the dispatch vector asscoiated with this instance.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The return status for the operation
|
||
|
||
Notes:
|
||
|
||
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
PAGED_CODE();
|
||
|
||
if ((pMasterExchange->SmbCeState == SMBCE_EXCHANGE_INITIATED) &&
|
||
!FlagOn(pMasterExchange->SmbCeFlags,SMBCE_ASSOCIATED_EXCHANGE)) {
|
||
Status = SmbCeInitializeExchange(
|
||
pAssociatedExchangePointer,
|
||
NULL,
|
||
pMasterExchange->SmbCeContext.pVNetRoot,
|
||
Type,
|
||
pDispatchVector);
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
PSMB_EXCHANGE pAssociatedExchange;
|
||
|
||
pAssociatedExchange = *pAssociatedExchangePointer;
|
||
|
||
pAssociatedExchange->SmbCeState = SMBCE_EXCHANGE_INITIATED;
|
||
pAssociatedExchange->SmbCeFlags |= SMBCE_ASSOCIATED_EXCHANGE;
|
||
|
||
SmbCeIncrementPendingLocalOperations(pMasterExchange);
|
||
InterlockedIncrement(&pMasterExchange->Master.PendingAssociatedExchanges);
|
||
pAssociatedExchange->Associated.pMasterExchange = pMasterExchange;
|
||
|
||
InitializeListHead(&pAssociatedExchange->WorkQueueItem.List);
|
||
}
|
||
} else {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
SmbCeTransformExchange(
|
||
PSMB_EXCHANGE pExchange,
|
||
SMB_EXCHANGE_TYPE NewType,
|
||
PSMB_EXCHANGE_DISPATCH_VECTOR pDispatchVector)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine transforms an exchange instance of one kind to an exchange instance
|
||
of another kind ( A sophisticated form of casting )
|
||
|
||
Arguments:
|
||
|
||
pExchange - the exchange instance.
|
||
|
||
Type - the new type of the exchange
|
||
|
||
pDispatchVector - the dispatch vector asscoiated with this instance.
|
||
|
||
Return Value:
|
||
|
||
RXSTATUS - The return status for the operation
|
||
|
||
Notes:
|
||
|
||
As it is currently implemented no restrictions are imposed. Once the number of exchanges
|
||
have been established further restrictions will be imposed barring certain kinds of
|
||
transformations. The transformation merely switches the dispatch vector associated
|
||
with the exchange but the context is left intact.
|
||
|
||
--*/
|
||
{
|
||
PAGED_CODE();
|
||
|
||
pExchange->Type = (UCHAR)NewType;
|
||
pExchange->pDispatchVector = pDispatchVector;
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
SmbCePrepareExchangeForReuse(
|
||
PSMB_EXCHANGE pExchange)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine transforms an exchange instance of one kind to an exchange instance
|
||
of another kind ( A sophisticated form of casting )
|
||
|
||
Arguments:
|
||
|
||
pExchange - the exchange instance.
|
||
|
||
Return Value:
|
||
|
||
RXSTATUS - The return status for the operation
|
||
|
||
--*/
|
||
{
|
||
PSMBCEDB_SERVER_ENTRY pServerEntry = NULL;
|
||
PSMBCEDB_SESSION_ENTRY pSessionEntry = NULL;
|
||
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry = NULL;
|
||
PSMBCE_V_NET_ROOT_CONTEXT pVNetRootContext = NULL;
|
||
|
||
PAGED_CODE();
|
||
|
||
RxDbgTrace( 0, Dbg, ("SmbCePrepareExchangeForReuse: Invoked\n"));
|
||
|
||
if (!FlagOn(pExchange->SmbCeFlags,SMBCE_EXCHANGE_SMBCE_STOPPED)) {
|
||
pNetRootEntry = SmbCeGetExchangeNetRootEntry(pExchange);
|
||
pSessionEntry = SmbCeGetExchangeSessionEntry(pExchange);
|
||
pServerEntry = SmbCeGetExchangeServerEntry(pExchange);
|
||
pVNetRootContext = SmbCeGetExchangeVNetRootContext(pExchange);
|
||
|
||
if (pServerEntry != NULL) {
|
||
// Disassociate the MID associated with the exchange
|
||
if (pExchange->SmbCeFlags & SMBCE_EXCHANGE_MID_VALID) {
|
||
SmbCeDissociateMidFromExchange(pServerEntry,pExchange);
|
||
}
|
||
|
||
// Tear down all the copy data requests associated with this exchange
|
||
SmbCePurgeBuffersAssociatedWithExchange(pServerEntry,pExchange);
|
||
|
||
// Uninitialize the transport associated with the exchange
|
||
SmbCeUninitializeExchangeTransport(pExchange);
|
||
}
|
||
|
||
// If this exchange has been marked as a constructor for either a
|
||
// session or netroot finalize the appropriate entries. ( mark
|
||
// them for deletion so that other exchanges can be resumed )
|
||
|
||
if (pExchange->SmbCeFlags & SMBCE_EXCHANGE_SESSION_CONSTRUCTOR) {
|
||
ASSERT(pSessionEntry != NULL);
|
||
RxDbgTrace( 0, Dbg, ("Dispatching Session Entry Finalization\n"));
|
||
|
||
SmbCeReferenceSessionEntry(pSessionEntry);
|
||
|
||
ASSERT(pExchange->SessionSetupStatus != STATUS_SUCCESS ||
|
||
pSessionEntry->Header.State == SMBCEDB_CONSTRUCTION_IN_PROGRESS);
|
||
|
||
SmbCeCompleteSessionEntryInitialization(pSessionEntry,pExchange->SessionSetupStatus);
|
||
pExchange->SmbCeFlags &= ~SMBCE_EXCHANGE_SESSION_CONSTRUCTOR;
|
||
}
|
||
|
||
if (pExchange->SmbCeFlags & SMBCE_EXCHANGE_NETROOT_CONSTRUCTOR) {
|
||
ASSERT(pVNetRootContext != NULL);
|
||
RxDbgTrace( 0, Dbg, ("Dispatching Net root Entry Finalization\n"));
|
||
|
||
SmbCeReferenceVNetRootContext(pVNetRootContext);
|
||
SmbCeCompleteVNetRootContextInitialization(pVNetRootContext);
|
||
pExchange->SmbCeFlags &= ~SMBCE_EXCHANGE_NETROOT_CONSTRUCTOR;
|
||
}
|
||
|
||
if (pVNetRootContext != NULL) {
|
||
SmbCeDereferenceVNetRootContext(pVNetRootContext);
|
||
} else {
|
||
if (pServerEntry != NULL) {
|
||
SmbCeDereferenceServerEntry(pServerEntry);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (FlagOn(pExchange->SmbCeFlags,SMBCE_ASSOCIATED_EXCHANGE)) {
|
||
PSMB_EXCHANGE pMasterExchange;
|
||
LONG AssociatedExchangeCount;
|
||
|
||
pMasterExchange = pExchange->Associated.pMasterExchange;
|
||
|
||
AssociatedExchangeCount = InterlockedDecrement(
|
||
&pMasterExchange->Master.PendingAssociatedExchanges);
|
||
|
||
if (FlagOn(
|
||
pMasterExchange->SmbCeFlags,
|
||
SMBCE_ASSOCIATED_EXCHANGES_COMPLETION_HANDLER_ACTIVATED) &&
|
||
(AssociatedExchangeCount == 0)){
|
||
NTSTATUS Status;
|
||
BOOLEAN PostRequest;
|
||
|
||
ClearFlag(
|
||
pMasterExchange->SmbCeFlags,
|
||
SMBCE_ASSOCIATED_EXCHANGES_COMPLETION_HANDLER_ACTIVATED);
|
||
|
||
Status = SMB_EXCHANGE_DISPATCH(
|
||
pMasterExchange,
|
||
AssociatedExchangesCompletionHandler,
|
||
(pMasterExchange,&PostRequest));
|
||
|
||
RxDbgTrace(0,Dbg,("Master Exchange %lx Assoc. Completion Status %lx\n",pMasterExchange,Status));
|
||
}
|
||
|
||
SmbCeDecrementPendingLocalOperationsAndFinalize(pMasterExchange);
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
VOID
|
||
SmbCeDiscardExchangeWorkerThreadRoutine(PVOID pExchange)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine discards an exchange.
|
||
|
||
Arguments:
|
||
|
||
pExchange - the exchange to be discarded.
|
||
|
||
Return Value:
|
||
|
||
RXSTATUS - The return status for the operation
|
||
|
||
Notes:
|
||
|
||
Even though this is simple, it cannot be inlined since the destruction of an
|
||
exchange instance can be posted to a waorker thread.
|
||
|
||
--*/
|
||
{
|
||
PSMB_EXCHANGE pSmbExchange = pExchange;
|
||
|
||
PAGED_CODE();
|
||
|
||
RxDbgTrace( 0, Dbg, ("SmbCeDiscardExchange: Invoked\n"));
|
||
|
||
//RxLog((">>>Discard %lx",pSmbExchange));
|
||
|
||
// Destory the context
|
||
if (pSmbExchange->ReferenceCount == 0) {
|
||
SmbCeAcquireResource();
|
||
|
||
RemoveEntryList(&pSmbExchange->ExchangeList);
|
||
|
||
SmbCeReleaseResource();
|
||
|
||
SmbCePrepareExchangeForReuse(pSmbExchange);
|
||
|
||
SmbCeDecrementActiveExchangeCount();
|
||
|
||
// Discard the memory associated with the exchange
|
||
SmbMmFreeExchange(pSmbExchange);
|
||
} else {
|
||
RxDbgTrace(
|
||
0,
|
||
Dbg,
|
||
("SmbCeDiscardExchange: Exchange %lx not discarded %ld\n",
|
||
pSmbExchange,pSmbExchange->ReferenceCount)
|
||
);
|
||
}
|
||
}
|
||
|
||
VOID
|
||
SmbCeDiscardExchange(PVOID pExchange)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine discards an exchange.
|
||
|
||
Arguments:
|
||
|
||
pExchange - the exchange to be discarded.
|
||
|
||
Notes:
|
||
|
||
The destruction of an exchange instance is posted to a worker thread in order to
|
||
avoid deadlock in transport.
|
||
|
||
--*/
|
||
{
|
||
PSMB_EXCHANGE pSmbExchange = pExchange;
|
||
PSMBCEDB_SERVER_ENTRY pServerEntry = SmbCeGetExchangeServerEntry(pSmbExchange);
|
||
|
||
// Disassociate the MID associated with the exchange
|
||
if (pSmbExchange->SmbCeFlags & SMBCE_EXCHANGE_MID_VALID) {
|
||
SmbCeDissociateMidFromExchange(pServerEntry,pSmbExchange);
|
||
}
|
||
|
||
RxPostToWorkerThread(
|
||
MRxSmbDeviceObject,
|
||
CriticalWorkQueue,
|
||
&((PSMB_EXCHANGE)pExchange)->WorkQueueItem,
|
||
SmbCeDiscardExchangeWorkerThreadRoutine,
|
||
(PSMB_EXCHANGE)pExchange);
|
||
}
|
||
|
||
NTSTATUS
|
||
SmbCeCancelExchange(
|
||
PRX_CONTEXT pRxContext)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine initiates the cancellation of an exchange.
|
||
|
||
Arguments:
|
||
|
||
pRxContext - the RX_CONTEXT instance for which cancellation needs to be
|
||
initiated.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The return status for the operation
|
||
|
||
Notes:
|
||
|
||
The cancellation policy that has been implemented is a "best effort" policy.
|
||
Since the server has already committed resources to an operation at its end
|
||
the best that we can do within the scope of the SMB protocol is to initiate
|
||
a cancellation operation by sending the appropriate SMB_COM_NT_CANCEL command
|
||
|
||
Not all dialects of SMB support this command. For the downlevel dialects the
|
||
best that we can do is to ensure that the MID is not reused during the lifetime
|
||
of the connection. This will result in a gradual degradation of performance.
|
||
|
||
The difficulty in detecting the end of operations is that there are MIDS
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
PSMB_EXCHANGE pExchange;
|
||
|
||
PMRXSMB_RX_CONTEXT pMRxSmbContext;
|
||
|
||
SmbCeLog(("SmbCe Cancel %lx\n",pRxContext));
|
||
|
||
SmbCeAcquireSpinLock();
|
||
|
||
pMRxSmbContext = MRxSmbGetMinirdrContext(pRxContext);
|
||
pExchange = (PSMB_EXCHANGE)pMRxSmbContext->pCancelContext;
|
||
|
||
if (pExchange != NULL) {
|
||
if (!FlagOn(pExchange->SmbCeFlags,SMBCE_EXCHANGE_FINALIZED)) {
|
||
if (pExchange->ReceivePendingOperations > 0) {
|
||
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
||
|
||
// This exchange is awaiting a response from the server. In all
|
||
// these cases a CANCEL command needs to be sent to the server
|
||
// This command can only be sent to NT servers. For non NT
|
||
// servers this exchange can be terminated with the detrimental
|
||
// side effect of reducing the maximum number of commands by 1.
|
||
|
||
InterlockedIncrement(&pExchange->LocalPendingOperations);
|
||
|
||
Status = STATUS_MORE_PROCESSING_REQUIRED;
|
||
|
||
pServerEntry = SmbCeGetExchangeServerEntry(pExchange);
|
||
|
||
if (!FlagOn(pServerEntry->Server.DialectFlags,DF_NT_SMBS)) {
|
||
if (pExchange->SmbCeFlags & SMBCE_EXCHANGE_MID_VALID) {
|
||
NTSTATUS LocalStatus;
|
||
|
||
LocalStatus = SmbCepDiscardMidAssociatedWithExchange(
|
||
pExchange);
|
||
|
||
ASSERT(LocalStatus == STATUS_SUCCESS);
|
||
}
|
||
}
|
||
} else {
|
||
InterlockedCompareExchange(
|
||
&pExchange->CancellationStatus,
|
||
SMBCE_EXCHANGE_CANCELLED,
|
||
SMBCE_EXCHANGE_NOT_CANCELLED);
|
||
}
|
||
}
|
||
}
|
||
|
||
SmbCeReleaseSpinLock();
|
||
|
||
if (Status == STATUS_MORE_PROCESSING_REQUIRED) {
|
||
PSMBCE_SERVER pServer;
|
||
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
||
|
||
SmbCeLog(("SmbCeCancel Initiate %lx\n",pExchange));
|
||
|
||
pServerEntry = SmbCeGetExchangeServerEntry(pExchange);
|
||
pServer = SmbCeGetExchangeServer(pExchange);
|
||
|
||
if (FlagOn(pServer->DialectFlags,DF_NT_SMBS)) {
|
||
UCHAR LastCommandInHeader;
|
||
PUCHAR pCommand;
|
||
PSMB_HEADER pSmbHeader;
|
||
PNT_SMB_HEADER pNtSmbHeader;
|
||
|
||
#define CANCEL_BUFFER_SIZE (sizeof(SMB_HEADER) + sizeof(REQ_NT_CANCEL))
|
||
|
||
BYTE SmbBuffer[TRANSPORT_HEADER_SIZE + CANCEL_BUFFER_SIZE];
|
||
PBYTE CancelRequestBuffer = SmbBuffer + TRANSPORT_HEADER_SIZE;
|
||
ULONG CancelRequestBufferSize = CANCEL_BUFFER_SIZE;
|
||
|
||
pSmbHeader = (PSMB_HEADER)CancelRequestBuffer;
|
||
pNtSmbHeader = (PNT_SMB_HEADER)pSmbHeader;
|
||
|
||
// Before issuing the cancel request ensure that if this exchange
|
||
// is set as a timed receive operation. This will ensure that if
|
||
// the cancel is delayed at the server we will initiate a tear down
|
||
// of the connection.
|
||
|
||
if (!FlagOn(
|
||
pExchange->SmbCeFlags,
|
||
SMBCE_EXCHANGE_TIMED_RECEIVE_OPERATION)) {
|
||
|
||
SmbCeAcquireResource();
|
||
|
||
SmbCeSetExpiryTime(pExchange);
|
||
|
||
pExchange->SmbCeFlags |= SMBCE_EXCHANGE_TIMED_RECEIVE_OPERATION;
|
||
|
||
SmbCeReleaseResource();
|
||
}
|
||
|
||
// Build the Cancel request and send it across to the server.
|
||
Status = SmbCeBuildSmbHeader(
|
||
pExchange,
|
||
CancelRequestBuffer,
|
||
CancelRequestBufferSize,
|
||
&CancelRequestBufferSize,
|
||
&LastCommandInHeader,
|
||
&pCommand);
|
||
|
||
ASSERT(LastCommandInHeader == SMB_COM_NO_ANDX_COMMAND);
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
PREQ_NT_CANCEL pCancelRequest = (PREQ_NT_CANCEL)(&CancelRequestBuffer[sizeof(SMB_HEADER)]);
|
||
PMDL pCancelSmbMdl;
|
||
|
||
*pCommand = SMB_COM_NT_CANCEL;
|
||
|
||
SmbPutUshort(&pSmbHeader->Mid,pExchange->Mid);
|
||
|
||
if (BooleanFlagOn(
|
||
pExchange->SmbCeFlags,
|
||
SMBCE_EXCHANGE_FULL_PROCESSID_SPECIFIED)) {
|
||
|
||
ULONG ProcessId;
|
||
|
||
ProcessId = RxGetRequestorProcessId(pRxContext);
|
||
|
||
SmbPutUshort(&pNtSmbHeader->Pid, (USHORT)((ProcessId) & 0xFFFF));
|
||
SmbPutUshort(&pNtSmbHeader->PidHigh, (USHORT)((ProcessId) >> 16));
|
||
}
|
||
|
||
SmbPutUshort(&pCancelRequest->WordCount,0);
|
||
pCancelRequest->ByteCount = 0;
|
||
CancelRequestBufferSize = CANCEL_BUFFER_SIZE;
|
||
|
||
RxAllocateHeaderMdl(
|
||
CancelRequestBuffer,
|
||
CancelRequestBufferSize,
|
||
pCancelSmbMdl
|
||
);
|
||
|
||
if (pCancelSmbMdl != NULL) {
|
||
RxProbeAndLockHeaderPages(
|
||
pCancelSmbMdl,
|
||
KernelMode,
|
||
IoModifyAccess,
|
||
Status);
|
||
|
||
if (Status == STATUS_SUCCESS) {
|
||
Status = SmbCeSendToServer(
|
||
pServerEntry,
|
||
RXCE_SEND_SYNCHRONOUS,
|
||
pCancelSmbMdl,
|
||
CancelRequestBufferSize);
|
||
|
||
RxUnlockHeaderPages(pCancelSmbMdl);
|
||
}
|
||
|
||
IoFreeMdl(pCancelSmbMdl);
|
||
}
|
||
}
|
||
} else {
|
||
SmbCeFinalizeExchangeOnDisconnect(pExchange);
|
||
}
|
||
|
||
|
||
InterlockedCompareExchange(
|
||
&pExchange->CancellationStatus,
|
||
SMBCE_EXCHANGE_CANCELLED,
|
||
SMBCE_EXCHANGE_NOT_CANCELLED);
|
||
|
||
SmbCeDecrementPendingLocalOperationsAndFinalize(pExchange);
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
SmbCeIncrementPendingOperations(
|
||
PSMB_EXCHANGE pExchange,
|
||
ULONG PendingOperationMask,
|
||
PVOID FileName,
|
||
ULONG FileLine)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine increments the appropriate pending operation count
|
||
|
||
Arguments:
|
||
|
||
pExchange - the exchange to be finalized.
|
||
|
||
PendingOperationsMask -- the pending operations to be incremented
|
||
|
||
Return Value:
|
||
|
||
RxStatus(SUCCESS) if successful
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
||
|
||
pServerEntry = SmbCeGetExchangeServerEntry(pExchange);
|
||
|
||
SmbCeAcquireSpinLock();
|
||
|
||
if (!BooleanFlagOn(pExchange->SmbCeFlags,SMBCE_EXCHANGE_FINALIZED)) {
|
||
if ((pServerEntry != NULL) &&
|
||
((pServerEntry->ServerStatus == STATUS_SUCCESS) ||
|
||
(pExchange->NodeTypeCode == SMB_EXCHANGE_NTC(ADMIN_EXCHANGE)))) {
|
||
|
||
if (PendingOperationMask & SMBCE_LOCAL_OPERATION) {
|
||
pExchange->LocalPendingOperations++;
|
||
}
|
||
|
||
if (PendingOperationMask & SMBCE_SEND_COMPLETE_OPERATION) {
|
||
pExchange->SendCompletePendingOperations++;
|
||
}
|
||
|
||
if (PendingOperationMask & SMBCE_COPY_DATA_OPERATION) {
|
||
pExchange->CopyDataPendingOperations++;
|
||
}
|
||
|
||
if (PendingOperationMask & SMBCE_RECEIVE_OPERATION) {
|
||
pExchange->ReceivePendingOperations++;
|
||
}
|
||
|
||
Status = STATUS_SUCCESS;
|
||
} else {
|
||
if ((PendingOperationMask & SMBCE_LOCAL_OPERATION) &&
|
||
(PendingOperationMask & ~SMBCE_LOCAL_OPERATION) == 0) {
|
||
|
||
pExchange->LocalPendingOperations++;
|
||
Status = STATUS_SUCCESS;
|
||
} else {
|
||
Status = STATUS_CONNECTION_DISCONNECTED;
|
||
}
|
||
}
|
||
} else {
|
||
Status = STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
SmbCeReleaseSpinLock();
|
||
|
||
return Status;
|
||
}
|
||
|
||
VOID
|
||
SmbCeFinalizeExchangeWorkerThreadRoutine(
|
||
PSMB_EXCHANGE pExchange)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the worker thread exchange finalization routine.
|
||
|
||
Arguments:
|
||
|
||
pExchange - the exchange to be finalized.
|
||
|
||
--*/
|
||
{
|
||
BOOLEAN fPostFinalize;
|
||
NTSTATUS Status;
|
||
|
||
PAGED_CODE();
|
||
|
||
Status = SMB_EXCHANGE_DISPATCH(
|
||
pExchange,
|
||
Finalize,
|
||
(pExchange,&fPostFinalize));
|
||
|
||
ASSERT(!fPostFinalize && (Status == STATUS_SUCCESS));
|
||
}
|
||
|
||
VOID
|
||
SmbCepFinalizeExchange(
|
||
PSMB_EXCHANGE pExchange)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the common finalization routine used by both the routines below
|
||
|
||
Arguments:
|
||
|
||
pExchange - the exchange to be finalized.
|
||
|
||
--*/
|
||
{
|
||
BOOLEAN fAssociatedExchange;
|
||
|
||
ASSERT(FlagOn(pExchange->SmbCeFlags,SMBCE_EXCHANGE_FINALIZED));
|
||
fAssociatedExchange = BooleanFlagOn(pExchange->SmbCeFlags,SMBCE_ASSOCIATED_EXCHANGE);
|
||
|
||
if (fAssociatedExchange) {
|
||
PSMB_EXCHANGE pMasterExchange;
|
||
|
||
// The local operation will be decremented on resumption of
|
||
// the finalization routine
|
||
pMasterExchange = pExchange->Associated.pMasterExchange;
|
||
SmbCeIncrementPendingLocalOperations(pMasterExchange);
|
||
|
||
RxPostToWorkerThread(
|
||
MRxSmbDeviceObject,
|
||
CriticalWorkQueue,
|
||
&pExchange->WorkQueueItem,
|
||
SmbCepFinalizeAssociatedExchange,
|
||
pExchange);
|
||
} else {
|
||
NTSTATUS Status;
|
||
BOOLEAN fPostFinalize = FALSE;
|
||
|
||
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
||
|
||
pServerEntry = SmbCeGetExchangeServerEntry(pExchange);
|
||
|
||
pExchange->ExpiryTime.QuadPart = 0;
|
||
|
||
if (!FlagOn(pExchange->SmbCeFlags,SMBCE_EXCHANGE_RETAIN_MID)) {
|
||
SmbCeDissociateMidFromExchange(
|
||
pServerEntry,
|
||
pExchange);
|
||
}
|
||
|
||
Status = SMB_EXCHANGE_DISPATCH(
|
||
pExchange,
|
||
Finalize,
|
||
(pExchange,&fPostFinalize));
|
||
|
||
if ((Status == STATUS_SUCCESS) &&
|
||
fPostFinalize) {
|
||
// Post the request to a worker thread so that the finalization can be completed
|
||
// at a lower IRQL.
|
||
RxPostToWorkerThread(
|
||
MRxSmbDeviceObject,
|
||
CriticalWorkQueue,
|
||
&pExchange->WorkQueueItem,
|
||
SmbCeFinalizeExchangeWorkerThreadRoutine,
|
||
pExchange);
|
||
}
|
||
}
|
||
}
|
||
|
||
#define SENTINEL_ENTRY ((PSINGLE_LIST_ENTRY)IntToPtr(0xffffffff))
|
||
|
||
VOID
|
||
SmbCepFinalizeAssociatedExchange(
|
||
PSMB_EXCHANGE pExchange)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the common finalization routine used by both the routines below
|
||
|
||
Arguments:
|
||
|
||
pExchange - the exchange to be finalized.
|
||
|
||
--*/
|
||
{
|
||
PSMB_EXCHANGE pMasterExchange;
|
||
PSMB_EXCHANGE pAssociatedExchange;
|
||
SINGLE_LIST_ENTRY AssociatedExchangeList;
|
||
|
||
ASSERT(FlagOn(pExchange->SmbCeFlags,SMBCE_ASSOCIATED_EXCHANGE));
|
||
|
||
pMasterExchange = pExchange->Associated.pMasterExchange;
|
||
|
||
ASSERT(pMasterExchange->Master.AssociatedExchangesToBeFinalized.Next != NULL);
|
||
|
||
for (;;) {
|
||
BOOLEAN fAllAssociatedExchangesFinalized = FALSE;
|
||
|
||
SmbCeAcquireSpinLock();
|
||
|
||
if (pMasterExchange->Master.AssociatedExchangesToBeFinalized.Next == SENTINEL_ENTRY) {
|
||
pMasterExchange->Master.AssociatedExchangesToBeFinalized.Next = NULL;
|
||
fAllAssociatedExchangesFinalized = TRUE;
|
||
} else if (pMasterExchange->Master.AssociatedExchangesToBeFinalized.Next == NULL) {
|
||
fAllAssociatedExchangesFinalized = TRUE;
|
||
} else {
|
||
AssociatedExchangeList.Next =
|
||
pMasterExchange->Master.AssociatedExchangesToBeFinalized.Next;
|
||
|
||
pMasterExchange->Master.AssociatedExchangesToBeFinalized.Next =
|
||
SENTINEL_ENTRY;
|
||
}
|
||
|
||
SmbCeReleaseSpinLock();
|
||
|
||
if (!fAllAssociatedExchangesFinalized) {
|
||
for (;;) {
|
||
PSINGLE_LIST_ENTRY pAssociatedExchangeEntry;
|
||
|
||
pAssociatedExchangeEntry = AssociatedExchangeList.Next;
|
||
|
||
if ((pAssociatedExchangeEntry != NULL) &&
|
||
(pAssociatedExchangeEntry != SENTINEL_ENTRY)) {
|
||
NTSTATUS Status;
|
||
BOOLEAN fPostFinalize = FALSE;
|
||
|
||
AssociatedExchangeList.Next = pAssociatedExchangeEntry->Next;
|
||
|
||
pAssociatedExchange = (PSMB_EXCHANGE)
|
||
CONTAINING_RECORD(
|
||
pAssociatedExchangeEntry,
|
||
SMB_EXCHANGE,
|
||
Associated.NextAssociatedExchange);
|
||
|
||
ASSERT(IsListEmpty(&pAssociatedExchange->WorkQueueItem.List));
|
||
|
||
Status = SMB_EXCHANGE_DISPATCH(
|
||
pAssociatedExchange,
|
||
Finalize,
|
||
(pAssociatedExchange,&fPostFinalize));
|
||
} else {
|
||
break;
|
||
}
|
||
};
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
|
||
SmbCeDecrementPendingLocalOperationsAndFinalize(pMasterExchange);
|
||
}
|
||
|
||
BOOLEAN
|
||
SmbCeCanExchangeBeFinalized(
|
||
PSMB_EXCHANGE pExchange,
|
||
PSMBCE_EXCHANGE_STATUS pExchangeStatus)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine determines if the exchange instance can be finalized.
|
||
|
||
Arguments:
|
||
|
||
pExchange - the exchange to be finalized.
|
||
|
||
pExchangeStatus - the finalization status
|
||
|
||
Return Value:
|
||
|
||
TRUE if the exchange can be finalized
|
||
|
||
Notes:
|
||
|
||
As a side effect it also sets the SMBCE_EXCHANGE_FINALIZED flag
|
||
|
||
The SmbCe spin lock must have been acquire on entry
|
||
|
||
--*/
|
||
{
|
||
BOOLEAN fFinalizeExchange = FALSE;
|
||
BOOLEAN fAssociatedExchange;
|
||
|
||
fAssociatedExchange = BooleanFlagOn(pExchange->SmbCeFlags,SMBCE_ASSOCIATED_EXCHANGE);
|
||
|
||
if (!(pExchange->SmbCeFlags & SMBCE_EXCHANGE_FINALIZED)) {
|
||
if ((pExchange->ReceivePendingOperations == 0) &&
|
||
(pExchange->CopyDataPendingOperations == 0) &&
|
||
(pExchange->SendCompletePendingOperations == 0) &&
|
||
(pExchange->LocalPendingOperations == 0)) {
|
||
|
||
fFinalizeExchange = TRUE;
|
||
*pExchangeStatus = SmbCeExchangeFinalized;
|
||
pExchange->SmbCeFlags |= SMBCE_EXCHANGE_FINALIZED;
|
||
|
||
if (fAssociatedExchange) {
|
||
PSMB_EXCHANGE pMasterExchange = pExchange->Associated.pMasterExchange;
|
||
|
||
if (pMasterExchange->Master.AssociatedExchangesToBeFinalized.Next != NULL) {
|
||
fFinalizeExchange = FALSE;
|
||
}
|
||
|
||
pExchange->Associated.NextAssociatedExchange.Next =
|
||
pMasterExchange->Master.AssociatedExchangesToBeFinalized.Next;
|
||
pMasterExchange->Master.AssociatedExchangesToBeFinalized.Next =
|
||
&pExchange->Associated.NextAssociatedExchange;
|
||
}
|
||
} else {
|
||
*pExchangeStatus = SmbCeExchangeNotFinalized;
|
||
}
|
||
} else {
|
||
*pExchangeStatus = SmbCeExchangeAlreadyFinalized;
|
||
}
|
||
|
||
if (fFinalizeExchange &&
|
||
(pExchange->RxContext != NULL)) {
|
||
NTSTATUS Status;
|
||
PMRXSMB_RX_CONTEXT pMRxSmbContext;
|
||
|
||
pMRxSmbContext = MRxSmbGetMinirdrContext(pExchange->RxContext);
|
||
pMRxSmbContext->pCancelContext = NULL;
|
||
|
||
Status = RxSetMinirdrCancelRoutine(
|
||
pExchange->RxContext,
|
||
NULL);
|
||
}
|
||
|
||
return fFinalizeExchange;
|
||
}
|
||
|
||
SMBCE_EXCHANGE_STATUS
|
||
SmbCeFinalizeExchange(
|
||
PSMB_EXCHANGE pExchange)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine finalizes an exchange instance.
|
||
|
||
Arguments:
|
||
|
||
pExchange - the exchange to be finalized.
|
||
|
||
Return Value:
|
||
|
||
appropriate exchange status
|
||
|
||
Notes:
|
||
|
||
When an exchange is initiated and the start routine is invoked a number of
|
||
SMB's are sent. This routine is invoked when all processing pertaining to the
|
||
SMB's that have been sent has ceased.
|
||
|
||
This routine encapsulates all the idiosyncratic behaviour associated with the
|
||
transports.
|
||
|
||
--*/
|
||
{
|
||
BOOLEAN fFinalizeExchange = FALSE;
|
||
|
||
SMBCE_EXCHANGE_STATUS ExchangeStatus;
|
||
|
||
SmbCeAcquireSpinLock();
|
||
|
||
fFinalizeExchange = SmbCeCanExchangeBeFinalized(
|
||
pExchange,
|
||
&ExchangeStatus);
|
||
|
||
SmbCeReleaseSpinLock();
|
||
|
||
if (fFinalizeExchange) {
|
||
SmbCepFinalizeExchange(pExchange);
|
||
}
|
||
|
||
return ExchangeStatus;
|
||
}
|
||
|
||
NTSTATUS
|
||
SmbCeDecrementPendingOperations(
|
||
PSMB_EXCHANGE pExchange,
|
||
ULONG PendingOperationMask,
|
||
PVOID FileName,
|
||
ULONG FileLine)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine decrements the corresponding pending operation count
|
||
and finalizes an exchange instance if required
|
||
|
||
Arguments:
|
||
|
||
pExchange - the exchange to be finalized.
|
||
|
||
PendingOperationsMask -- the pending operations to be decremented.
|
||
|
||
Return Value:
|
||
|
||
appropriate exchange status
|
||
|
||
Notes:
|
||
|
||
When an exchange is initiated and the start routine is invoked a number of
|
||
SMB's are sent. This routine is invoked when all processing pertaining to the
|
||
SMB's that have been sent has ceased.
|
||
|
||
This routine encapsulates all the idiosyncratic behaviour associated with the
|
||
transports.
|
||
|
||
--*/
|
||
{
|
||
SmbCeAcquireSpinLock();
|
||
|
||
ASSERT(!BooleanFlagOn(pExchange->SmbCeFlags,SMBCE_EXCHANGE_FINALIZED));
|
||
|
||
if (PendingOperationMask & SMBCE_LOCAL_OPERATION) {
|
||
ASSERT(pExchange->LocalPendingOperations > 0);
|
||
pExchange->LocalPendingOperations--;
|
||
}
|
||
|
||
if (PendingOperationMask & SMBCE_SEND_COMPLETE_OPERATION) {
|
||
ASSERT(pExchange->SendCompletePendingOperations > 0);
|
||
pExchange->SendCompletePendingOperations--;
|
||
}
|
||
|
||
if (PendingOperationMask & SMBCE_COPY_DATA_OPERATION) {
|
||
ASSERT(pExchange->CopyDataPendingOperations > 0);
|
||
pExchange->CopyDataPendingOperations--;
|
||
}
|
||
|
||
if ((PendingOperationMask & SMBCE_RECEIVE_OPERATION) &&
|
||
(pExchange->ReceivePendingOperations > 0)) {
|
||
pExchange->ReceivePendingOperations--;
|
||
}
|
||
SmbCeReleaseSpinLock();
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
SMBCE_EXCHANGE_STATUS
|
||
SmbCeDecrementPendingOperationsAndFinalize(
|
||
PSMB_EXCHANGE pExchange,
|
||
ULONG PendingOperationMask,
|
||
PVOID FileName,
|
||
ULONG FileLine)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine decrements the corresponding pending operation count
|
||
and finalizes an exchange instance if required
|
||
|
||
Arguments:
|
||
|
||
pExchange - the exchange to be finalized.
|
||
|
||
PendingOperationsMask -- the pending operations to be decremented.
|
||
|
||
Return Value:
|
||
|
||
appropriate exchange status
|
||
|
||
Notes:
|
||
|
||
When an exchange is initiated and the start routine is invoked a number of
|
||
SMB's are sent. This routine is invoked when all processing pertaining to the
|
||
SMB's that have been sent has ceased.
|
||
|
||
This routine encapsulates all the idiosyncratic behaviour associated with the
|
||
transports.
|
||
|
||
--*/
|
||
{
|
||
BOOLEAN fFinalizeExchange = FALSE;
|
||
SMBCE_EXCHANGE_STATUS ExchangeStatus;
|
||
|
||
SmbCeAcquireSpinLock();
|
||
|
||
ASSERT(!BooleanFlagOn(pExchange->SmbCeFlags,SMBCE_EXCHANGE_FINALIZED));
|
||
|
||
if (PendingOperationMask & SMBCE_LOCAL_OPERATION) {
|
||
ASSERT(pExchange->LocalPendingOperations > 0);
|
||
pExchange->LocalPendingOperations--;
|
||
}
|
||
|
||
if (PendingOperationMask & SMBCE_SEND_COMPLETE_OPERATION) {
|
||
ASSERT(pExchange->SendCompletePendingOperations > 0);
|
||
pExchange->SendCompletePendingOperations--;
|
||
}
|
||
|
||
if (PendingOperationMask & SMBCE_COPY_DATA_OPERATION) {
|
||
ASSERT(pExchange->CopyDataPendingOperations > 0);
|
||
pExchange->CopyDataPendingOperations--;
|
||
}
|
||
|
||
if ((PendingOperationMask & SMBCE_RECEIVE_OPERATION) &&
|
||
(pExchange->ReceivePendingOperations > 0)) {
|
||
pExchange->ReceivePendingOperations--;
|
||
}
|
||
|
||
fFinalizeExchange = SmbCeCanExchangeBeFinalized(
|
||
pExchange,
|
||
&ExchangeStatus);
|
||
|
||
|
||
SmbCeReleaseSpinLock();
|
||
|
||
if (fFinalizeExchange) {
|
||
SmbCepFinalizeExchange(pExchange);
|
||
}
|
||
|
||
return ExchangeStatus;
|
||
}
|
||
|
||
VOID
|
||
SmbCeFinalizeExchangeOnDisconnect(
|
||
PSMB_EXCHANGE pExchange)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine handles the finalization of an exchange instance during transport disconnects
|
||
|
||
Arguments:
|
||
|
||
pExchange - the exchange instance
|
||
|
||
--*/
|
||
{
|
||
PAGED_CODE();
|
||
|
||
if (pExchange != NULL) {
|
||
pExchange->Status = STATUS_CONNECTION_DISCONNECTED;
|
||
pExchange->SmbStatus = STATUS_CONNECTION_DISCONNECTED;
|
||
pExchange->ReceivePendingOperations = 0;
|
||
|
||
SmbCeFinalizeExchange(pExchange);
|
||
}
|
||
}
|
||
|
||
VOID
|
||
SmbCeSetExpiryTime(
|
||
PSMB_EXCHANGE pExchange)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine sets the expiry time for a timed exchange,
|
||
i.e., SMBCE_EXCHANGE_TIMED_OPERATION must be set
|
||
|
||
Arguments:
|
||
|
||
pExchange - the exchange instance.
|
||
|
||
Notes:
|
||
|
||
--*/
|
||
{
|
||
LARGE_INTEGER CurrentTime;
|
||
LARGE_INTEGER ExpiryTimeInTicks;
|
||
|
||
KeQueryTickCount( &CurrentTime );
|
||
|
||
ExpiryTimeInTicks.QuadPart = (1000 * 1000 * 10) / KeQueryTimeIncrement();
|
||
|
||
ExpiryTimeInTicks.QuadPart = MRxSmbConfiguration.SessionTimeoutInterval * ExpiryTimeInTicks.QuadPart;
|
||
|
||
pExchange->ExpiryTime.QuadPart = CurrentTime.QuadPart + ExpiryTimeInTicks.QuadPart;
|
||
}
|
||
|
||
BOOLEAN
|
||
SmbCeDetectExpiredExchanges(
|
||
PSMBCEDB_SERVER_ENTRY pServerEntry)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine periodically walks the list of timed exchanges and chooses the
|
||
instances for finalization.
|
||
|
||
A timed exchange choosen by this routine will have waited for some network
|
||
response for the given time interval
|
||
|
||
Arguments:
|
||
|
||
pServerEntry -- the server entry for which this needs to be done
|
||
|
||
Notes:
|
||
|
||
--*/
|
||
{
|
||
BOOLEAN ExpiredExchangesDetected = FALSE;
|
||
PSMB_EXCHANGE pExchange;
|
||
PLIST_ENTRY pListHead;
|
||
PLIST_ENTRY pListEntry;
|
||
LARGE_INTEGER CurrentTime;
|
||
|
||
PAGED_CODE();
|
||
|
||
KeQueryTickCount( &CurrentTime );
|
||
|
||
SmbCeAcquireResource();
|
||
|
||
pListHead = &pServerEntry->ActiveExchanges;
|
||
pListEntry = pListHead->Flink;
|
||
|
||
while (pListEntry != pListHead) {
|
||
PLIST_ENTRY pNextListEntry;
|
||
|
||
pNextListEntry = pListEntry->Flink;
|
||
pExchange = (PSMB_EXCHANGE)CONTAINING_RECORD(pListEntry,SMB_EXCHANGE,ExchangeList);
|
||
|
||
// There are two kinds of exchanges that are candidates for
|
||
// time out finalization.
|
||
// (1) Any exchange which has a outstanding send complete
|
||
// operation which has not completed.
|
||
// (2) timed network operation exchanges which have a
|
||
// receive or copy data operation pending.
|
||
//
|
||
// In all such cases the associated server entry is marked
|
||
// for tear down and further processing is terminated.
|
||
//
|
||
|
||
if ((pExchange->SendCompletePendingOperations > 0) ||
|
||
(FlagOn(pExchange->SmbCeFlags,SMBCE_EXCHANGE_TIMED_RECEIVE_OPERATION) &&
|
||
((pExchange->CopyDataPendingOperations > 0) ||
|
||
(pExchange->ReceivePendingOperations > 0)))) {
|
||
if ((pExchange->ExpiryTime.QuadPart != 0) &&
|
||
(pExchange->ExpiryTime.QuadPart < CurrentTime.QuadPart) &&
|
||
!FlagOn(pExchange->SmbCeFlags,SMBCE_EXCHANGE_FINALIZED)) {
|
||
|
||
RxLog(("Marking server for tear down %lx \n",pServerEntry));
|
||
|
||
ExpiredExchangesDetected = TRUE;
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
pListEntry = pNextListEntry;
|
||
}
|
||
|
||
SmbCeReleaseResource();
|
||
|
||
return ExpiredExchangesDetected;
|
||
}
|
||
|
||
//
|
||
// Default handler implementation of exchange handler functions.
|
||
//
|
||
|
||
NTSTATUS
|
||
DefaultSmbExchangeIndError(
|
||
IN PSMB_EXCHANGE pExchange) // the SMB exchange instance
|
||
{
|
||
PAGED_CODE();
|
||
|
||
UNREFERENCED_PARAMETER(pExchange);
|
||
return STATUS_NOT_IMPLEMENTED;
|
||
}
|
||
|
||
NTSTATUS
|
||
DefaultSmbExchangeIndReceive(
|
||
IN PSMB_EXCHANGE pExchange) // The exchange instance
|
||
{
|
||
PAGED_CODE();
|
||
|
||
UNREFERENCED_PARAMETER(pExchange);
|
||
return STATUS_NOT_IMPLEMENTED;
|
||
}
|
||
|
||
NTSTATUS
|
||
DefaultSmbExchangeIndSendCallback(
|
||
IN PSMB_EXCHANGE pExchange) // The exchange instance
|
||
{
|
||
PAGED_CODE();
|
||
|
||
UNREFERENCED_PARAMETER(pExchange);
|
||
return STATUS_NOT_IMPLEMENTED;
|
||
}
|
||
|
||
|