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