You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3126 lines
104 KiB
3126 lines
104 KiB
/*++
|
|
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 pNetRootEntry2;
|
|
|
|
RxDbgTrace( 0, Dbg, ("SmbCeInitiateExchange: Exchange %lx State %lx\n",pExchange,pExchange->SmbCeState));
|
|
pNetRootEntry2 = 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;
|
|
}
|
|
|
|
|