/*++ Copyright (c) 1987-1993 Microsoft Corporation Module Name: SmbPse.c Abstract: This module defines the types and functions related to the SMB protocol selection engine: the component that translates minirdr calldowns into SMBs. Author: Joe Linn [JoeLi] -- Implemented Ordinary Exchange Notes: The Ordinary exchange bridges the mismatch between the connection engine exchange which is oriented towards sending a single SMB request to the server and processing the response from the server and the requests recieved from RDBSS. The requests from RDBSS come in one of two flavours -- synchronous and asynchronous. There are requests which often translate into multiple SMB's being sent to the server and the associated response processing. There is no one to one mapping between the requests and the SMBs that need to be sent. In some cases a reconnection attempt needs to be made and in others a delayed open needs to be sent before the associated request can be processed. There are instances of requests which are inherently multi SMB, e.g., large read and write requests. The ordinary exchange provides the framework for dealing with all these variations. The ORDINARY_EXCHANGE wraps a connection engine exchange and extends it with different hooks for customization. The custromization of ORDINARY_EXCHANGE is possible both from the data and control viewpoint. The data portion is provided by a union at the tail end of the ORDINARY_EXCHANGE which provides for the appropriate state to be captured. The code customization consists of three routines that can be specified as part of the ORDIANRY_EXCHANGE. These are the Asynchronous Resumption routine (AsyncResumptionRoutine), the continuation routine (ContinuationRoutine) and the start routine (StartRoutine). The SmbPseCreateOrdinaryExchange, SmbPseSubmitOrdinaryExchange and SmbPseFinalizeOrdinaryExchange provide the necessay mechanism for creating an ordinary exchange, triggering the action and finalizing it upon completion. The ordinary exchange implementation tailors the dispatch vector associated with the underlying connection engine exchange using extensive tables. All the routines suffixed with _default are the default routines for the underlying connection engine exchange. The typical course of exchange in response to a request from the RDBSS is to 1) create an ordinary exchange (SmbPseCreateOrdinaryExchange) 2) submit it for processing (SmbPseSubmitOrdinaryExchange) 2.1) The Ordinary exchange completes the initialization w.r.t the state associated with it and initiates the processing in the connection engine (SmbCeInitiateExchange) 2.2) The connection engine completes the initialization associated with the connection engine and invokes the Start routine provided in the dispatch vector. 2.3) This results in the Start routine provided to the Ordinary exchange being invoked. The request specific initialization is carried out followed by a call to SmbCeTranceive or SmbCeSend. 2.4) The resulting exchange is suspended while the underlying connection engine interfaces with the transport to ship the packet over and receive the response. 2.5) Once the connection engine quiesces the SMbPseContinueOrdinaryExchange is called. This routine either invokes the continuation routine to resume processing or wrap up the ordianry exchange processing and return to the caller. this involves either setting the event for synchronous requests or invoking the AsyncResumption routine for asynchronous requests. The request for read/write which involve multiple packets use the continuation routine to spin up further requests. These can be network exchanges which are wired to the original exchange and are referred to as associated exchanges. On completion of all associated exchanges the connection engine invokes the AssociatedExchangeCompletionHandler which results in the resumption of ORDINARY_EXCHANGE processing in 2.5. --*/ #include "precomp.h" #pragma hdrstop #include #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, __SmbPseDbgCheckOEMdls) #pragma alloc_text(PAGE, SmbPseContinueOrdinaryExchange) #pragma alloc_text(PAGE, SmbPseOrdinaryExchange) #pragma alloc_text(PAGE, __SmbPseCreateOrdinaryExchange) #pragma alloc_text(PAGE, SmbPseFinalizeOrdinaryExchange) #pragma alloc_text(PAGE, SmbPseExchangeStart_default) #pragma alloc_text(PAGE, SmbPseExchangeCopyDataHandler_Read) #pragma alloc_text(PAGE, __SmbPseRMTableEntry) #pragma alloc_text(PAGE, SmbPseInitializeTables) #pragma alloc_text(PAGE, MRxSmbQueryDosVolumeInformation) #endif RXDT_DefineCategory(SMBPSE); #define Dbg (DEBUG_TRACE_SMBPSE) #define MINIMUM_SEND_SIZE 512 PVOID LastOE; #define MIN(x,y) ((x) < (y) ? (x) : (y)) #define IM_THE_LAST_GUY (*Response==0) // // Generic AndX request // GENERIC_ANDX NullGenericAndX = { // typedef struct _GENERIC_ANDX { 0, // UCHAR WordCount; // Count of parameter words // UCHAR AndXCommand; // Secondary (X) command; 0xFF = none SMB_COM_NO_ANDX_COMMAND, 0, // UCHAR AndXReserved; // Reserved 0 // _USHORT( AndXOffset ); // Offset (from SMB header start) // } GENERIC_ANDX; }; NTSTATUS SmbPseExchangeStart_default( IN OUT PSMB_EXCHANGE pExchange); NTSTATUS SmbPseExchangeSendCallbackHandler_default( IN PSMB_EXCHANGE pExchange, IN PMDL pXmitBuffer, IN NTSTATUS SendCompletionStatus); NTSTATUS SmbPseExchangeCopyDataHandler_default( IN PSMB_EXCHANGE pExchange, IN PMDL pDataBuffer, IN ULONG DataSize); NTSTATUS SmbPseExchangeCopyDataHandler_Read( IN PSMB_EXCHANGE pExchange, IN PMDL pDataBuffer, IN ULONG DataSize); NTSTATUS SmbPseExchangeReceive_default( IN struct _SMB_EXCHANGE *pExchange, IN ULONG BytesIndicated, IN ULONG BytesAvailable, OUT ULONG *pBytesTaken, IN PSMB_HEADER pSmbHeader, OUT PMDL *pDataBufferPointer, OUT PULONG pDataSize, IN ULONG ReceiveFlags); NTSTATUS SmbPseExchangeFinalize_default( IN OUT struct _SMB_EXCHANGE *pExchange, OUT BOOLEAN *pPostFinalize); SMB_EXCHANGE_DISPATCH_VECTOR SmbPseOEDispatch = { SmbPseExchangeStart_default, SmbPseExchangeReceive_default, SmbPseExchangeCopyDataHandler_default, SmbPseExchangeSendCallbackHandler_default, SmbPseExchangeFinalize_default, NULL }; #if DBG #define P__ASSERT(exp) { \ if (!(exp)) { \ DbgPrint("NOT %s\n",#exp); \ errors++; \ }} VOID __SmbPseOEAssertConsistentLinkage( PSZ MsgPrefix, PSZ File, unsigned Line, PRX_CONTEXT RxContext, PSMB_PSE_ORDINARY_EXCHANGE OrdinaryExchange, PSMBSTUFFER_BUFFER_STATE StufferState, ULONG Flags ) /*++ Routine Description: This routine performs a variety of checks to ensure that the linkage between the rxcontext, the OE, and the stufferstate is correct and that various fields have correct values. if anything is bad....print stuff out and brkpoint; Arguments: MsgPrefix an identifying msg RxContext duh OrdinaryExchange . StufferState . Return Value: none Notes: --*/ { ULONG errors = 0; PMRXSMB_RX_CONTEXT pMRxSmbContext; PSMB_EXCHANGE Exchange = &OrdinaryExchange->Exchange; pMRxSmbContext = MRxSmbGetMinirdrContext(RxContext); if (Exchange->CancellationStatus != SMBCE_EXCHANGE_CANCELLED) { P__ASSERT( OrdinaryExchange->SerialNumber == RxContext->SerialNumber ); P__ASSERT( NodeType(RxContext) == RDBSS_NTC_RX_CONTEXT ); } P__ASSERT( NodeType(OrdinaryExchange)==SMB_EXCHANGE_NTC(ORDINARY_EXCHANGE) ); P__ASSERT( OrdinaryExchange->RxContext == RxContext ); P__ASSERT( NodeType(StufferState) == SMB_NTC_STUFFERSTATE ); P__ASSERT( Exchange == StufferState->Exchange); P__ASSERT( StufferState->RxContext == RxContext ); if(StufferState->HeaderMdl!=NULL){ P__ASSERT( !RxMdlIsPartial(StufferState->HeaderMdl) ); } if (FlagOn(OrdinaryExchange->Flags,SMBPSE_OE_FLAG_OE_HDR_PARTIAL_INITIALIZED)) { P__ASSERT( RxMdlIsPartial(StufferState->HeaderPartialMdl) ); } if (errors==0) { return; } DbgPrint("%s INCONSISTENT OE STATE: %d errors at %s line %d\n", MsgPrefix,errors,File,Line); //DbgBreakPoint(); return; } VOID __SmbPseDbgRunMdlChain( PMDL MdlChain, ULONG CountToCompare, PSMB_PSE_ORDINARY_EXCHANGE OrdinaryExchange, PSZ MsgPrefix, PSZ File, unsigned Line ) { ULONG i,total; RxDbgTrace(0,Dbg,("__SmbPseRunMdlChain: -------------%08lx\n",MdlChain)); for (total=i=0;MdlChain!=NULL;i++,MdlChain=MdlChain->Next) { total+=MdlChain->ByteCount; RxDbgTrace(0,Dbg,("--->%02d %08lx %08lx %08lx %6d %6d\n",i,MdlChain,MdlChain->MdlFlags, MmGetMdlVirtualAddress(MdlChain),MdlChain->ByteCount,total)); } if (total == CountToCompare) return; DbgPrint("%s: MdlChain.Count!=CountToCompart c1,c2,xch.st=%08lx %08lx %08lx\n", MsgPrefix, total,CountToCompare,OrdinaryExchange->Status, File,Line); //DbgBreakPoint(); } #define SmbPseDbgRunMdlChain(a,b,c,d) {\ __SmbPseDbgRunMdlChain(a,b,c,d,__FILE__,__LINE__);\ } VOID __SmbPseDbgCheckOEMdls( PSMB_PSE_ORDINARY_EXCHANGE OrdinaryExchange, PSZ MsgPrefix, PSZ File, unsigned Line ) { ULONG errors = 0; PSMBSTUFFER_BUFFER_STATE StufferState = &OrdinaryExchange->AssociatedStufferState; PMDL SubmitMdl = StufferState->HeaderPartialMdl; PAGED_CODE(); P__ASSERT (OrdinaryExchange->SaveDataMdlForDebug == SubmitMdl->Next); P__ASSERT (OrdinaryExchange->SaveDataMdlForDebug == StufferState->DataMdl); P__ASSERT (SubmitMdl != NULL); if (errors==0) { return; } DbgPrint("%s CheckOEMdls failed: %d errors at %s line %d: OE=%08lx\n", MsgPrefix,errors,File,Line,OrdinaryExchange); //DbgBreakPoint(); return; } #define SmbPseDbgCheckOEMdls(a,b) {\ __SmbPseDbgCheckOEMdls(a,b,__FILE__,__LINE__);\ } ULONG SmbPseShortStatus(ULONG Status) { ULONG ShortStatus; ShortStatus = Status & 0xc0003fff; ShortStatus = ShortStatus | (ShortStatus >>16); return(ShortStatus); } VOID SmbPseUpdateOEHistory( PSMB_PSE_ORDINARY_EXCHANGE OrdinaryExchange, ULONG Tag1, ULONG Tag2 ) { ULONG MyIndex,Long0,Long1; MyIndex = InterlockedIncrement(&OrdinaryExchange->History.Next); MyIndex = (MyIndex-1) & (SMBPSE_OE_HISTORY_SIZE-1); Long0 = (Tag1<<16) | (Tag2 & 0xffff); Long1 = (SmbPseShortStatus(OrdinaryExchange->SmbStatus)<<16) | OrdinaryExchange->Flags; OrdinaryExchange->History.Markers[MyIndex].Longs[0] = Long0; OrdinaryExchange->History.Markers[MyIndex].Longs[1] = Long1; } VOID SmbPseVerifyDataPartialAllocationPerFlags( PSMB_PSE_ORDINARY_EXCHANGE OrdinaryExchange ) { BOOLEAN FlagsSayPartialAllocated,TheresADataPartial; ULONG t = OrdinaryExchange->Flags & (SMBPSE_OE_FLAG_OE_ALLOCATED_DATA_PARTIAL|SMBPSE_OE_FLAG_MUST_SUCCEED_ALLOCATED_SMBBUF); FlagsSayPartialAllocated = (t!=0)?TRUE:FALSE; //the compiler is getting confused TheresADataPartial = (OrdinaryExchange->DataPartialMdl != NULL)?TRUE:FALSE; //the compiler is getting confused if ( FlagsSayPartialAllocated != TheresADataPartial){ DbgPrint("Flags %08lx datapartial %08lx t %08lx fspa %08lx tadp %08lx\n", OrdinaryExchange->Flags, OrdinaryExchange->DataPartialMdl, t, FlagsSayPartialAllocated, TheresADataPartial); ASSERT ( FlagsSayPartialAllocated == TheresADataPartial); } } #else #define SmbPseDbgRunMdlChain(a,b,c,d) {NOTHING;} #define SmbPseDbgCheckOEMdls(a,b) {NOTHING;} #define SmbPseVerifyDataPartialAllocationPerFlags(a) {NOTHING;} #endif #define UPDATE_OE_HISTORY_WITH_STATUS(a) \ UPDATE_OE_HISTORY_2SHORTS(a,SmbPseShortStatus(OrdinaryExchange->Status)) VOID MRxSmbResumeAsyncReadWriteRequests( PRX_CONTEXT RxContext) /*++ Routine Description: Asynchronous read write requests can be deferred because of SMB FCB resource acquistion. In all such cases this routine resumes the request. We cannot directly reume execution with MRxSmbRead/MRxSmbWrite routine because we need to invoke LowIoCompletion in certain failure cases. We have two choices to do so .... either we can include this logic in the MRxSmbRead/MRxSmbWrite routine or consolidate it in pne place. This routine implements the later approach. Arguments: RxContext - the RDBSS context Notes: --*/ { NTSTATUS Status; PMRX_CALLDOWN ResumptionRoutine; switch (RxContext->MajorFunction) { case IRP_MJ_READ: ResumptionRoutine = MRxSmbRead; break; case IRP_MJ_WRITE: ResumptionRoutine = MRxSmbWrite; break; default: ASSERT(!"Valid IRP Major Function code for CscResumeReadWrite"); return; } Status = (ResumptionRoutine)(RxContext); if (Status != STATUS_PENDING) { if (Status != STATUS_SUCCESS) { DbgPrint("RxContext Async CSC Status %lx\n",Status); RxContext->StoredStatus = Status; RxContext->InformationToReturn = 0; } // Invoke the Low Io Resumption routine RxLowIoCompletion(RxContext); } } NTSTATUS SmbPseHoldOrdinaryExchange( IN OUT PSMB_PSE_ORDINARY_EXCHANGE OrdinaryExchange ) /*++ Routine Description: This routine holds an exchange until the session recovery completes. Session recovery is initiated when a kerberos ticket expires on the server and we get a STATUS_NETWORK_SESSION_EXPIRED error from the server. The session needs to be rebuilt before operations can resume. Note that this is a special case because the server maintains state across a session setup. Session recovery is indicated by the pSessionEntry->SessionRecoverInProgress flag. Once session recovery is complete, we call SmbPseContinueOrdinaryExchange(). Note that if we are unable to hold the exchange, we directly call SmbPseContinueOrdinaryExchange(). Arguments: OrdinaryExchange - the context of the operation. . Return Value: STATUS_PENDING if the exchange was 'held' successfully. Notes: IRQL < DISPATCH_DEVEL --*/ { NTSTATUS Status; ASSERT( OrdinaryExchange->AsyncResumptionRoutine != NULL ); Status = SmbCeHoldExchangeForSessionRecovery( (PSMB_EXCHANGE)OrdinaryExchange, (PSMBCE_RELEASE_ROUTINE) OrdinaryExchange->AsyncResumptionRoutine ); // // If we didnt hold, then call the continuation routine directly. // if( Status != STATUS_PENDING ) { Status = OrdinaryExchange->AsyncResumptionRoutine( OrdinaryExchange, OrdinaryExchange->RxContext ); } return Status; } NTSTATUS SmbPseContinueOrdinaryExchange( IN OUT PSMB_PSE_ORDINARY_EXCHANGE OrdinaryExchange ) /*++ Routine Description: This routine resumes processing on an exchange. This is called when work is required to finish processing a request that cannot be completed at DPC level. This happens either because the parse routine needs access to structures that are not locks OR because the operation if asynchronous and there maybe more work to be done. The two cases are regularized by delaying the parse if we know that we're going to post: this is indicated by the presense of a resume routine. Arguments: RxContext - the context of the operation. . Return Value: NTSTATUS - The return status for the operation Notes: --*/ { NTSTATUS Status; PRX_CONTEXT RxContext = OrdinaryExchange->RxContext; RxCaptureFobx; PSMB_EXCHANGE Exchange = (PSMB_EXCHANGE) OrdinaryExchange; PSMBSTUFFER_BUFFER_STATE StufferState = &OrdinaryExchange->AssociatedStufferState; PMDL SubmitMdl, HeaderFullMdl; BOOLEAN InvokeContinuationRoutine = FALSE; PAGED_CODE(); RxDbgTrace(+1, Dbg, ("SmbPseContinueOrdinaryExchange entering........OE=%08lx\n",OrdinaryExchange)); Status = Exchange->Status; if (OrdinaryExchange->OpSpecificState != SmbPseOEInnerIoStates_OperationCompleted) { ClearFlag(OrdinaryExchange->Flags,SMBPSE_OE_FLAG_OE_AWAITING_DISPATCH); SmbPseOEAssertConsistentLinkageFromOE("SmbPseContinueOrdinaryExchange:"); UPDATE_OE_HISTORY_WITH_STATUS('0c'); SubmitMdl = StufferState->HeaderPartialMdl; HeaderFullMdl = StufferState->HeaderMdl; ASSERT(FlagOn(OrdinaryExchange->Flags,SMBPSE_OE_FLAG_OE_HDR_PARTIAL_INITIALIZED)); SmbPseOEAssertConsistentLinkage("Top of OE continue: "); RxUnprotectMdlFromFree(SubmitMdl); RxUnprotectMdlFromFree(HeaderFullMdl); SmbPseDbgCheckOEMdls( OrdinaryExchange,"SmbPseContinueOrdinaryExchange(top)"); SmbPseDbgRunMdlChain( SubmitMdl, OrdinaryExchange->SaveLengthForDebug, OrdinaryExchange, "SmbPseContinueOrdinaryExchange(top)"); MmPrepareMdlForReuse(SubmitMdl); ClearFlag(OrdinaryExchange->Flags,SMBPSE_OE_FLAG_OE_HDR_PARTIAL_INITIALIZED); SmbPseVerifyDataPartialAllocationPerFlags(OrdinaryExchange); if ( OrdinaryExchange->DataPartialMdl ) { MmPrepareMdlForReuse( OrdinaryExchange->DataPartialMdl ); } RxDbgTrace( 0, Dbg, (" --> P4Reuse %08lx, full %08lx is no longer unlocked here\n" ,SubmitMdl,HeaderFullMdl)); } if (OrdinaryExchange->ContinuationRoutine == NULL) { if (Status == STATUS_MORE_PROCESSING_REQUIRED) { ULONG BytesTaken; ULONG DataSize = 0; ULONG MessageLength = OrdinaryExchange->MessageLength; PMDL DataBufferPointer = NULL; PSMB_HEADER SmbHeader = (PSMB_HEADER)StufferState->BufferBase; Status = SMB_EXCHANGE_DISPATCH( Exchange, Receive, ( Exchange, // IN struct SMB_EXCHANGE *pExchange, MessageLength, // IN ULONG BytesIndicated, MessageLength, // IN ULONG BytesAvailable, &BytesTaken, // OUT ULONG *pBytesTaken, SmbHeader, // IN PSMB_HEADER pSmbHeader, &DataBufferPointer, // OUT PMDL *pDataBufferPointer, &DataSize, // OUT PULONG pDataSize) TDI_RECEIVE_ENTIRE_MESSAGE )); if (Status == STATUS_SUCCESS) { Status = Exchange->Status; UPDATE_OE_HISTORY_WITH_STATUS('2c'); } else { UPDATE_OE_HISTORY_WITH_STATUS('dd'); } if (DataSize != 0 || DataBufferPointer != NULL || BytesTaken != MessageLength || Status == STATUS_MORE_PROCESSING_REQUIRED) { Status = STATUS_INVALID_NETWORK_RESPONSE; } InvokeContinuationRoutine = TRUE; } } else { InvokeContinuationRoutine = TRUE; } if (InvokeContinuationRoutine) { if ( OrdinaryExchange->ContinuationRoutine != NULL ) { if ( Status == STATUS_MORE_PROCESSING_REQUIRED){ Exchange->Status = STATUS_SUCCESS; } Status = OrdinaryExchange->ContinuationRoutine( OrdinaryExchange ); UPDATE_OE_HISTORY_WITH_STATUS('1c'); if (Status != STATUS_PENDING) { Exchange->Status = Status; OrdinaryExchange->ContinuationRoutine = NULL; } } } if (Status != STATUS_PENDING) { if (Status != STATUS_SUCCESS) { if (RxContext->MajorFunction != IRP_MJ_CLOSE) { // There is no point in transitioning CLOSE operations since // the context is lost anycase. Status = CscTransitionVNetRootForDisconnectedOperation( RxContext, SmbCeGetExchangeVNetRoot( (PSMB_EXCHANGE)OrdinaryExchange), Status); } OrdinaryExchange->Status = OrdinaryExchange->SmbStatus = Status; } if (OrdinaryExchange->AsyncResumptionRoutine ) { // // To prevent critical work queue threads from getting stuck trying to re-initiate exchanges // which returned with STATUS_RETRY, we 'hold' these on a session recovery list. When session // setup completes, these will be retried. // if( OrdinaryExchange->Status == STATUS_RETRY ) { Status = SmbPseHoldOrdinaryExchange(OrdinaryExchange); } else { //call the continuation is it's async Status = OrdinaryExchange->AsyncResumptionRoutine( OrdinaryExchange, RxContext ); } UPDATE_OE_HISTORY_WITH_STATUS('3c'); } //remove my references, if i'm the last guy then do the putaway... UPDATE_OE_HISTORY_WITH_STATUS('4c'); SmbPseFinalizeOrdinaryExchange(OrdinaryExchange); } RxDbgTrace(-1, Dbg, ("SmbPseContinueOrdinaryExchange returning %08lx.\n", Status)); return(Status); } // SmbPseContinueOrdinaryExchange NTSTATUS SmbPseOrdinaryExchange( SMBPSE_ORDINARY_EXCHANGE_ARGUMENT_SIGNATURE, IN SMB_PSE_ORDINARY_EXCHANGE_TYPE OEType ) /*++ Routine Description: This routine implements an ordinary exchange as viewed by the protocol selection routines. Arguments: OrdinaryExchange - the exchange to be conducted. OEType - Ordinary Exchange Type Return Value: NTSTATUS - The return status for the operation Notes: --*/ { NTSTATUS Status; RxCaptureFobx; RxCaptureFcb; PMRXSMB_RX_CONTEXT pMRxSmbContext = MRxSmbGetMinirdrContext(RxContext); PSMB_EXCHANGE Exchange = (PSMB_EXCHANGE) OrdinaryExchange; PSMBSTUFFER_BUFFER_STATE StufferState; PSMB_PSE_OE_START_ROUTINE Continuation; ULONG SmbLength; PMDL SubmitMdl,HeaderFullMdl; ULONG SendOptions; DEBUG_ONLY_DECL( ULONG LengthP; ULONG LengthF; ) KEVENT SyncEvent; PAGED_CODE(); RxDbgTrace(+1, Dbg, ("SmbPseOrdinaryExchange entering.......OE=%08lx\n",OrdinaryExchange)); SmbPseOEAssertConsistentLinkageFromOE("SmbPseOrdinaryExchange:"); OrdinaryExchange->OEType = OEType; StufferState = &OrdinaryExchange->AssociatedStufferState; KeInitializeEvent( &SyncEvent, NotificationEvent, FALSE ); HeaderFullMdl = StufferState->HeaderMdl; ASSERT( HeaderFullMdl != NULL ); SmbLength = (ULONG)(StufferState->CurrentPosition - StufferState->BufferBase); SubmitMdl = StufferState->HeaderPartialMdl; ASSERT(RxMdlIsOwned(SubmitMdl)); RxBuildPartialHeaderMdl( StufferState->HeaderMdl, SubmitMdl, StufferState->BufferBase, SmbLength ); SetFlag(OrdinaryExchange->Flags,SMBPSE_OE_FLAG_OE_HDR_PARTIAL_INITIALIZED); // // If there is a data MDL associated with this request, then // we'll have to chain it. // SubmitMdl->Next = StufferState->DataMdl; if (StufferState->DataMdl) { SmbLength += StufferState->DataSize; } DbgDoit( SmbPseDbgRunMdlChain( SubmitMdl, SmbLength, OrdinaryExchange, "SmbPseOrdinaryExchange(before)"); OrdinaryExchange->SaveDataMdlForDebug = SubmitMdl->Next; OrdinaryExchange->SaveLengthForDebug = SmbLength; if (OrdinaryExchange->RxContextCapturedRequestPacket != NULL) { OrdinaryExchange->SaveIrpMdlForDebug = OrdinaryExchange->RxContextCapturedRequestPacket->MdlAddress; } ) RxDbgTrace( 0, Dbg, (" --> mdllength/smblength %08lx/%08lx headermdl %08lx\n", MmGetMdlByteCount(SubmitMdl), SmbLength, StufferState->HeaderMdl) ); ClearFlag( OrdinaryExchange->Flags, (SMBPSE_OE_FLAG_HEADER_ALREADY_PARSED | SMBPSE_OE_FLAG_OE_ALREADY_RESUMED) ); SendOptions = OrdinaryExchange->SendOptions; SmbCeReferenceExchange( Exchange ); //this one is taken away in ContinueOE SmbCeReferenceExchange( Exchange ); //this one is taken away below... //i must NOT finalize before SmbCe returns SmbCeResetExchange(Exchange); Continuation = OrdinaryExchange->AsyncResumptionRoutine; if (((OrdinaryExchange->OEType == SMBPSE_OETYPE_WRITE) || (OrdinaryExchange->OEType == SMBPSE_OETYPE_READ) || (OrdinaryExchange->OEType == SMBPSE_OETYPE_LOCKS)) && BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION) ) { ASSERT(Continuation!=NULL); } if (Continuation == NULL) { OrdinaryExchange->pSmbCeSynchronizationEvent = &SyncEvent; } DbgDoit((LengthP = MmGetMdlByteCount(SubmitMdl),LengthF = MmGetMdlByteCount(HeaderFullMdl))); RxProtectMdlFromFree(SubmitMdl); RxProtectMdlFromFree(HeaderFullMdl); SmbPseOEAssertConsistentLinkage("just before transceive: "); UPDATE_OE_HISTORY_2SHORTS('eo',(Continuation!=NULL)?'!!':0); DbgDoit( InterlockedIncrement(&OrdinaryExchange->History.Submits); ) if (FlagOn(OrdinaryExchange->Flags,SMBPSE_OE_FLAG_VALIDATE_FID)) { PMRX_SRV_OPEN SrvOpen = capFobx->pSrvOpen; PMRX_SMB_SRV_OPEN smbSrvOpen = MRxSmbGetSrvOpenExtension(SrvOpen); PSMBCEDB_SERVER_ENTRY pServerEntry = SmbCeGetExchangeServerEntry(OrdinaryExchange); if (smbSrvOpen->Version == pServerEntry->Server.Version) { Status = STATUS_SUCCESS; } else { Exchange->Status = Exchange->SmbStatus = Status = STATUS_CONNECTION_DISCONNECTED; capFcb->fShouldBeOrphaned = TRUE; } IF_DEBUG { PSMB_HEADER pSmbHeader = (PSMB_HEADER)MmGetSystemAddressForMdlSafe(SubmitMdl,LowPagePriority); USHORT Flags2 = 0; if (pSmbHeader) { Flags2 = SmbGetUshort(&pSmbHeader->Flags2); } RxDbgTrace(0, Dbg, ("Flags2 Value for Exchange %lx is %lx\n",Exchange,Flags2)); } } else { Status = STATUS_SUCCESS; } if (Status == STATUS_SUCCESS) { if (FlagOn(OrdinaryExchange->Flags,SMBPSE_OE_FLAG_NO_RESPONSE_EXPECTED)) { Status = SmbCeSend( Exchange, SendOptions, SubmitMdl, SmbLength); } else { PSMBCEDB_SERVER_ENTRY pServerEntry = SmbCeGetExchangeServerEntry(OrdinaryExchange); Status = SmbCeTranceive( Exchange, SendOptions, SubmitMdl, SmbLength); } } SmbPseFinalizeOrdinaryExchange(OrdinaryExchange); //okay to finalize now that we're back if ( Status == STATUS_PENDING) { if ( Continuation != NULL ) { goto FINALLY; } UPDATE_OE_HISTORY_WITH_STATUS('1o'); KeWaitForSingleObject(&SyncEvent,Executive, KernelMode, FALSE, NULL ); OrdinaryExchange->pSmbCeSynchronizationEvent = NULL; ASSERT(RxMdlIsOwned(SubmitMdl)); DbgDoit ( //variables in the assert are only declared for DBG //asserts can be enabled separately ASSERT( LengthP == MmGetMdlByteCount(SubmitMdl) && LengthF == MmGetMdlByteCount(HeaderFullMdl) ); ) } else { RxDbgTrace (0, Dbg, (" -->Status after transceive %08lx\n",Status)); DbgDoit ( //variables in the assert are only declared for DBG //asserts can be enabled separately ASSERT( LengthP == MmGetMdlByteCount(SubmitMdl) && LengthF == MmGetMdlByteCount(HeaderFullMdl) ); ) RxUnprotectMdlFromFree(SubmitMdl); RxUnprotectMdlFromFree(HeaderFullMdl); SmbPseOEAssertConsistentLinkage("nonpending return from transceive: "); // if it's an error, remove the references that i placed and get out if (NT_ERROR(Status)) { SmbPseFinalizeOrdinaryExchange(OrdinaryExchange); goto FINALLY; } } //at last, call the continuation........ SmbPseOEAssertConsistentLinkage("just before continueOE: "); UPDATE_OE_HISTORY_WITH_STATUS('9b'); Status = SmbPseContinueOrdinaryExchange( OrdinaryExchange ); UPDATE_OE_HISTORY_WITH_STATUS('9o'); FINALLY: RxDbgTrace(-1, Dbg, ("SmbPseOrdinaryExchange returning %08lx.\n", Status)); return(Status); } // SmbPseOrdinaryExchange NTSTATUS __SmbPseCreateOrdinaryExchange ( IN PRX_CONTEXT RxContext, IN PMRX_V_NET_ROOT VNetRoot, IN SMB_PSE_ORDINARY_EXCHANGE_ENTRYPOINTS EntryPoint, IN PSMB_PSE_OE_START_ROUTINE StartRoutine, IN OUT SMBFCB_HOLDING_STATE *SmbFcbHoldingState OPTIONAL, OUT PSMB_PSE_ORDINARY_EXCHANGE *OrdinaryExchangePtr ) /*++ Routine Description: This routine allocates and initializes an SMB header buffer. Currently, we just allocate them from pool except when must_succeed is specified. Arguments: RxContext - the RDBSS context VNetRoot - DispatchVector - Return Value: A buffer ready to go, OR NULL. Notes: --*/ { PMRXSMB_RX_CONTEXT pMRxSmbContext = MRxSmbGetMinirdrContext(RxContext); PSMBSTUFFER_BUFFER_STATE StufferState = NULL; PSMB_PSE_ORDINARY_EXCHANGE OrdinaryExchange = NULL; PCHAR SmbBuffer = NULL; PMDL HeaderFullMdl = NULL; NTSTATUS Status = STATUS_SUCCESS; RxCaptureFobx; SMBFCB_HOLDING_STATE TempHoldingState = SmbFcb_NotHeld; PAGED_CODE(); /* Other size improvement stuff: //CODE.IMPROVEMENT finalize renamed to transitiontoquiescent big fix - remove unwanted capture macros longname - delete top part of routine by using the studcode smbutils - we could reduce the table size by a factor of 2-3 by storing s 16bit representation of the ntstatus */ RxDbgTrace(+1, Dbg, ("SmbPseCreateOrdinaryExchange\n") ); if (SmbFcbHoldingState == NULL) { SmbFcbHoldingState = &TempHoldingState; } IF_NOT_MRXSMB_CSC_ENABLED{ ASSERT(*SmbFcbHoldingState == SmbFcb_NotHeld); } OrdinaryExchange = (PSMB_PSE_ORDINARY_EXCHANGE)SmbMmAllocateExchange(ORDINARY_EXCHANGE,NULL); //we rely on the fact that SmbMmAllocate Zeros the exchange............. if (OrdinaryExchange == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto UNWIND; } StufferState = &OrdinaryExchange->AssociatedStufferState; StufferState->NodeTypeCode = SMB_NTC_STUFFERSTATE; StufferState->NodeByteSize = sizeof(SMBSTUFFER_BUFFER_STATE); StufferState->Exchange = &OrdinaryExchange->Exchange; DbgDoit(OrdinaryExchange->SerialNumber = RxContext->SerialNumber); //CODE.IMPROVEMENT should this be in the SMB_EXCHANGE? // // Initialize the exchange packet // Status = SmbCeInitializeExchange( &StufferState->Exchange, RxContext, (PMRX_V_NET_ROOT)VNetRoot, ORDINARY_EXCHANGE, &SmbPseOEDispatch); if (StufferState->Exchange != NULL) { SmbCeReferenceExchange(StufferState->Exchange); RxDbgTrace(0, Dbg, (" exchng=%08lx,type=%08lx\n",&StufferState->Exchange,StufferState->Exchange->Type)); } StufferState->RxContext = RxContext; //place a reference on the rxcontext until we are finished InterlockedIncrement( &RxContext->ReferenceCount ); OrdinaryExchange->StufferStateDbgPtr = StufferState; OrdinaryExchange->RxContext = RxContext; OrdinaryExchange->EntryPoint = EntryPoint; OrdinaryExchange->StartRoutine = StartRoutine; OrdinaryExchange->SmbBufSize = MAXIMUM_SMB_BUFFER_SIZE; DbgDoit(OrdinaryExchange->RxContextCapturedRequestPacket = RxContext->CurrentIrp;); //note: create path must turn this flag on. OrdinaryExchange->SmbCeFlags &= ~(SMBCE_EXCHANGE_ATTEMPT_RECONNECTS); ASSERT( (FlagOn(OrdinaryExchange->Flags,SMBPSE_OE_FLAG_MUST_SUCCEED_ALLOCATED_OE)) || (OrdinaryExchange->Flags == 0) ); ASSERT( OrdinaryExchange->SendOptions == 0 ); ASSERT( OrdinaryExchange->DataPartialMdl == NULL ); pMRxSmbContext->pExchange = &OrdinaryExchange->Exchange; pMRxSmbContext->pStufferState = StufferState; if (capFobx != NULL) { if (BooleanFlagOn(capFobx->Flags,FOBX_FLAG_DFS_OPEN)) { SetFlag(OrdinaryExchange->Flags,SMBPSE_OE_FLAG_TURNON_DFS_FLAG); } } else if (BooleanFlagOn(VNetRoot->pNetRoot->Flags,NETROOT_FLAG_DFS_AWARE_NETROOT) && (RxContext->Create.NtCreateParameters.DfsContext == UIntToPtr(DFS_OPEN_CONTEXT))) { SetFlag(OrdinaryExchange->Flags,SMBPSE_OE_FLAG_TURNON_DFS_FLAG); } if (Status != STATUS_SUCCESS) { goto UNWIND; } // // Allocate the SmbBuffer // if (SmbBuffer == NULL) { SmbBuffer = (PCHAR)RxAllocatePoolWithTag( PagedPool, OrdinaryExchange->SmbBufSize + TRANSPORT_HEADER_SIZE, 'BMSx' ); } if ( SmbBuffer == NULL ) { Status = STATUS_INSUFFICIENT_RESOURCES; goto UNWIND; } RxDbgTrace(0, Dbg, (" smbbuf=%08lx,stfstate=%08lx\n",SmbBuffer,StufferState)); StufferState->ActualBufferBase = SmbBuffer; (PBYTE) SmbBuffer += TRANSPORT_HEADER_SIZE; StufferState->BufferBase = SmbBuffer; StufferState->BufferLimit = SmbBuffer + OrdinaryExchange->SmbBufSize; // // Init the HeaderMdl // HeaderFullMdl = StufferState->HeaderMdl = &OrdinaryExchange->HeaderMdl.Mdl; RxInitializeHeaderMdl(HeaderFullMdl,SmbBuffer, OrdinaryExchange->SmbBufSize); RxDbgTrace( 0, Dbg, (" --> smbbufsize %08lx, mdllength %08lx\n", OrdinaryExchange->SmbBufSize, MmGetMdlByteCount(HeaderFullMdl))); //finally, lock down the smbbuf taking different paths according to whether // we are must-succeed or not ASSERT( !RxMdlIsLocked(HeaderFullMdl) ); ASSERT( HeaderFullMdl->Next == NULL ); RxDbgTrace( 0, Dbg, (" --> LOCKING %08lx\n",HeaderFullMdl)); RxProbeAndLockHeaderPages( HeaderFullMdl, KernelMode, IoModifyAccess, Status ); if (Status != STATUS_SUCCESS) { RxDbgTrace( 0, Dbg, (" --> LOCKING FAILED\n")); goto UNWIND; } SetFlag(OrdinaryExchange->Flags,SMBPSE_OE_FLAG_OE_HDR_LOCKED); if (MmGetSystemAddressForMdlSafe(HeaderFullMdl,LowPagePriority) == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto UNWIND; } // // No initialization is required for the partial...just set the pointer StufferState->HeaderPartialMdl = &OrdinaryExchange->HeaderPartialMdl.Mdl; RxDbgTrace( -1, Dbg, (" --> exiting w!\n") ); *OrdinaryExchangePtr = OrdinaryExchange; return Status; UNWIND: RxDbgTrace( -1, Dbg, (" --> exiting w/o!\n") ); if (OrdinaryExchange != NULL ) { SmbPseFinalizeOrdinaryExchange( OrdinaryExchange ); } *OrdinaryExchangePtr = NULL; return Status; } // SmbPseCreateOrdinaryExchange #if DBG ULONG MRxSmbFinalizeStfStateTraceLevel = 1200; #define FINALIZESS_LEVEL MRxSmbFinalizeStfStateTraceLevel #define FINALIZE_TRACKING_SETUP() \ struct { \ ULONG marker1; \ ULONG finalstate; \ ULONG marker2; \ } Tracking = {'ereh',0,'ereh'}; #define FINALIZE_TRACKING(x) {\ Tracking.finalstate |= x; \ } #define FINALIZE_TRACE(x) SmbPseFinalizeOETrace(x,Tracking.finalstate) VOID SmbPseFinalizeOETrace(PSZ text,ULONG finalstate) { PAGED_CODE(); RxDbgTraceLV(0, Dbg, FINALIZESS_LEVEL, ("MRxSmbFinalizeSmbStufferState --> %s(%08lx)\n",text,finalstate)); } #else #define FINALIZE_TRACKING_SETUP() #define FINALIZE_TRACKING(x) #define FINALIZE_TRACE(x) #endif BOOLEAN SmbPseFinalizeOrdinaryExchange ( IN OUT PSMB_PSE_ORDINARY_EXCHANGE OrdinaryExchange ) /*++ Routine Description: This finalizes an OE. Arguments: OrdinaryExchange - pointer to the OE to be dismantled. Return Value: TRUE if finalization occurs otherwise FALSE. Notes: --*/ { PMRXSMB_RX_CONTEXT pMRxSmbContext; PSMBSTUFFER_BUFFER_STATE StufferState; LONG result; ULONG OrdinaryExchangeFlags = OrdinaryExchange->Flags; FINALIZE_TRACKING_SETUP() PAGED_CODE(); SmbPseOEAssertConsistentLinkageFromOEwithFlags( "SmbPseFinalizeOrdinaryExchange:", OECHKLINKAGE_FLAG_NO_REQPCKT_CHECK); StufferState = &OrdinaryExchange->AssociatedStufferState; pMRxSmbContext = MRxSmbGetMinirdrContext(StufferState->RxContext); RxDbgTraceLV(+1, Dbg, 1000, ("MRxSmbFinalizeSmbStufferState\n")); result = SmbCeDereferenceExchange(&OrdinaryExchange->Exchange); if ( result != 0 ) { RxDbgTraceLV( -1, Dbg, 1000, ("MRxSmbFinalizeSmbStufferState -- returning w/o finalizing (%d)\n", result)); return FALSE; } #if 0 RxLog((">>>OE %lx %lx %lx %lx %lx", OrdinaryExchange, OrdinaryExchange->DataPartialMdl, StufferState->HeaderPartialMdl, StufferState->HeaderMdl, OrdinaryExchange->Flags )); #endif FINALIZE_TRACKING( 0x10000000 ); FINALIZE_TRACE("ready to freedatapartial"); if (OrdinaryExchange->CopyDataPendingOperations != 0 || OrdinaryExchange->SendCompletePendingOperations != 0) { DbgBreakPoint(); } SmbPseVerifyDataPartialAllocationPerFlags(OrdinaryExchange); if ( OrdinaryExchange->DataPartialMdl ) { if (!FlagOn(OrdinaryExchangeFlags, SMBPSE_OE_FLAG_MUST_SUCCEED_ALLOCATED_SMBBUF)) { IoFreeMdl( OrdinaryExchange->DataPartialMdl ); FINALIZE_TRACKING( 0x8000000 ); } } if (FlagOn(OrdinaryExchange->Flags,SMBPSE_OE_FLAG_OE_HDR_LOCKED)) { RxUnlockHeaderPages(StufferState->HeaderMdl); ClearFlag(OrdinaryExchange->Flags,SMBPSE_OE_FLAG_OE_HDR_LOCKED); MmPrepareMdlForReuse( StufferState->HeaderMdl ); FINALIZE_TRACKING( 0x4000000 ); } FINALIZE_TRACE("ready to uninit hdr partial"); if (FlagOn(OrdinaryExchange->Flags,SMBPSE_OE_FLAG_OE_HDR_PARTIAL_INITIALIZED)) { MmPrepareMdlForReuse( StufferState->HeaderPartialMdl ); //no harm in calling this multiple times FINALIZE_TRACKING( 0x300000 ); } else { FINALIZE_TRACKING( 0xf00000 ); } if (!FlagOn(OrdinaryExchangeFlags, SMBPSE_OE_FLAG_MUST_SUCCEED_ALLOCATED_SMBBUF)) { FINALIZE_TRACE("ready to freepool actualbuffer"); if ( StufferState->ActualBufferBase != NULL ) { RxFreePool( StufferState->ActualBufferBase ); FINALIZE_TRACKING( 0x5000 ); } else { FINALIZE_TRACKING( 0xf000 ); } } if ( StufferState->RxContext != NULL ) { //get rid of the reference on the RxContext....if i'm the last guy this will finalize RxDereferenceAndDeleteRxContext( StufferState->RxContext );//CODE.IMPROVEMENT Capture rxcontext earlier FINALIZE_TRACKING( 0x600 ); } else { FINALIZE_TRACKING( 0xf00 ); } FINALIZE_TRACE("ready to discard exchange"); SmbCeDiscardExchange(OrdinaryExchange); FINALIZE_TRACKING( 0x2000000 ); FINALIZE_TRACKING( 0x8 ); RxDbgTraceLV(-1, Dbg, 1000, ("MRxSmbFinalizeSmbStufferState --> exit finalstate=%x\n",Tracking.finalstate)); return(TRUE); } // MRxSmbFinalizeSmbStufferState NTSTATUS SmbPseExchangeFinalize_default( IN OUT PSMB_EXCHANGE pExchange, OUT BOOLEAN *pPostFinalize ) /*++ Routine Description: Arguments: pExchange - the exchange instance Return Value: RXSTATUS - The return status for the operation --*/ { PSMB_PSE_ORDINARY_EXCHANGE OrdinaryExchange = (PSMB_PSE_ORDINARY_EXCHANGE)pExchange; PRX_CONTEXT RxContext = OrdinaryExchange->RxContext; UPDATE_OE_HISTORY_WITH_STATUS('ff'); SmbPseOEAssertConsistentLinkageFromOE("SmbPseExchangeFinalize_default: "); if (OrdinaryExchange->SmbStatus != STATUS_SUCCESS) { OrdinaryExchange->Status = OrdinaryExchange->SmbStatus; } if (OrdinaryExchange->AsyncResumptionRoutine != NULL) { NTSTATUS PostStatus; RxDbgTraceLV(0, Dbg, 1000, ("Resume with post-to-async\n")); SetFlag(OrdinaryExchange->Flags,SMBPSE_OE_FLAG_OE_AWAITING_DISPATCH); IF_DEBUG { //fill the workqueue structure with deadbeef....all the better to diagnose //a failed post ULONG i; for (i=0;i+sizeof(ULONG)-1WorkQueueItem);i+=sizeof(ULONG)) { //*((PULONG)(((PBYTE)&OrdinaryExchange->WorkQueueItem)+i)) = 0xdeadbeef; PBYTE BytePtr = ((PBYTE)&OrdinaryExchange->WorkQueueItem)+i; PULONG UlongPtr = (PULONG)BytePtr; *UlongPtr = 0xdeadbeef; } } PostStatus = RxPostToWorkerThread( MRxSmbDeviceObject, CriticalWorkQueue, &OrdinaryExchange->WorkQueueItem, SmbPseContinueOrdinaryExchange, OrdinaryExchange); ASSERT(PostStatus == STATUS_SUCCESS); } else { RxDbgTraceLV(0, Dbg, 1000, ("sync resume\n")); KeSetEvent(OrdinaryExchange->pSmbCeSynchronizationEvent, 0, FALSE); } *pPostFinalize = FALSE; return STATUS_SUCCESS; } NTSTATUS SmbPseExchangeSendCallbackHandler_default( IN PSMB_EXCHANGE pExchange, IN PMDL pXmitBuffer, IN NTSTATUS SendCompletionStatus ) /*++ Routine Description: This is the send call back indication handling routine for ordinary exchanges. Arguments: pExchange - the exchange instance pXmitBuffer - pointer to the transmit buffer MDL BytesSent - number of bytes transmitted SendCompletionStatus - status for the send Return Value: RXSTATUS - The return status for the operation --*/ { PSMB_PSE_ORDINARY_EXCHANGE OrdinaryExchange = (PSMB_PSE_ORDINARY_EXCHANGE)pExchange; SmbPseOEAssertConsistentLinkageFromOE("SmbPseExchangeSendCallbackHandler_default: "); UPDATE_OE_HISTORY_WITH_STATUS('cs'); OrdinaryExchange->SendCompletionStatus = SendCompletionStatus; if (!NT_SUCCESS(SendCompletionStatus)) { //sometimes we use exchange-status, sometimes exchange->smbstatus //set them both pExchange->Status = SendCompletionStatus; pExchange->SmbStatus = SendCompletionStatus; } SmbPseDbgRunMdlChain( OrdinaryExchange->AssociatedStufferState.HeaderPartialMdl, OrdinaryExchange->SaveLengthForDebug, OrdinaryExchange, "SmbPseExchangeSendCallbackHandler_default"); return STATUS_SUCCESS; } // SmbPseExchangeSendCallbackHandler_default NTSTATUS SmbPseExchangeStart_default( IN PSMB_EXCHANGE pExchange ) /*++ Routine Description: This is the start routine for ordinary exchanges. irght now this is just a simple wrapper. Arguments: pExchange - the exchange instance NOT an Ordinary Exchange Return Value: RXSTATUS - The return status for the operation --*/ { PSMB_PSE_ORDINARY_EXCHANGE OrdinaryExchange = (PSMB_PSE_ORDINARY_EXCHANGE)pExchange; PAGED_CODE(); return OrdinaryExchange->StartRoutine( (PSMB_PSE_ORDINARY_EXCHANGE)pExchange, pExchange->RxContext); } // SmbPseExchangeStart_default NTSTATUS SmbPseExchangeCopyDataHandler_default( IN PSMB_EXCHANGE pExchange, IN PMDL pCopyDataBuffer, IN ULONG CopyDataSize ) /*++ Routine Description: This is the copy data handling routine for ordinary exchanges. Arguments: pExchange - the exchange instance Return Value: RXSTATUS - The return status for the operation --*/ { PSMB_PSE_ORDINARY_EXCHANGE OrdinaryExchange = (PSMB_PSE_ORDINARY_EXCHANGE)pExchange; SmbPseOEAssertConsistentLinkageFromOE("SmbPseExchangeCopyDataHandler_default: "); UPDATE_OE_HISTORY_WITH_STATUS('dd'); OrdinaryExchange->MessageLength = CopyDataSize; pExchange->Status = STATUS_MORE_PROCESSING_REQUIRED; return STATUS_SUCCESS; } // SmbPseExchangeCopyDataHandler_default NTSTATUS SmbPseExchangeReceive_default( IN struct _SMB_EXCHANGE *pExchange, IN ULONG BytesIndicated, IN ULONG BytesAvailable, OUT ULONG *pBytesTaken, IN PSMB_HEADER pSmbHeader, OUT PMDL *pDataBufferPointer, OUT PULONG pDataSize, IN ULONG ReceiveFlags) /*++ Routine Description: This is the receive indication handling routine for ordinary exchanges Arguments: pExchange - the exchange instance BytesIndicated - the number of bytes indicated Bytes Available - the number of bytes available pBytesTaken - the number of bytes consumed pSmbHeader - pointer to the data buffer pDataBufferPointer - pointer to the buffer Mdl into which the remaining data is to be copied. pDataSize - the buffer size. Return Value: RXSTATUS - The return status for the operation Notes: This routine is called at DPC level directly from the tdi receive event handler. BUT, it is also called at task time from SmbPseContinueOrdinaryExchange. Often, we cannot complete processing from DPClevel because fileobjects, fcbs, srvopens, and fobx are pageable and not locked. --*/ { PSMB_PSE_ORDINARY_EXCHANGE OrdinaryExchange = (PSMB_PSE_ORDINARY_EXCHANGE)pExchange; PSMBSTUFFER_BUFFER_STATE StufferState = &OrdinaryExchange->AssociatedStufferState; PRX_CONTEXT RxContext = OrdinaryExchange->RxContext; NTSTATUS SmbStatus; NTSTATUS Status = STATUS_SUCCESS; PGENERIC_ANDX CommandState; UCHAR Command; ULONG CopyBufferLength; BOOLEAN ThisIsAReenter = BooleanFlagOn(OrdinaryExchange->Flags, SMBPSE_OE_FLAG_HEADER_ALREADY_PARSED); PLOWIO_CONTEXT LowIoContext; ULONG ByteCount; ULONG Remain; PSMB_PSE_OE_READWRITE rw = &OrdinaryExchange->ReadWrite; PCHAR startVa; SmbPseOEAssertConsistentLinkage("SmbPseExchangeReceive_default: "); UPDATE_OE_HISTORY_WITH_STATUS(ThisIsAReenter?'00':'01'); RxDbgTrace (0, Dbg, ("SmbPseExchangeReceive_default av/ind=%08lx/%08lx\n", BytesAvailable,BytesIndicated) ); RxDbgTrace (0, Dbg, (" -->headermdl %08lx\n",StufferState->HeaderMdl)); ASSERT_ORDINARY_EXCHANGE( OrdinaryExchange ); CommandState = &OrdinaryExchange->ParseResumeState; if ( !ThisIsAReenter ) { OrdinaryExchange->BytesIndicatedCopy = BytesIndicated; OrdinaryExchange->BytesAvailableCopy = BytesAvailable; pExchange->Status = SmbCeParseSmbHeader( pExchange, pSmbHeader, CommandState, &OrdinaryExchange->SmbStatus, BytesAvailable, BytesIndicated, pBytesTaken); UPDATE_OE_HISTORY_WITH_STATUS('22'); if ( pExchange->Status == STATUS_MORE_PROCESSING_REQUIRED) { goto COPY_FOR_RESUME; } if ( (pExchange->Status != STATUS_SUCCESS) || ((Command = OrdinaryExchange->ParseResumeState.AndXCommand) == SMB_COM_NO_ANDX_COMMAND) ) { goto FINALLY; } if (Command == SMB_COM_WRITE_ANDX) { if (!FlagOn(pSmbHeader->Flags2,SMB_FLAGS2_COMPRESSED)) { if (OrdinaryExchange->ReadWrite.CompressedRequestInProgress) { OrdinaryExchange->ReadWrite.CompressedReadOrWrite = FALSE; } } } SetFlag(OrdinaryExchange->Flags,SMBPSE_OE_FLAG_HEADER_ALREADY_PARSED); } else { OrdinaryExchange->Status = OrdinaryExchange->SmbStatus; RxDbgTrace (0, Dbg, (" -->this is a reenter\n")); Command = CommandState->AndXCommand; } SmbStatus = OrdinaryExchange->SmbStatus; if ( (SmbStatus!=RX_MAP_STATUS(SUCCESS)) ) { RxDbgTrace (0, Dbg, (" STATUS NOT SUCCESS = %08lx\n", SmbStatus)); } for ( ; Command != SMB_COM_NO_ANDX_COMMAND ; ) { PSMBPSE_RECEIVE_MODEL_PARAMETERS ReceiveModelParams = &SmbPseReceiveModelParameters[Command]; ULONG ReceiveModelParamsFlags; UCHAR mappedCommand = Command; PCHAR Response = (PCHAR)pSmbHeader + SmbGetUshort(&CommandState->AndXOffset); if( Response > (PCHAR)pSmbHeader + BytesAvailable ) { // Invalid Command *pBytesTaken = BytesAvailable; *pDataBufferPointer = NULL; *pDataSize = 0; Status = STATUS_SUCCESS; pExchange->Status = STATUS_INVALID_NETWORK_RESPONSE; goto FINALLY; } OrdinaryExchange->LastSmbCommand = Command; //this is used to multiplex in finish routines UPDATE_OE_HISTORY_WITH_STATUS('88'); // // Case on the Smb Command Type // ReceiveModelParamsFlags = ReceiveModelParams->Flags; if (ReceiveModelParamsFlags!=0) { //map this onto read_andx....which is the arm of the switch that implements the model mappedCommand = SMB_COM_READ_ANDX; } else { // // If there's a continuation, then copy&post. it used to always do this. // now, we're going to do it unless the command is modeled. // the modeling code will take care of correctly deciding to post/nopost. // if ( (OrdinaryExchange->AsyncResumptionRoutine != NULL) && !ThisIsAReenter) { goto COPY_FOR_RESUME; } } switch (mappedCommand) { case SMB_COM_READ_ANDX: { NTSTATUS FinishStatus = RX_MAP_STATUS(SUCCESS); NTSTATUS FinalStatus = RX_MAP_STATUS(SUCCESS); BOOLEAN ThisIsAnAndX = BooleanFlagOn(ReceiveModelParamsFlags,SMBPSE_RMP_THIS_IS_ANDX); BOOLEAN ThisWouldBeMyError = (IM_THE_LAST_GUY || !ThisIsAnAndX); RxDbgTrace( 0, Dbg, (" *(ind) %s, smbstatus=%08lx\n",ReceiveModelParams->IndicationString,SmbStatus) ); IF_DEBUG { BOOLEAN BadType = FALSE; DbgDoit(BadType = (OrdinaryExchange->OEType < ReceiveModelParams->LowType) || (OrdinaryExchange->OEType > ReceiveModelParams->HighType) ); if (BadType) { DbgPrint("Bad OEType....%u,Cmd=%02lx,Exch=%08lx\n",OrdinaryExchange->OEType,Command,OrdinaryExchange); ASSERT(!"proceed???"); } } // If this is an error and it's an error for this guy of the AndX // chain then finishup If it's a warning tho, continue according // to the Flags if ( NT_ERROR(SmbStatus) && ThisWouldBeMyError ) { SmbPseDiscardProtocol( SmbStatus ); RxDbgTrace( 0, Dbg, ("--->discard1\n")); goto FINALLY; } else if ( (SmbStatus != RX_MAP_STATUS(SUCCESS)) && ThisWouldBeMyError ) { if (!FlagOn(ReceiveModelParamsFlags,SMBPSE_RMP_WARNINGS_OK)) { SmbPseDiscardProtocol(SmbStatus); RxDbgTrace( 0, Dbg, ("--->discard1\n")); goto FINALLY; } else { FinalStatus = SmbStatus; } } // if there's no nocopy handler then do things the old way if (!FlagOn(ReceiveModelParamsFlags,SMBPSE_RMP_NOCOPY_HANDLER)) { // TEMPORARY!!!!!! // If there's a continuation, then copy&post. it used to always do this. now, we're // going to do it unless the command is modeled. the modeling code will take care of // correctly deciding to post/nopost. // if ((OrdinaryExchange->AsyncResumptionRoutine != NULL) && !ThisIsAReenter ) { goto COPY_FOR_RESUME; } // // Some operations can be completed only in the context of the thread // originating the request. (for example CREATE.) // if( FlagOn( ReceiveModelParamsFlags, SMBPSE_RMP_FORCE_SYNC ) && !ThisIsAReenter ) { goto COPY_FOR_RESUME; } //eventually, we'll finish from here but for now copy //CODE.IMPROVEMENT.ASHAMED....this is really mandatory....... if (RxShouldPostCompletion()) { goto COPY_FOR_RESUME; } if (ReceiveModelParams->ReceiveHandlerToken < SMBPSE_RECEIVE_HANDLER_TOKEN_MAXIMUM){ PSMBPSE_RECEIVE_HANDLER ReceiveHandler = SmbPseReceiveHandlers[ReceiveModelParams->ReceiveHandlerToken]; FinishStatus = ReceiveHandler( OrdinaryExchange, Response); } } else { PSMBPSE_NOCOPY_RECEIVE_HANDLER NoCopyReceiveHandler = (PSMBPSE_NOCOPY_RECEIVE_HANDLER)(SmbPseReceiveHandlers[ReceiveModelParams->ReceiveHandlerToken]); UCHAR Action; OrdinaryExchange->NoCopyFinalStatus = FinalStatus; Action = NoCopyReceiveHandler( OrdinaryExchange, BytesIndicated, BytesAvailable, pBytesTaken, pSmbHeader, pDataBufferPointer, pDataSize, #if DBG ThisIsAReenter, #endif Response ); switch(Action) { case SMBPSE_NOCOPYACTION_NORMALFINISH: NOTHING; break; case SMBPSE_NOCOPYACTION_MDLFINISH: Status = STATUS_MORE_PROCESSING_REQUIRED; //note that whatever does this must be the last command in the // packet unless we make continueOE more complicated goto FINALLY; case SMBPSE_NOCOPYACTION_COPY_FOR_RESUME: goto COPY_FOR_RESUME; case SMBPSE_NOCOPYACTION_DISCARD: *pBytesTaken = BytesAvailable; RxDbgTrace( 0, Dbg, ("--->discardX\n")); goto FINALLY; } } pExchange->Status = (FinishStatus==RX_MAP_STATUS(SUCCESS)) ? FinalStatus : FinishStatus; if (!ThisIsAnAndX) { Response = (PCHAR)&NullGenericAndX; } }//this corresponds to the top level of the switch break; default: { PSMBCEDB_SERVER_ENTRY pServerEntry = SmbCeGetExchangeServerEntry(pExchange); RxDbgTrace( 0, Dbg, (" *(ind) Unimplemented cmd=%02lx,wct=%02lx\n", Command,*Response) ); SmbCeTransportDisconnectIndicated(pServerEntry); *pBytesTaken = BytesAvailable; *pDataBufferPointer = NULL; *pDataSize = 0; Status = STATUS_SUCCESS; pExchange->Status = STATUS_INVALID_NETWORK_RESPONSE; goto FINALLY; } } CommandState = (PGENERIC_ANDX)Response; Command = CommandState->AndXCommand; } // // If we get here then we're done. // Make everyone happy by taking all the bytes. // //CODE.IMPROVEMENT: it is not clear to me that this is enough. some servers may send extra bytes // on the end. // *pBytesTaken = BytesAvailable; goto FINALLY; COPY_FOR_RESUME: //CODE.IMPROVEMENT even if we are taking by copy (as opposed to tail-MDL // which is how reads should work) we shouldn't copy the whole packet - // just the residue. of course, this is really only an issue when we have // significant andXing. CopyBufferLength = MmGetMdlByteCount(StufferState->HeaderMdl); if (BytesAvailable > CopyBufferLength) { pExchange->Status = STATUS_INVALID_NETWORK_RESPONSE; *pBytesTaken = BytesAvailable; Status = STATUS_SUCCESS; } else if (!FlagOn(ReceiveFlags,TDI_RECEIVE_ENTIRE_MESSAGE) || (BytesAvailable > BytesIndicated) || (BytesAvailable > 127)) { RxDbgTrace( 0, Dbg, ("Taking data through MDL\n") ); // Pass an MDL back in for copying the data *pDataBufferPointer = StufferState->HeaderMdl; *pDataSize = CopyBufferLength; *pBytesTaken = 0; Status = STATUS_MORE_PROCESSING_REQUIRED; } else { // Copy the data and resume the exchange ASSERT( BytesAvailable == BytesIndicated ); RxDbgTrace( 0, Dbg, ("Taking data through copying\n") ); *pBytesTaken = OrdinaryExchange->MessageLength = BytesAvailable; RtlCopyMemory(StufferState->BufferBase, pSmbHeader,BytesIndicated); ASSERT(SmbGetUlong((PULONG)pSmbHeader->Protocol) == (ULONG)SMB_HEADER_PROTOCOL); pExchange->Status = RX_MAP_STATUS(MORE_PROCESSING_REQUIRED); } if (ThisIsAReenter) { pExchange->Status = STATUS_INVALID_NETWORK_RESPONSE; } FINALLY: OrdinaryExchange->ParseResumeState = *CommandState; UPDATE_OE_HISTORY_WITH_STATUS('99'); return Status; } // SmbPseExchangeReceive_default #define SmbPseRIStringsBufferSize 500 CHAR SmbPseReceiveIndicationStringsBuffer[SmbPseRIStringsBufferSize]; ULONG SmbPseRIStringsBufferUsed = 0; VOID __SmbPseRMTableEntry( UCHAR SmbCommand, UCHAR Flags, SMBPSE_RECEIVE_HANDLER_TOKEN ReceiveHandlerToken, PSMBPSE_RECEIVE_HANDLER ReceiveHandler #if DBG , PBYTE IndicationString, SMB_PSE_ORDINARY_EXCHANGE_TYPE LowType, SMB_PSE_ORDINARY_EXCHANGE_TYPE HighType #endif ) { PSMBPSE_RECEIVE_MODEL_PARAMETERS r = &SmbPseReceiveModelParameters[SmbCommand]; #if DBG ULONG ISlength = strlen(IndicationString)+1; #endif PAGED_CODE(); r->Flags = SMBPSE_RMP_MODELED | Flags; r->ReceiveHandlerToken = (UCHAR)ReceiveHandlerToken; if (ReceiveHandlerToken < SMBPSE_RECEIVE_HANDLER_TOKEN_MAXIMUM){ ASSERT((SmbPseReceiveHandlers[ReceiveHandlerToken] == ReceiveHandler) || (SmbPseReceiveHandlers[ReceiveHandlerToken] == NULL)); SmbPseReceiveHandlers[ReceiveHandlerToken] = ReceiveHandler; } #if DBG r->ReceiveHandler = ReceiveHandler; r->LowType = LowType; r->HighType = HighType; if (SmbPseRIStringsBufferUsed+ISlength<=SmbPseRIStringsBufferSize) { r->IndicationString = &SmbPseReceiveIndicationStringsBuffer[SmbPseRIStringsBufferUsed]; RtlCopyMemory(r->IndicationString,IndicationString,ISlength); } else { if (SmbPseRIStringsBufferUsedIndicationString = &SmbPseReceiveIndicationStringsBuffer[SmbPseRIStringsBufferUsed]; } SmbPseRIStringsBufferUsed += ISlength; #endif } #if DBG #define SmbPseRMTableEntry(__smbcommand,b,c,token,__rcv,flags) \ __SmbPseRMTableEntry(SMB_COM_##__smbcommand,flags,token,__rcv \ ,#__smbcommand,b,c) #else #define SmbPseRMTableEntry(__smbcommand,b,c,token,__rcv,flags) \ __SmbPseRMTableEntry(SMB_COM_##__smbcommand,flags,token,__rcv \ ) #endif VOID SmbPseInitializeTables( void ) /*++ Routine Description: This routine initializes tables that are used at various points by the smbpse mechanisms. The must succeed structure(s) is(are) also initialized. Arguments: none Return Value: none --*/ { ULONG i; PAGED_CODE(); for (i=0;i<256;i++) { SmbPseReceiveModelParameters[i].Flags = 0; SmbPseReceiveModelParameters[i].ReceiveHandlerToken = SMBPSE_RECEIVE_HANDLER_TOKEN_MAXIMUM; } for (i=0;i