2025-04-27 07:49:33 -04:00

1809 lines
62 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++ BUILD Version: 0009 // Increment this if a change has global effects
Copyright (c) 1987-1993 Microsoft Corporation
Module Name:
smbcxchng.c
Abstract:
This is the include file that implements the SMB_*_EXCHANGE creation, deletion and
dispatch routines.
Notes:
--*/
#include "precomp.h"
#pragma hdrstop
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()
{
KeInitializeEvent(
&SmbCeStartStopContext.StopEvent,
NotificationEvent,
FALSE);
SmbCeStartStopContext.ActiveExchanges = 0;
SmbCeStartStopContext.State = SMBCE_STARTED;
return STATUS_SUCCESS;
}
NTSTATUS
MRxSmbTearDownSmbCe()
{
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()
{
NTSTATUS Status = STATUS_SUCCESS;
SmbCeAcquireSpinLock();
if (SmbCeStartStopContext.State != SMBCE_STARTED) {
Status = STATUS_UNSUCCESSFUL;
} else {
InterlockedIncrement(&SmbCeStartStopContext.ActiveExchanges);
}
SmbCeReleaseSpinLock();
return Status;
}
VOID
SmbCeDecrementActiveExchangeCount()
{
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;
BOOLEAN ResourceAcquired;
pServerEntry = pExchange->SmbCeContext.pServerEntry;
ASSERT(pExchange->SmbCeState == SMBCE_EXCHANGE_INITIALIZATION_START);
if (pServerEntry->Header.State != SMBCEDB_ACTIVE) {
if (pExchange->SmbCeFlags & SMBCE_EXCHANGE_ATTEMPT_RECONNECTS) {
// Acquire the resource
SmbCeAcquireResource();
ResourceAcquired = TRUE;
switch (pServerEntry->Header.State) {
case SMBCEDB_INVALID:
{
SMBCEDB_OBJECT_STATE State;
// Assume the worst case. if everything goes well the ServerStatus will
// be updated as a result of parsing the negotiate response.
pServerEntry->ServerStatus = STATUS_CONNECTION_DISCONNECTED;
SmbCeUpdateServerEntryState(
pServerEntry,
SMBCEDB_CONSTRUCTION_IN_PROGRESS);
// release the resource for the server entry
ResourceAcquired = FALSE;
SmbCeReleaseResource();
// Initialize the transport associated with the server
Status = SmbCeInitializeServerTransport(pServerEntry);
if (Status == STATUS_SUCCESS) {
Status = SmbCeNegotiate(
pServerEntry,
(PMRX_SRV_CALL)pExchange->SmbCeContext.pVNetRoot->pNetRoot->pSrvCall);
}
if (Status != STATUS_SUCCESS) {
// Either the transport initialization failed or the NEGOTIATE
// SMB could not be sent ....
SmbCeUpdateServerEntryState(pServerEntry,SMBCEDB_MARKED_FOR_DELETION);
SmbCeCompleteServerEntryInitialization(pServerEntry);
InterlockedIncrement(&MRxIfsStatistics.Reconnects);
}
}
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;
SmbCeAddRequestEntry(&pServerEntry->OutstandingRequests,pRequestEntry);
Status = STATUS_PENDING;
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
}
break;
default :
Status = STATUS_CONNECTION_DISCONNECTED;
break;
}
if (ResourceAcquired) {
SmbCeReleaseResource();
}
} else {
Status = STATUS_CONNECTION_DISCONNECTED;
}
}
if ((Status == STATUS_SUCCESS) || (Status == STATUS_PENDING)) {
pExchange->SmbCeState = SMBCE_EXCHANGE_SERVER_INITIALIZED;
}
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;
PSMBCEDB_SERVER_ENTRY pServerEntry;
PSMBCEDB_SESSION_ENTRY pSessionEntry;
pServerEntry = pExchange->SmbCeContext.pServerEntry;
pSessionEntry = pExchange->SmbCeContext.pSessionEntry;
ASSERT(pSessionEntry != NULL);
ASSERT(pServerEntry->Header.ObjectType == SMBCEDB_OT_SERVER);
ASSERT(pExchange->SmbCeState == SMBCE_EXCHANGE_SERVER_INITIALIZED);
ASSERT(SmbCeGetServerType(pServerEntry) == SMBCEDB_FILE_SERVER);
// Acquire the resource
SmbCeAcquireResource();
Status = STATUS_USER_SESSION_DELETED;
fReestablishSession = BooleanFlagOn(pExchange->SmbCeFlags,SMBCE_EXCHANGE_ATTEMPT_RECONNECTS);
switch (pSessionEntry->Header.State) {
case SMBCEDB_ACTIVE:
Status = STATUS_SUCCESS;
break;
case SMBCEDB_INVALID:
if (!fReestablishSession) {
break;
}
RxDbgTrace( 0, Dbg, ("SmbCeReferenceSession: Reestablishing session\n"));
pSessionEntry->Session.UserId = 0;
// fall thru ...
case SMBCEDB_START_CONSTRUCTION:
ASSERT(SmbCeGetServerType(pServerEntry) == SMBCEDB_FILE_SERVER);
pExchange->SmbCeFlags |= SMBCE_EXCHANGE_SESSION_CONSTRUCTOR;
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;
SmbCeAddRequestEntry(&pSessionEntry->Requests,pRequestEntry);
Status = STATUS_PENDING;
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
}
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;
}
// Release the server resource ...
SmbCeReleaseResource();
if ((Status == STATUS_SUCCESS) || (Status == STATUS_PENDING)) {
pExchange->SmbCeState = SMBCE_EXCHANGE_SESSION_INITIALIZED;
}
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;
pServerEntry = pExchange->SmbCeContext.pServerEntry;
pNetRootEntry = pExchange->SmbCeContext.pNetRootEntry;
ASSERT(pNetRootEntry->Header.ObjectType == SMBCEDB_OT_NETROOT);
ASSERT(pServerEntry->Header.ObjectType == SMBCEDB_OT_SERVER);
ASSERT(pExchange->SmbCeState == SMBCE_EXCHANGE_SESSION_INITIALIZED);
// Acquire the resource
SmbCeAcquireResource();
Status = STATUS_CONNECTION_DISCONNECTED;
fReconnectNetRoot = BooleanFlagOn(pExchange->SmbCeFlags,SMBCE_EXCHANGE_ATTEMPT_RECONNECTS);
switch (pNetRootEntry->Header.State) {
case SMBCEDB_ACTIVE:
Status = STATUS_SUCCESS;
break;
case SMBCEDB_INVALID:
RxDbgTrace( 0, Dbg, ("SmbCeReferenceNetRoot: Reestablishing net root\n"));
if (!fReconnectNetRoot) {
break;
}
pNetRootEntry->NetRoot.TreeId = 0;
// fall thru
case SMBCEDB_START_CONSTRUCTION:
pExchange->SmbCeFlags |= SMBCE_EXCHANGE_NETROOT_CONSTRUCTOR;
pNetRootEntry->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;
SmbCeAddRequestEntry(&pNetRootEntry->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;
}
// Release the resource ...
SmbCeReleaseResource();
if ((Status == STATUS_SUCCESS) || (Status == STATUS_PENDING)) {
pExchange->SmbCeState = SMBCE_EXCHANGE_NETROOT_INITIALIZED;
}
return Status;
}
NTSTATUS
SmbCeInitiateExchange(
PSMB_EXCHANGE pExchange)
/*++
Routine Description:
This routine initiates 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;
ASSERT(pExchange->SmbCeContext.pServerEntry != NULL);
switch (SmbCeGetServerType(pExchange->SmbCeContext.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)) &&
((pExchange->SmbCeContext.pNetRootEntry == NULL) ||
(pExchange->SmbCeContext.pSessionEntry == NULL))) {
Status = STATUS_REQUEST_ABORTED;
break;
}
case SMBCEDB_MAILSLOT_SERVER:
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;
}
if (pExchange->pSmbCeSynchronizationEvent != NULL) {
KeInitializeEvent(
pExchange->pSmbCeSynchronizationEvent,
SynchronizationEvent,
FALSE);
}
for (;;) {
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 (SmbCeGetServerType(pExchange->SmbCeContext.pServerEntry) == SMBCEDB_MAILSLOT_SERVER) {
// Mailslot servers do not have any netroot/session associated with them.
pExchange->SmbCeState = SMBCE_EXCHANGE_INITIATED;
Status = STATUS_SUCCESS;
break;
} else {
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;
}
}
// 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:
pExchange->SmbCeState = SMBCE_EXCHANGE_INITIATED;
Status = STATUS_SUCCESS;
break;
default:
ASSERT(!"Valid State for a SMB exchange, exchange Initiation aborted");
break;
}
if ((pExchange->pSmbCeSynchronizationEvent != NULL) &&
(pExchange->SmbCeState != SMBCE_EXCHANGE_INITIATED) &&
(Status == STATUS_PENDING)) {
KeWaitForSingleObject(
pExchange->pSmbCeSynchronizationEvent,
Executive,
KernelMode,
FALSE,
NULL );
ASSERT(pExchange->Status != STATUS_PENDING);
Status = pExchange->Status;
if (Status != STATUS_SUCCESS) {
break;
}
} else {
break;
}
}
ASSERT((Status != STATUS_PENDING) ||
(pExchange->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",
pExchange->SmbCeContext.pServerEntry,
pExchange->SmbCeContext.pSessionEntry,
pExchange->SmbCeContext.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) {
// Start the exchange
ASSERT(pExchange->SmbCeState == SMBCE_EXCHANGE_INITIATED);
if ((pExchange->SmbCeContext.pServerEntry->Header.State == SMBCEDB_ACTIVE) ||
(pExchange->NodeTypeCode == SMB_EXCHANGE_NTC(ADMIN_EXCHANGE))) {
Status = SmbCeInitializeExchangeTransport(pExchange);
} else {
Status = STATUS_CONNECTION_DISCONNECTED;
}
if (Status == STATUS_SUCCESS) {
pExchange->SmbStatus = STATUS_SUCCESS;
pExchange->ServerVersion = pExchange->SmbCeContext.pServerEntry->Server.Version;
Status = SMB_EXCHANGE_DISPATCH(pExchange,Start,((PSMB_EXCHANGE)pExchange));
RxDbgTrace( 0, Dbg, ("SmbCeInitiateExchange: SMB_EXCHANGE_DISPATCH(Start) returned %lx\n",Status));
}
} else if (Status != STATUS_PENDING) {
RxDbgTrace( 0, Dbg, ("SmbCeInitiateExchange: Exchange(%lx) Initiation failed %lx \n",pExchange,Status));
}
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:
--*/
{
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 which 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 = 0;
USHORT Flags2 = 0;
PSMBCE_SERVER pServer;
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);
pServer = &pExchange->SmbCeContext.pServerEntry->Server;
if (pServer->Dialect == LANMAN21_DIALECT) {
Flags = (SMB_FLAGS_CASE_INSENSITIVE | SMB_FLAGS_CANONICALIZED_PATHS);
}
//DOWNLEVEL.NOTCORE flags for lanman10
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(pExchange->SmbCeContext.pServerEntry)) {
case SMBCEDB_MAILSLOT_SERVER :
break;
case SMBCEDB_FILE_SERVER:
{
if (pExchange->SmbCeContext.pSessionEntry != NULL) {
pSmbHeader->Uid = pExchange->SmbCeContext.pSessionEntry->Session.UserId;
}
if (pExchange->SmbCeContext.pNetRootEntry != NULL) {
pSmbHeader->Tid = pExchange->SmbCeContext.pNetRootEntry->NetRoot.TreeId;
}
pSmbBuffer = (PGENERIC_ANDX)(pSmbHeader + 1);
if ((pExchange->SmbCeFlags & SMBCE_EXCHANGE_SESSION_CONSTRUCTOR) ||
(pExchange->SmbCeFlags & SMBCE_EXCHANGE_NETROOT_CONSTRUCTOR)) {
// There is an opportunity to compound some SessionSetup/TreeConnect SMB with the
// given SMB command.
if (pExchange->SmbCeContext.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 session for share level AT LEAST NOT FOR CORE!!!
}
if (NT_SUCCESS(Status) &&
pExchange->SmbCeContext.pNetRootEntry->Header.State == SMBCEDB_CONSTRUCTION_IN_PROGRESS) {
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));
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
};
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 necessarily indicated.
BytesIndicated - the length of the SMB buffer available 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 successfully. 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 updating/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 fSessionSetupResponse = FALSE;
BOOLEAN fTreeConnectResponse = FALSE;
SMBCEDB_OBJECT_STATE SessionState;
SMBCEDB_OBJECT_STATE NetRootState;
// 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);
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;
}
}
// 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;
}
} else {
Status = SmbResponseStatus;
}
if (NT_SUCCESS(Status)) {
if (SessionSetupResponseLength + *pBytesConsumed <= BytesIndicated) {
*pBytesConsumed += SessionSetupResponseLength;
pSmbBuffer += SessionSetupResponseLength;
pExchange->SmbCeContext.pSessionEntry->Session.UserId = pSmbHeader->Uid;
fSessionSetupResponse = TRUE;
SessionState = SMBCEDB_ACTIVE;
InterlockedIncrement(&MRxIfsStatistics.Sessions);
} else {
Status = STATUS_MORE_PROCESSING_REQUIRED;
}
} else {
RxDbgTrace( 0, Dbg, ("SmbCeParseSmbHeader::Session setup and X Response %lx\n",Status));
fSessionSetupResponse = TRUE;
SessionState = SMBCEDB_MARKED_FOR_DELETION;
InterlockedIncrement(&MRxIfsStatistics.FailedSessions);
if ((SmbCommand == SMB_COM_TREE_CONNECT_ANDX) ||
(SmbCommand == SMB_COM_TREE_CONNECT)) {
RxDbgTrace( 0, Dbg, ("SmbCeParseSmbHeader:: Tearing down a tree connection\n"));
fTreeConnectResponse = TRUE;
NetRootState = SMBCEDB_MARKED_FOR_DELETION;
}
}
}
}
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;
}
}
// 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) {
ULONG TreeConnectResponseLength,TreeConnectByteCount,ServiceStringLength;
PUCHAR pShareTypeResponseString = NULL;
PRESP_21_TREE_CONNECT_ANDX p21TreeConnectAndXResponse;
p21TreeConnectAndXResponse = (PRESP_21_TREE_CONNECT_ANDX)(pSmbBuffer);
SmbCommand = p21TreeConnectAndXResponse->AndXCommand;
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
switch (p21TreeConnectAndXResponse->WordCount) {
case 0:
Status = SmbResponseStatus;
break;
case 3:
{
pShareTypeResponseString = (PUCHAR)&p21TreeConnectAndXResponse->Buffer;
TreeConnectByteCount = SmbGetUshort(&p21TreeConnectAndXResponse->ByteCount);
if (SmbCommand == SMB_COM_NO_ANDX_COMMAND) {
TreeConnectResponseLength =
FIELD_OFFSET(RESP_21_TREE_CONNECT_ANDX,Buffer) + TreeConnectByteCount;
Status = SmbResponseStatus;
} else {
TreeConnectResponseLength =
SmbGetUshort(&p21TreeConnectAndXResponse->AndXOffset) -
*pBytesConsumed;
}
}
break;
case 2:
{
PRESP_TREE_CONNECT_ANDX pTreeConnectAndXResponse;
pTreeConnectAndXResponse = (PRESP_TREE_CONNECT_ANDX)(pSmbBuffer);
//SmbCommand = pTreeConnectAndXResponse->AndXCommand;
ASSERT(FIELD_OFFSET(RESP_TREE_CONNECT_ANDX,AndXCommand)
==FIELD_OFFSET(RESP_21_TREE_CONNECT_ANDX,AndXCommand));
pShareTypeResponseString = (PUCHAR)&pTreeConnectAndXResponse->Buffer;
TreeConnectByteCount = SmbGetUshort(&pTreeConnectAndXResponse->ByteCount);
if (SmbCommand == SMB_COM_NO_ANDX_COMMAND) {
TreeConnectResponseLength =
FIELD_OFFSET(RESP_TREE_CONNECT_ANDX,Buffer) + TreeConnectByteCount;
Status = SmbResponseStatus;
} else {
TreeConnectResponseLength =
SmbGetUshort(&pTreeConnectAndXResponse->AndXOffset) -
*pBytesConsumed;
}
}
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 = &pExchange->SmbCeContext.pNetRootEntry->NetRoot;
PSMBCE_SERVER psmbServer = &pExchange->SmbCeContext.pServerEntry->Server;
if (TreeConnectResponseLength + *pBytesConsumed <= BytesIndicated) {
*pBytesConsumed += TreeConnectResponseLength;
// Update the NetRoot fields based on the response.
psmbNetRoot->TreeId = pSmbHeader->Tid;
{ struct __Service_Name_Entry *i;
for (i=ServiceNameTable;;i++) {
ServiceStringLength = i->NameLength;
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) {
psmbNetRoot->MaximumReadBufferSize = psmbServer->MaximumDiskFileReadBufferSize;
} else {
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;
fTreeConnectResponse = TRUE;
NetRootState = SMBCEDB_ACTIVE;
} else {
Status = STATUS_MORE_PROCESSING_REQUIRED;
}
} else {
fTreeConnectResponse = TRUE;
NetRootState = SMBCEDB_MARKED_FOR_DELETION;
}
}
}
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)) {
if (TreeConnectResponseLength + *pBytesConsumed <= BytesIndicated) {
pExchange->SmbCeContext.pNetRootEntry->NetRoot.TreeId
= SmbGetUshort(&pTreeConnectResponse->Tid);
pExchange->SmbCeContext.pNetRootEntry->NetRoot.NetRootType = NET_ROOT_WILD;
if (pExchange->SmbCeContext.pServerEntry->Server.MaximumBufferSize == 0){
RxDbgTrace( 0, Dbg, ("SmbCeParseSmbHeader:: setting srvmaxbufsize %ld\n",
SmbGetUshort(&pTreeConnectResponse->MaxBufferSize)));
pExchange->SmbCeContext.pServerEntry->Server.MaximumBufferSize =
SmbGetUshort(&pTreeConnectResponse->MaxBufferSize);
}
*pBytesConsumed += TreeConnectResponseLength;
pSmbBuffer += *pBytesConsumed;
fTreeConnectResponse = TRUE;
NetRootState = SMBCEDB_ACTIVE;
//for CORE, this counts as a successful session setup as well!
pExchange->SmbCeContext.pSessionEntry->Session.UserId = pSmbHeader->Uid;
fSessionSetupResponse = TRUE;
SessionState = SMBCEDB_ACTIVE;
} else {
Status = STATUS_MORE_PROCESSING_REQUIRED;
}
} else {
Status = SmbResponseStatus;
fTreeConnectResponse = TRUE;
NetRootState = SMBCEDB_MARKED_FOR_DELETION;
}
RxDbgTrace( 0, Dbg, ("SmbCeParseSmbHeader::Tree connect Response %lx\n",Status));
}
// Initiate further action if the status of the exchange/conenction engine can be
// updated based on the data available.
if ((pExchange->SmbCeFlags & SMBCE_EXCHANGE_SESSION_CONSTRUCTOR) ||
(pExchange->SmbCeFlags & SMBCE_EXCHANGE_NETROOT_CONSTRUCTOR)) {
if (fSessionSetupResponse) {
SmbCeUpdateSessionEntryState(
pExchange->SmbCeContext.pSessionEntry,
SessionState);
RxDbgTrace( 0, Dbg, ("Dispatching Session Entry Finalization\n"));
}
if (fTreeConnectResponse) {
SmbCeUpdateNetRootEntryState(
pExchange->SmbCeContext.pNetRootEntry,
NetRootState);
RxDbgTrace( 0, Dbg, ("Dispatching Net root Entry Finalization\n"));
}
} else {
ASSERT(pSmbHeader->Uid == pExchange->SmbCeContext.pSessionEntry->Session.UserId);
ASSERT(pSmbHeader->Tid == pExchange->SmbCeContext.pNetRootEntry->NetRoot.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
SmbCeInitializeExchange(
PSMB_EXCHANGE *pExchangePointer,
PMRX_V_NET_ROOT pVNetRoot,
SMB_EXCHANGE_TYPE Type,
PSMB_EXCHANGE_DISPATCH_VECTOR pDispatchVector)
/*++
Routine Description:
This routine initializes the given exchange instance
Arguments:
pExchangePointer - the placeholder for the exchange instance. If it is NULL a new one
is allocated.
pVirtualNetRoot - the virtual net root
pNetRoot - the associated net root
Type - the type of the exchange
pDispatchVector - the dispatch vector associated with this instance.
Return Value:
RXSTATUS - The return status for the operation
--*/
{
NTSTATUS Status;
RxDbgTrace( 0, Dbg, ("SmbCeInitializeExchange: Invoked\n"));
if (*pExchangePointer == NULL) {
// Allocate a new exchange instance.
*pExchangePointer = SmbMmAllocateExchange(Type,NULL);
if (*pExchangePointer == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
}
if ((Status = SmbCeIncrementActiveExchangeCount()) == STATUS_SUCCESS) {
PSMB_EXCHANGE LocalExchangePointer = *pExchangePointer;
LocalExchangePointer->SmbCeContext.pVNetRoot = pVNetRoot;
LocalExchangePointer->SmbCeContext.pServerEntry =
SmbCeReferenceAssociatedServerEntry(pVNetRoot->pNetRoot->pSrvCall);
LocalExchangePointer->SmbCeContext.pSessionEntry =
SmbCeReferenceAssociatedSessionEntry(pVNetRoot);
LocalExchangePointer->SmbCeContext.pNetRootEntry =
SmbCeReferenceAssociatedNetRootEntry(pVNetRoot->pNetRoot);
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);
}
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 associated 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.
--*/
{
pExchange->Type = 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;
RxDbgTrace( 0, Dbg, ("SmbCePrepareExchangeForReuse: Invoked\n"));
pNetRootEntry = pExchange->SmbCeContext.pNetRootEntry;
pSessionEntry = pExchange->SmbCeContext.pSessionEntry;
pServerEntry = pExchange->SmbCeContext.pServerEntry;
if (pServerEntry != NULL) {
// Disassociate the MID associated with the exchange
if (pExchange->SmbCeFlags & SMBCE_EXCHANGE_MID_VALID) {
SmbCeDisassociateMidFromExchange(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);
SmbCeCompleteSessionEntryInitialization(pSessionEntry);
pExchange->SmbCeFlags &= ~SMBCE_EXCHANGE_SESSION_CONSTRUCTOR;
}
if (pExchange->SmbCeFlags & SMBCE_EXCHANGE_NETROOT_CONSTRUCTOR) {
ASSERT(pNetRootEntry != NULL);
RxDbgTrace( 0, Dbg, ("Dispatching Net root Entry Finalization\n"));
// Finalize the construction of the net root entry.
SmbCeReferenceNetRootEntry(pExchange->SmbCeContext.pNetRootEntry);
SmbCeCompleteNetRootEntryInitialization(pNetRootEntry);
pExchange->SmbCeFlags &= ~SMBCE_EXCHANGE_NETROOT_CONSTRUCTOR;
}
SmbCeDereferenceEntries(
pServerEntry,
pSessionEntry,
pNetRootEntry);
} else {
ASSERT((pSessionEntry == NULL) && (pNetRootEntry == NULL));
}
return STATUS_SUCCESS;
}
VOID
SmbCeDiscardExchange(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 worker thread.
--*/
{
PSMB_EXCHANGE pSmbExchange = pExchange;
RxDbgTrace( 0, Dbg, ("SmbCeDiscardExchange: Invoked\n"));
RxLog((">>>Discard %lx",pSmbExchange));
// Destroy the context
if (pSmbExchange->ReferenceCount == 0) {
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)
);
}
}
NTSTATUS
SmbCeIncrementPendingOperations(
PSMB_EXCHANGE pExchange,
ULONG PendingOperationMask)
/*++
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;
SmbCeAcquireSpinLock();
if (!BooleanFlagOn(pExchange->SmbCeFlags,SMBCE_EXCHANGE_FINALIZED)) {
if ((pExchange->SmbCeContext.pServerEntry != NULL) &&
((pExchange->SmbCeContext.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;
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.
--*/
{
NTSTATUS Status;
BOOLEAN fPostFinalize = FALSE;
ASSERT(FlagOn(pExchange->SmbCeFlags,SMBCE_EXCHANGE_FINALIZED));
if (!(pExchange->SmbCeFlags & SMBCE_EXCHANGE_RETAIN_MID)) {
SmbCeDisassociateMidFromExchange(
pExchange->SmbCeContext.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(
MRxIfsDeviceObject,
CriticalWorkQueue,
&pExchange->WorkQueueItem,
SmbCeFinalizeExchangeWorkerThreadRoutine,
pExchange);
}
}
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();
if (!(pExchange->SmbCeFlags & SMBCE_EXCHANGE_FINALIZED)) {
if ((pExchange->ReceivePendingOperations == 0) &&
(pExchange->CopyDataPendingOperations == 0) &&
(pExchange->SendCompletePendingOperations == 0) &&
(pExchange->LocalPendingOperations == 0)) {
fFinalizeExchange = TRUE;
ExchangeStatus = SmbCeExchangeFinalized;
pExchange->SmbCeFlags |= SMBCE_EXCHANGE_FINALIZED;
} else {
ExchangeStatus = SmbCeExchangeNotFinalized;
}
} else {
ExchangeStatus = SmbCeExchangeAlreadyFinalized;
}
SmbCeReleaseSpinLock();
if (fFinalizeExchange) {
SmbCepFinalizeExchange(pExchange);
}
return ExchangeStatus;
}
SMBCE_EXCHANGE_STATUS
SmbCeDecrementPendingOperationsAndFinalize(
PSMB_EXCHANGE pExchange,
ULONG PendingOperationMask)
/*++
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--;
}
if ((pExchange->ReceivePendingOperations == 0) &&
(pExchange->CopyDataPendingOperations == 0) &&
(pExchange->SendCompletePendingOperations == 0) &&
(pExchange->LocalPendingOperations == 0)) {
fFinalizeExchange = TRUE;
ExchangeStatus = SmbCeExchangeFinalized;
pExchange->SmbCeFlags |= SMBCE_EXCHANGE_FINALIZED;
} else {
ExchangeStatus = SmbCeExchangeNotFinalized;
}
SmbCeReleaseSpinLock();
if (fFinalizeExchange) {
SmbCepFinalizeExchange(pExchange);
}
return ExchangeStatus;
}
//
// Default handler implementation of exchange handler functions.
//
NTSTATUS
DefaultSmbExchangeIndError(
IN PSMB_EXCHANGE pExchange) // the SMB exchange instance
{
UNREFERENCED_PARAMETER(pExchange);
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS
DefaultSmbExchangeIndReceive(
IN PSMB_EXCHANGE pExchange) // The exchange instance
{
UNREFERENCED_PARAMETER(pExchange);
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS
DefaultSmbExchangeIndSendCallback(
IN PSMB_EXCHANGE pExchange) // The exchange instance
{
UNREFERENCED_PARAMETER(pExchange);
return STATUS_NOT_IMPLEMENTED;
}