windows-nt/Source/XPSP1/NT/base/fs/rdr2/rdbss/smb.mrx/transact.c
2020-09-26 16:20:57 +08:00

3607 lines
143 KiB
C

/*++ BUILD Version: 0009 // Increment this if a change has global effects
Copyright (c) 1987-1993 Microsoft Corporation
Module Name:
transact.c
Abstract:
This file conatins the implementation of the transact exchange.
Author:
Balan Sethu Raman (SethuR) 06-Feb-95 Created
Revision:
Joe Linn (JoeLi) -- Revise multiple packet implementation
--*/
#include "precomp.h"
#pragma hdrstop
#include "align.h"
#pragma warning(error:4100) // Unreferenced formal parameter
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, SmbCeInitializeTransactionParameters)
#pragma alloc_text(PAGE, SmbCeUninitializeTransactionParameters)
#pragma alloc_text(PAGE, SmbCeDiscardTransactExchange)
#pragma alloc_text(PAGE, SmbCeSubmitTransactionRequest)
#pragma alloc_text(PAGE, _SmbCeTransact)
#pragma alloc_text(PAGE, SmbTransactBuildHeader)
#pragma alloc_text(PAGE, SmbTransactExchangeStart)
#pragma alloc_text(PAGE, SmbTransactExchangeAbort)
#pragma alloc_text(PAGE, SmbTransactExchangeErrorHandler)
#pragma alloc_text(PAGE, SmbTransactExchangeSendCallbackHandler)
#pragma alloc_text(PAGE, SmbCeInitializeTransactExchange)
#pragma alloc_text(PAGE, SendSecondaryRequests)
#endif
//#define SET_DONTSUBSUME_PARAMS
#ifdef SET_DONTSUBSUME_PARAMS
ULONG MRxSmbDontSubsumeParams = 1;
#else
ULONG MRxSmbDontSubsumeParams = 0;
#endif
#if DBG
#define DONTSUBSUME_PARAMS MRxSmbDontSubsumeParams
#else
#define DONTSUBSUME_PARAMS FALSE
#endif
SMB_TRANSACTION_OPTIONS RxDefaultTransactionOptions = DEFAULT_TRANSACTION_OPTIONS;
RXDT_DefineCategory(TRANSACT);
#define Dbg (DEBUG_TRACE_TRANSACT)
#define MIN(x,y) ((x) < (y) ? (x) : (y))
#define SMB_TRANSACT_MAXIMUM_PARAMETER_SIZE (0xffff)
#define SMB_TRANSACT_MAXIMUM_DATA_SIZE (0xffff)
typedef struct _SMB_TRANSACT_RESP_FORMAT_DESCRIPTION {
ULONG WordCount;
ULONG TotalParameterCount;
ULONG TotalDataCount;
ULONG ParameterCount;
ULONG ParameterOffset;
ULONG ParameterDisplacement;
ULONG DataCount;
ULONG DataOffset;
ULONG DataDisplacement;
ULONG ByteCount;
ULONG ApparentMsgLength;
} SMB_TRANSACT_RESP_FORMAT_DESCRIPTION, *PSMB_TRANSACT_RESP_FORMAT_DESCRIPTION;
NTSTATUS
SmbTransactAccrueAndValidateFormatData(
IN struct _SMB_TRANSACT_EXCHANGE *pTransactExchange, // The exchange instance
IN PSMB_HEADER pSmbHeader,
IN ULONG BytesIndicated,
OUT PSMB_TRANSACT_RESP_FORMAT_DESCRIPTION Format
);
extern NTSTATUS
SmbTransactExchangeFinalize(
PSMB_EXCHANGE pExchange,
BOOLEAN *pPostFinalize);
extern NTSTATUS
ParseTransactResponse(
IN struct _SMB_TRANSACT_EXCHANGE *pTransactExchange, // The exchange instance
IN PSMB_TRANSACT_RESP_FORMAT_DESCRIPTION Format,
IN ULONG BytesIndicated,
IN ULONG BytesAvailable,
OUT ULONG *pBytesTaken,
IN PSMB_HEADER pSmbHeader,
OUT PMDL *pCopyRequestMdlPointer,
OUT PULONG pCopyRequestSize);
extern NTSTATUS
SendSecondaryRequests(PVOID pContext);
extern NTSTATUS
SmbCeInitializeTransactExchange(
PSMB_TRANSACT_EXCHANGE pTransactExchange,
PRX_CONTEXT RxContext,
PSMB_TRANSACTION_OPTIONS pOptions,
PSMB_TRANSACTION_SEND_PARAMETERS pSendParameters,
PSMB_TRANSACTION_RECEIVE_PARAMETERS pReceiveParameters,
PSMB_TRANSACTION_RESUMPTION_CONTEXT pResumptionContext);
NTSTATUS
SmbCeInitializeTransactionParameters(
PVOID pSetup,
USHORT SetupLength,
PVOID pParam,
ULONG ParamLength,
PVOID pData,
ULONG DataLength,
PSMB_TRANSACTION_PARAMETERS pTransactionParameters
)
/*++
Routine Description:
This routine initializes the transaction parameters
Arguments:
pSetup - the setup buffer
SetupLength - the setup buffer length
pParam - the param buffer
ParamLength - the param buffer length
pData - the data buffer
DataLength - the data buffer length
pTransactionParameters - the transaction parameters instance
Return Value:
RXSTATUS - The return status for the operation
Notes:
The TRANSACTION parameters come in two flavours -- the send parameters for the data
that is to be sent to the server and the receive parameters for receiving the data
from the server. There is one subtle difference in the way in which the parameters are
stored and referenced in these two cases. In the send case the Setup buffer is stored
as a pointer itself while in the receive case it is stored in the form of a MDL.
This is because the SMB protocol requires that the Header + setup information for a
transaction request cannot be greated then the maximum SMB buffer size, i.e., setup
information cannot spill to a secondary request. The buffer that is allocated for the
header is made sufficiently large enough to hold the setup data as well. On the other
hand the receives are handled in a two phase manner, -- the indication at the DPC
level followed by a copy data request if required. In order to avoid having to transition
between DPC level and a worker thread the MDL's for the buffers are eagerly evaluated.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
PMDL pSetupMdl = NULL;
PMDL pParamMdl = NULL;
PMDL pDataMdl = NULL;
PAGED_CODE();
if (pTransactionParameters->Flags & TRANSACTION_RECEIVE_PARAMETERS_FLAG) {
if ((pSetup != NULL) && (SetupLength > 0)) {
pSetupMdl = RxAllocateMdl(pSetup,SetupLength);
if (pSetupMdl == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
} else {
RxProbeAndLockPages(pSetupMdl,KernelMode,IoModifyAccess,Status);
if ((Status != RX_MAP_STATUS(SUCCESS))) {
IoFreeMdl(pSetupMdl);
pSetupMdl = NULL;
} else {
if (MmGetSystemAddressForMdlSafe(pSetupMdl,LowPagePriority) == NULL) { //this maps the Mdl
Status = STATUS_INSUFFICIENT_RESOURCES;
}
}
}
}
if ((Status == RX_MAP_STATUS(SUCCESS)) && (pParam != NULL) && (ParamLength > 0)) {
pParamMdl = RxAllocateMdl(pParam,ParamLength);
if (pParamMdl == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
} else {
RxProbeAndLockPages(pParamMdl,KernelMode,IoModifyAccess,Status);
if ((Status != RX_MAP_STATUS(SUCCESS))) {
IoFreeMdl(pParamMdl);
pParamMdl = NULL;
} else {
if (MmGetSystemAddressForMdlSafe(pParamMdl,LowPagePriority) == NULL) { //this maps the Mdl
Status = STATUS_INSUFFICIENT_RESOURCES;
}
}
}
}
pTransactionParameters->SetupLength = SetupLength;
pTransactionParameters->ParamLength = ParamLength;
pTransactionParameters->pParamMdl = pParamMdl;
pTransactionParameters->pSetupMdl = pSetupMdl;
} else {
pTransactionParameters->SetupLength = SetupLength;
pTransactionParameters->pSetup = pSetup;
pTransactionParameters->ParamLength = ParamLength;
pTransactionParameters->pParam = pParam;
pTransactionParameters->pParamMdl = NULL;
}
ASSERT( !((pData == NULL)&&(DataLength!=0)) );
if ((Status == RX_MAP_STATUS(SUCCESS)) && (pData != NULL) && (DataLength > 0)) {
pDataMdl = RxAllocateMdl(pData,DataLength);
if (pDataMdl == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
} else {
RxProbeAndLockPages(pDataMdl,KernelMode,IoModifyAccess,Status);
if ((Status != RX_MAP_STATUS(SUCCESS))) {
IoFreeMdl(pDataMdl);
pDataMdl = NULL;
} else {
if (MmGetSystemAddressForMdlSafe(pDataMdl,LowPagePriority) == NULL) { //this maps the Mdl
Status = STATUS_INSUFFICIENT_RESOURCES;
}
}
}
}
pTransactionParameters->pDataMdl = pDataMdl;
pTransactionParameters->DataLength = DataLength;
ASSERT((Status != RX_MAP_STATUS(SUCCESS)) || (DataLength == 0) || (pDataMdl != NULL));
if ((Status != RX_MAP_STATUS(SUCCESS))) {
if (pTransactionParameters->Flags & TRANSACTION_RECEIVE_PARAMETERS_FLAG) {
if (pSetupMdl != NULL) {
MmUnlockPages(pSetupMdl); //this unmaps as well
IoFreeMdl(pSetupMdl);
}
if (pParamMdl != NULL) {
MmUnlockPages(pParamMdl);
IoFreeMdl(pParamMdl);
}
}
if (pDataMdl != NULL) {
MmUnlockPages(pDataMdl);
IoFreeMdl(pDataMdl);
}
}
return Status;
}
VOID
SmbCeUninitializeTransactionParameters(
PSMB_TRANSACTION_PARAMETERS pTransactionParameters
)
/*++
Routine Description:
This routine uninitializes the transaction parameters, i.e., free the associated MDL's
Arguments:
pTransactionParameters - the parameter instance for uninitialization
--*/
{
PAGED_CODE();
if (pTransactionParameters->Flags & TRANSACTION_RECEIVE_PARAMETERS_FLAG) {
if (pTransactionParameters->pSetupMdl != NULL) {
MmUnlockPages(pTransactionParameters->pSetupMdl);
IoFreeMdl(pTransactionParameters->pSetupMdl);
}
}
if (pTransactionParameters->pParamMdl != NULL) {
MmUnlockPages(pTransactionParameters->pParamMdl);
IoFreeMdl(pTransactionParameters->pParamMdl);
}
if (pTransactionParameters->pDataMdl != NULL
&& !BooleanFlagOn(pTransactionParameters->Flags,SMB_XACT_FLAGS_CALLERS_SENDDATAMDL)) {
MmUnlockPages(pTransactionParameters->pDataMdl);
IoFreeMdl(pTransactionParameters->pDataMdl);
}
}
VOID
SmbCeDiscardTransactExchange(PSMB_TRANSACT_EXCHANGE pTransactExchange)
/*++
Routine Description:
This routine discards a transact exchange
Arguments:
pExchange - the exchange instance
--*/
{
PSMB_TRANSACTION_RESUMPTION_CONTEXT pResumptionContext;
PAGED_CODE();
// Deallocate any transact exchange specfic allocations ...
if (pTransactExchange->pActualPrimaryRequestSmbHeader != NULL) {
RxFreePool(pTransactExchange->pActualPrimaryRequestSmbHeader);
}
if (pTransactExchange->pReceiveSetupMdl != NULL) {
MmUnlockPages(pTransactExchange->pReceiveSetupMdl);
IoFreeMdl(pTransactExchange->pReceiveSetupMdl);
}
if (pTransactExchange->pReceiveParamMdl != NULL) {
MmUnlockPages(pTransactExchange->pReceiveParamMdl);
IoFreeMdl(pTransactExchange->pReceiveParamMdl);
}
if (pTransactExchange->pReceiveDataMdl != NULL) {
MmUnlockPages(pTransactExchange->pReceiveDataMdl);
IoFreeMdl(pTransactExchange->pReceiveDataMdl);
}
if (pTransactExchange->pSendSetupMdl != NULL) {
MmUnlockPages(pTransactExchange->pSendSetupMdl);
IoFreeMdl(pTransactExchange->pSendSetupMdl);
}
if ((pTransactExchange->pSendDataMdl != NULL) &&
!BooleanFlagOn(pTransactExchange->Flags,SMB_XACT_FLAGS_CALLERS_SENDDATAMDL)) {
MmUnlockPages(pTransactExchange->pSendDataMdl);
IoFreeMdl(pTransactExchange->pSendDataMdl);
}
if (pTransactExchange->pSendParamMdl != NULL) {
MmUnlockPages(pTransactExchange->pSendParamMdl);
IoFreeMdl(pTransactExchange->pSendParamMdl);
}
if ((pResumptionContext = pTransactExchange->pResumptionContext) != NULL) {
NTSTATUS FinalStatus;
PSMBCEDB_SERVER_ENTRY pServerEntry = SmbCeGetExchangeServerEntry((PSMB_EXCHANGE)pTransactExchange);
RxDbgTrace(0, Dbg,
("SmbCeTransactExchangeFinalize: everythings is good! parambytes (%ld) databytes (%ld)\n",
pTransactExchange->ParamBytesReceived, pTransactExchange->DataBytesReceived
));
FinalStatus = pTransactExchange->Status;
if (pTransactExchange->Status != STATUS_SUCCESS) {
FinalStatus = CscTransitionVNetRootForDisconnectedOperation(
pTransactExchange->RxContext,
SmbCeGetExchangeVNetRoot(pTransactExchange),
pTransactExchange->Status);
}
else if (pTransactExchange->SmbStatus != STATUS_SUCCESS)
{
FinalStatus = CscTransitionVNetRootForDisconnectedOperation(
pTransactExchange->RxContext,
SmbCeGetExchangeVNetRoot(pTransactExchange),
pTransactExchange->SmbStatus);
}
if (pServerEntry->ServerStatus != STATUS_SUCCESS &&
!SmbCeIsServerInDisconnectedMode(pServerEntry) &&
!FlagOn(pTransactExchange->SmbCeFlags,SMBCE_EXCHANGE_MAILSLOT_OPERATION)) {
// If the server entry is in error state, the transact cannot receive a response from server.
// In this case, we return the server status.
pResumptionContext->FinalStatusFromServer = pServerEntry->ServerStatus;
} else {
// If the server entry is in good or disconnected state, we return the smb status.
pResumptionContext->FinalStatusFromServer = pTransactExchange->SmbStatus;
}
if ((FinalStatus == STATUS_SUCCESS)||
(FinalStatus == STATUS_MORE_PROCESSING_REQUIRED)) {
FinalStatus = pResumptionContext->FinalStatusFromServer;
}
pResumptionContext->SmbCeResumptionContext.Status = FinalStatus;
pResumptionContext->SetupBytesReceived = pTransactExchange->SetupBytesReceived;
pResumptionContext->DataBytesReceived = pTransactExchange->DataBytesReceived;
pResumptionContext->ParameterBytesReceived = pTransactExchange->ParamBytesReceived;
pResumptionContext->ServerVersion = pTransactExchange->ServerVersion;
SmbCeResume(&pResumptionContext->SmbCeResumptionContext);
}
SmbCeDereferenceAndDiscardExchange((PSMB_EXCHANGE)pTransactExchange);
}
NTSTATUS
SmbCeSubmitTransactionRequest(
PRX_CONTEXT RxContext,
PSMB_TRANSACTION_OPTIONS pOptions,
PSMB_TRANSACTION_PARAMETERS pSendParameters,
PSMB_TRANSACTION_PARAMETERS pReceiveParameters,
PSMB_TRANSACTION_RESUMPTION_CONTEXT pResumptionContext )
/*++
Routine Description:
This routine submits a transaction request, i.e., allocates/initializes a transaction
exchange, sets up the completion information and initiates it
Arguments:
pNetRoot - the netroot for which the transaction request is intended
pOptions - the transaction options
pSendParameters - the transaction parameters to be sent to the server
pReceiveParameters - the transaction results from the server
pResumptionContext - the context for resuming the local activity on completion of the
transaction
Return Value:
RXSTATUS - The return status for the operation
STATUS_PENDING -- if the transcation was initiated successfully
Other error codes if the request could not be submitted successfully
Notes:
Whenever a status of STATUS_PENDING is returned it implies that the transact
exchange has assumed ownership of the MDLs passed in as receive and send
parameters. They will be released on completion of the exchange.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
RxCaptureFcb;
RxCaptureFobx;
PMRX_V_NET_ROOT pVNetRoot = NULL;
PSMB_TRANSACT_EXCHANGE pTransactExchange;
PSMB_EXCHANGE pExchange = NULL;
PAGED_CODE();
if (capFobx == NULL) {
if (RxContext->MajorFunction == IRP_MJ_CREATE) {
pVNetRoot = RxContext->Create.pVNetRoot;
}
} else {
// These are the root objects which are associated with the device FCB. In
// such cases
pVNetRoot = (PMRX_V_NET_ROOT)capFobx;
if (NodeType(pVNetRoot) != RDBSS_NTC_V_NETROOT) {
pVNetRoot = capFobx->pSrvOpen->pVNetRoot;
}
}
if (pVNetRoot == NULL) {
PSMBCEDB_SERVER_ENTRY pServerEntry;
pServerEntry = SmbCeGetAssociatedServerEntry(capFcb->pNetRoot->pSrvCall);
// Allocate and initialize an exchange for the given net root.
Status = SmbCeInitializeExchange2(
&pExchange,
RxContext,
pServerEntry,
TRANSACT_EXCHANGE,
&TransactExchangeDispatch);
} else {
// Allocate and initialize an exchange for the given net root.
Status = SmbCeInitializeExchange(
&pExchange,
RxContext,
pVNetRoot,
TRANSACT_EXCHANGE,
&TransactExchangeDispatch);
}
if (Status == STATUS_SUCCESS) {
// Initialize the transact exchange
pTransactExchange = (PSMB_TRANSACT_EXCHANGE)pExchange;
Status = SmbCeInitializeTransactExchange(
pTransactExchange,
RxContext,
pOptions,
pSendParameters,
pReceiveParameters,
pResumptionContext);
if (Status == STATUS_SUCCESS) {
// The transact exchange can be either asynchronous or synchronous. In
// the asynchronous case an additional reference is taken which is
// passed onto the caller alongwith the exchange squirelled away in the
// RX_CONTEXT if STATUS_PENDING is being returned. This enables the
// caller to control when the exchange is discarded. This works
// especially well in dealing with cancellation of asynchronous
// exchanges.
// This reference will be accounted for by the finalization routine
// of the transact exchange.
SmbCeReferenceExchange((PSMB_EXCHANGE)pTransactExchange);
if (BooleanFlagOn(pOptions->Flags,SMB_XACT_FLAGS_ASYNCHRONOUS)) {
// The corresponding dereference is the callers responsibility
SmbCeReferenceExchange((PSMB_EXCHANGE)pTransactExchange);
}
if (pTransactExchange->Flags & SMB_XACT_FLAGS_MAILSLOT_OPERATION) {
pTransactExchange->SmbCeFlags |= SMBCE_EXCHANGE_MAILSLOT_OPERATION;
}
pResumptionContext->pTransactExchange = pTransactExchange;
pResumptionContext->SmbCeResumptionContext.Status = STATUS_SUCCESS;
SmbCeIncrementPendingLocalOperations(pExchange);
// Initiate the exchange
Status = SmbCeInitiateExchange(pExchange);
if (Status != STATUS_PENDING) {
pExchange->Status = Status;
if (pExchange->SmbStatus == STATUS_SUCCESS) {
pExchange->SmbStatus = Status;
}
if (BooleanFlagOn(pOptions->Flags,SMB_XACT_FLAGS_ASYNCHRONOUS)) {
PMRXSMB_RX_CONTEXT pMRxSmbContext = MRxSmbGetMinirdrContext(RxContext);
pMRxSmbContext->pExchange = NULL;
// Since the exchange has already been completed there is no
// point in returning the additional reference to the caller
SmbCeDereferenceExchange((PSMB_EXCHANGE)pTransactExchange);
}
}
SmbCeDecrementPendingLocalOperationsAndFinalize(pExchange);
// Map the status to STATUS_PENDING so that continuation routines
// do not attempt to finalize.
Status = STATUS_PENDING;
} else {
PMRXSMB_RX_CONTEXT MRxSmbContext = MRxSmbGetMinirdrContext(RxContext);
ASSERT(MRxSmbContext->pExchange == pExchange);
MRxSmbContext->pExchange = NULL;
SmbCeDiscardExchange(pExchange);
}
}
return Status;
}
NTSTATUS
_SmbCeTransact(
PRX_CONTEXT RxContext,
PSMB_TRANSACTION_OPTIONS pOptions,
PVOID pInputSetupBuffer,
ULONG InputSetupBufferLength,
PVOID pOutputSetupBuffer,
ULONG OutputSetupBufferLength,
PVOID pInputParamBuffer,
ULONG InputParamBufferLength,
PVOID pOutputParamBuffer,
ULONG OutputParamBufferLength,
PVOID pInputDataBuffer,
ULONG InputDataBufferLength,
PVOID pOutputDataBuffer,
ULONG OutputDataBufferLength,
PSMB_TRANSACTION_RESUMPTION_CONTEXT pResumptionContext)
/*++
Routine Description:
This routine implements a standardized mechanism of submitting transaction requests,
and synchronizing with their completion. This does not provide the smae amount of control
that SmbCeSubmitTransactRequest provides. Nevertheless, this implements a common mechanism
that should satisfy most needs
Arguments:
RxContext - the context for the transaction
pOptions - the transaction options
pSetupBuffer - the transaction setup buffer
SetupBufferlength - the setup buffer length
pInputParamBuffer - the Input param buffer
InputParamBufferLength - the input param buffer length
pOutputParamBuffer - the output param buffer
OutputParamBufferlength - the output param buffer length
pInputDataBuffer - the Input data buffer
InputDataBufferLength - the input data buffer length
pOutputDataBuffer - the output data buffer
OutputDataBufferlength - the output data buffer length
pResumptionContext - the transaction resumption context
Return Value:
RXSTATUS - The return status for the operation
STATUS_SUCCESS if successfull.
Other error codes if the request could not be submitted successfully
Notes:
In the case of asynchronous exchanges if STATUS_PENDING is returned the
Exchange instance is squirelled away in the minirdr context associated with
the given RX_CONTEXT instance. This exchange will not be discarded without
the callers intervention. It is the callers responsibility to invoke
SmbCeDereferenceAndDiscardExchange to discard the exchange
--*/
{
NTSTATUS Status;
SMB_TRANSACTION_SEND_PARAMETERS SendParameters;
SMB_TRANSACTION_RECEIVE_PARAMETERS ReceiveParameters;
BOOLEAN fAsynchronous;
PAGED_CODE();
fAsynchronous = BooleanFlagOn(pOptions->Flags,SMB_XACT_FLAGS_ASYNCHRONOUS);
Status = SmbCeInitializeTransactionSendParameters(
pInputSetupBuffer,
(USHORT)InputSetupBufferLength,
pInputParamBuffer,
InputParamBufferLength,
pInputDataBuffer,
InputDataBufferLength,
&SendParameters);
if (Status == STATUS_SUCCESS) {
Status = SmbCeInitializeTransactionReceiveParameters(
pOutputSetupBuffer, // the setup information expected in return
(USHORT)OutputSetupBufferLength, // the length of the setup information
pOutputParamBuffer, // the buffer for the param information
OutputParamBufferLength, // the length of the param buffer
pOutputDataBuffer, // the buffer for data
OutputDataBufferLength, // the length of the buffer
&ReceiveParameters);
if (Status != STATUS_SUCCESS) {
SmbCeUninitializeTransactionSendParameters(&SendParameters);
}
}
if (Status == STATUS_SUCCESS) {
Status = SmbCeSubmitTransactionRequest(
RxContext, // the RXContext for the transaction
pOptions, // transaction options
&SendParameters, // input parameters
&ReceiveParameters, // expected results
pResumptionContext // the context for resumption.
);
if ((Status != STATUS_SUCCESS) &&
(Status != STATUS_PENDING)) {
SmbCeUninitializeTransactionReceiveParameters(&ReceiveParameters);
SmbCeUninitializeTransactionSendParameters(&SendParameters);
} else {
if (!fAsynchronous) {
if (Status == STATUS_PENDING) {
SmbCeWaitOnTransactionResumptionContext(pResumptionContext);
Status = pResumptionContext->SmbCeResumptionContext.Status;
if (Status != STATUS_SUCCESS) {
RxDbgTrace(0,Dbg,("SmbCeTransact: Transaction Request Completion Status %lx\n",Status));
}
} else if (Status != STATUS_SUCCESS) {
RxDbgTrace(0,Dbg,("SmbCeTransact: SmbCeSubmitTransactRequest returned %lx\n",Status));
} else {
Status = pResumptionContext->SmbCeResumptionContext.Status;
}
}
}
}
ASSERT(fAsynchronous || (Status != STATUS_PENDING));
if (fAsynchronous && (Status != STATUS_PENDING)) {
pResumptionContext->SmbCeResumptionContext.Status = Status;
pResumptionContext->FinalStatusFromServer = Status;
SmbCeResume(&pResumptionContext->SmbCeResumptionContext);
Status = STATUS_PENDING;
}
return Status;
}
NTSTATUS
SmbTransactBuildHeader(
PSMB_TRANSACT_EXCHANGE pTransactExchange,
UCHAR SmbCommand,
PSMB_HEADER pHeader)
/*++
Routine Description:
This routine builds the SMB header for transact exchanges
Arguments:
pTransactExchange - the exchange instance
SmbCommand - the SMB command
pHeader - the SMB buffer header
Return Value:
RXSTATUS - The return status for the operation
Notes:
--*/
{
NTSTATUS Status;
ULONG BufferConsumed;
UCHAR LastCommandInHeader;
PUCHAR pCommand;
PAGED_CODE();
// Initialize the SMB header ...
Status = SmbCeBuildSmbHeader(
(PSMB_EXCHANGE)pTransactExchange,
pHeader,
sizeof(SMB_HEADER),
&BufferConsumed,
&LastCommandInHeader,
&pCommand);
if (Status == STATUS_SUCCESS) {
PSMBCEDB_SERVER_ENTRY pServerEntry;
ASSERT(LastCommandInHeader == SMB_COM_NO_ANDX_COMMAND);
*pCommand = SmbCommand;
pServerEntry = SmbCeGetExchangeServerEntry(pTransactExchange);
if (FlagOn(pServerEntry->Server.DialectFlags,DF_NT_SMBS)) {
// for NT servers, we have to set the pid/pidhigh fields so that RPC will work. unless this is a
// mailslot write.
if (!(pTransactExchange->Flags & SMB_XACT_FLAGS_MAILSLOT_OPERATION)) {
SmbCeSetFullProcessIdInHeader(
(PSMB_EXCHANGE)pTransactExchange,
RxGetRequestorProcessId(pTransactExchange->RxContext),
((PNT_SMB_HEADER)pHeader));
}
}
if (pTransactExchange->Flags & SMB_XACT_FLAGS_MAILSLOT_OPERATION) {
pHeader->Flags2 &= ~(SMB_FLAGS2_NT_STATUS);
}
if (pTransactExchange->Flags & SMB_XACT_FLAGS_DFS_AWARE) {
pHeader->Flags2 |= SMB_FLAGS2_DFS;
}
}
return Status;
}
NTSTATUS
SmbTransactExchangeStart(
PSMB_EXCHANGE pExchange)
/*++
Routine Description:
This is the start routine for transact exchanges. This initiates the construction of the
appropriate SMB's if required.
Arguments:
pExchange - the exchange instance
Return Value:
RXSTATUS - The return status for the operation
Notes:
--*/
{
NTSTATUS Status;
PSMB_TRANSACT_EXCHANGE pTransactExchange;
PVOID pActualPrimaryRequestSmbHeader;
PSMB_HEADER pPrimaryRequestSmbHeader;
// The MDL's used in sending the primary request associated with the TRANSACT SMB
PMDL pPartialDataMdl = NULL;
PMDL pPartialParamMdl = NULL;
PMDL pPaddingMdl = NULL;
PMDL pPrimaryRequestSmbMdl = NULL;
PMDL pLastMdlInChain = NULL;
ULONG MaximumSmbBufferSize;
ULONG PrimaryRequestSmbSize = 0;
ULONG PaddingLength = 0;
BOOLEAN QuadwordAlignmentRequired = FALSE;
ULONG ParamBytesToBeSent = 0;
ULONG DataBytesToBeSent = 0;
ULONG ParamOffset,DataOffset;
ULONG SmbLength;
ULONG BccOffset;
ULONG MdlLength;
USHORT *pBcc;
PAGED_CODE();
pTransactExchange = (PSMB_TRANSACT_EXCHANGE)pExchange;
pActualPrimaryRequestSmbHeader = pTransactExchange->pActualPrimaryRequestSmbHeader;
pPrimaryRequestSmbHeader = pTransactExchange->pPrimaryRequestSmbHeader;
ASSERT(pActualPrimaryRequestSmbHeader != NULL);
ASSERT(pPrimaryRequestSmbHeader != NULL);
ASSERT(!(pExchange->SmbCeFlags & SMBCE_EXCHANGE_SESSION_CONSTRUCTOR) &&
!(pExchange->SmbCeFlags & SMBCE_EXCHANGE_NETROOT_CONSTRUCTOR));
// Initialize the SMB header ...
Status = SmbTransactBuildHeader(
pTransactExchange,
pTransactExchange->TransactSmbCommand,
pPrimaryRequestSmbHeader);
if ((Status != RX_MAP_STATUS(SUCCESS))) {
// Finalize the exchange.
pExchange->Status = Status;
return Status;
}
PrimaryRequestSmbSize = sizeof(SMB_HEADER);
// Compute the BccOffset and the ParamOffset which is in turn used in computing the
// param and data bytes to be sent as part of the primary request.
switch (pTransactExchange->TransactSmbCommand) {
case SMB_COM_TRANSACTION:
case SMB_COM_TRANSACTION2:
{
PREQ_TRANSACTION pTransactRequest = (PREQ_TRANSACTION)
(pPrimaryRequestSmbHeader + 1);
USHORT SetupLength = pTransactRequest->SetupCount * sizeof(WORD);
BccOffset = sizeof(SMB_HEADER) +
FIELD_OFFSET(REQ_TRANSACTION,Buffer) +
SetupLength;
ParamOffset = ROUND_UP_COUNT(
(BccOffset +
pTransactExchange->TransactionNameLength +
sizeof(USHORT)),
ALIGN_DWORD);
pBcc = (PUSHORT)((PBYTE)pPrimaryRequestSmbHeader + BccOffset);
}
break;
case SMB_COM_NT_TRANSACT:
{
PREQ_NT_TRANSACTION pNtTransactRequest = (PREQ_NT_TRANSACTION)
(pPrimaryRequestSmbHeader + 1);
USHORT SetupLength = pNtTransactRequest->SetupCount * sizeof(WORD);
RxDbgTrace( 0, Dbg, ("SmbTransactExchangeSTAAT1: init for NT_T (p,d,mp,md) %d %d %d %d\n",
pNtTransactRequest->TotalParameterCount, pNtTransactRequest->TotalDataCount,
pNtTransactRequest->MaxParameterCount, pNtTransactRequest->MaxDataCount));
RxDbgTrace( 0, Dbg, ("SmbTransactExchangeSTAyuk: init for NT_T (s,ms) %d %d \n",
pNtTransactRequest->SetupCount, pNtTransactRequest->MaxSetupCount));
BccOffset = sizeof(SMB_HEADER) +
FIELD_OFFSET(REQ_NT_TRANSACTION,Buffer[0]) +
SetupLength;
ParamOffset = ROUND_UP_COUNT(
(BccOffset + sizeof(USHORT)),
ALIGN_DWORD);
pBcc = (PUSHORT)((PBYTE)pPrimaryRequestSmbHeader + BccOffset);
if (pTransactExchange->NtTransactFunction == NT_TRANSACT_SET_QUOTA) {
QuadwordAlignmentRequired = TRUE;
}
}
break;
default:
ASSERT(!"Valid Smb Command for initiating Transaction");
return STATUS_INVALID_PARAMETER;
}
// Compute the data/param bytes that can be sent as part of the primary request
MaximumSmbBufferSize = pTransactExchange->MaximumTransmitSmbBufferSize;
ParamBytesToBeSent = MIN(
(MaximumSmbBufferSize - ParamOffset),
pTransactExchange->SendParamBufferSize);
if (!QuadwordAlignmentRequired) {
DataOffset = ROUND_UP_COUNT(ParamOffset + ParamBytesToBeSent, ALIGN_DWORD);
} else {
DataOffset = ROUND_UP_COUNT(ParamOffset + ParamBytesToBeSent, ALIGN_QUAD);
}
if (DataOffset < MaximumSmbBufferSize) {
DataBytesToBeSent = MIN((MaximumSmbBufferSize - DataOffset),
pTransactExchange->SendDataBufferSize);
PaddingLength = DataOffset - (ParamOffset + ParamBytesToBeSent);
} else {
DataBytesToBeSent = 0;
}
if ( DataBytesToBeSent == 0) {
DataOffset = PaddingLength = 0;
}
RxDbgTrace( 0, Dbg, ("SmbCeTransactExchangeStart: params,padding,data=%d,%d,%d\n",
ParamBytesToBeSent,PaddingLength,DataBytesToBeSent ));
RxDbgTrace( 0, Dbg, ("SmbCeTransactExchangeStart: paramsoffset,dataoffset=%d,%d\n",
ParamOffset,DataOffset ));
RxDbgTrace( 0, Dbg, ("SmbCeTransactExchangeStart: phdr,pbcc=%08lx,%08lx\n",
pPrimaryRequestSmbHeader,pBcc ));
// Update the primary request buffer with the final sizes of the data/parameter etc.
switch (pTransactExchange->TransactSmbCommand) {
case SMB_COM_TRANSACTION:
case SMB_COM_TRANSACTION2:
{
PREQ_TRANSACTION pTransactRequest = (PREQ_TRANSACTION)
(pPrimaryRequestSmbHeader + 1);
RxDbgTrace( 0, Dbg, ("SmbCeTransactExchangeStart: TRANSACTION/TRANSACTION2\n"));
SmbPutUshort( &pTransactRequest->ParameterCount, (USHORT)ParamBytesToBeSent );
SmbPutUshort( &pTransactRequest->ParameterOffset, (USHORT)ParamOffset);
SmbPutUshort( &pTransactRequest->DataCount, (USHORT)DataBytesToBeSent);
SmbPutUshort( &pTransactRequest->DataOffset, (USHORT)DataOffset);
}
break;
case SMB_COM_NT_TRANSACT:
{
PREQ_NT_TRANSACTION pNtTransactRequest = (PREQ_NT_TRANSACTION)
(pPrimaryRequestSmbHeader + 1);
RxDbgTrace( 0, Dbg, ("SmbCeTransactExchangeStart: NT transacton\n"));
RxDbgTrace( 0, Dbg, ("SmbTransactExchangeSTAAT2: init for NT_T (p,d,mp,md) %d %d %d %d\n",
pNtTransactRequest->TotalParameterCount, pNtTransactRequest->TotalDataCount,
pNtTransactRequest->MaxParameterCount, pNtTransactRequest->MaxDataCount));
SmbPutUlong( &pNtTransactRequest->ParameterCount, ParamBytesToBeSent);
SmbPutUlong( &pNtTransactRequest->ParameterOffset, ParamOffset);
SmbPutUlong( &pNtTransactRequest->DataCount, DataBytesToBeSent);
SmbPutUlong( &pNtTransactRequest->DataOffset, DataOffset);
}
break;
default:
ASSERT(!"Valid Smb Command for initiating Transaction");
return STATUS_INVALID_PARAMETER;
}
// Update the Bcc field in the SMB and compute the SMB length
SmbPutUshort(
pBcc,
(USHORT)((ParamOffset - BccOffset - sizeof(USHORT)) +
ParamBytesToBeSent +
PaddingLength +
DataBytesToBeSent)
);
SmbLength = ParamOffset +
ParamBytesToBeSent +
PaddingLength +
DataBytesToBeSent;
// The primary request buffer should be locked down for transmission. In order to
// preclude race conditions while freeing this routine assumes ownership of the buffer.
// There are two reasons why this model has to be adopted ...
// 1) Inititaiting a transaction request can possibly involve a reconnection attempt
// which will involve network traffic. Consequently the transmission of the primary
// request can potentially occur in a worker thread which is different from the one
// initializing the exchange. This problem can be worked around by carrying all the
// possible context around and actually constructing the header as part of this routine.
// But this would imply that those requests which could have been filtered out easily
// because of error conditions etc. will be handled very late.
pTransactExchange->pActualPrimaryRequestSmbHeader = NULL;
pTransactExchange->pPrimaryRequestSmbHeader = NULL;
// Ensure that the MDL's have been probed & locked. The new MDL's have been allocated.
// The partial MDL's are allocated to be large enough to span the maximum buffer
// length possible.
MdlLength = ParamOffset;
if (pTransactExchange->fParamsSubsumedInPrimaryRequest) {
MdlLength += ParamBytesToBeSent + PaddingLength;
}
RxAllocateHeaderMdl(
pPrimaryRequestSmbHeader,
MdlLength,
pPrimaryRequestSmbMdl
);
if (pPrimaryRequestSmbMdl != NULL) {
Status = STATUS_SUCCESS;
} else {
RxDbgTrace( 0, Dbg, ("SmbCeTransactExchangeStart: Insuffcient resources for MDL's\n"));
Status = STATUS_INSUFFICIENT_RESOURCES;
}
if ((DataBytesToBeSent > 0) &&
(Status == RX_MAP_STATUS(SUCCESS))) {
pPartialDataMdl = RxAllocateMdl(
0,
(MIN(pTransactExchange->SendDataBufferSize,MaximumSmbBufferSize) +
PAGE_SIZE - 1)
);
if (pPartialDataMdl != NULL) {
Status = STATUS_SUCCESS;
} else {
RxDbgTrace( 0, Dbg, ("SmbCeTransactExchangeStart: Insuffcient resources for MDL's\n"));
Status = STATUS_INSUFFICIENT_RESOURCES;
}
}
if ((ParamBytesToBeSent > 0) &&
!pTransactExchange->fParamsSubsumedInPrimaryRequest &&
(Status == RX_MAP_STATUS(SUCCESS))) {
pPartialParamMdl = RxAllocateMdl(
pTransactExchange->pSendParamBuffer,
ParamBytesToBeSent);
if (PaddingLength!= 0) {
pPaddingMdl = RxAllocateMdl(0,(sizeof(DWORD) + PAGE_SIZE - 1));
} else {
pPaddingMdl = NULL;
}
if ((pPartialParamMdl != NULL) &&
((pPaddingMdl != NULL)||(PaddingLength==0))) {
Status = STATUS_SUCCESS;
} else {
RxDbgTrace( 0, Dbg, ("SmbCeTransactExchangeStart: no param/pad MDLs %08lx %08lx\n",
pPartialParamMdl,pPaddingMdl));
Status = STATUS_INSUFFICIENT_RESOURCES;
}
}
// At this point the validity of all the parameters will have been ascertained. The trivial
// cases have been filtered out. Start the transact exchange.
// Implementation Note: The Transact exchange implementation relies upon chaining the
// MDL's together to build the relevant request buffers that need be sent. This ensures
// that redundant copying of data is avoided altogether. Depending upon the parameters
// specified the composite MDL that is sent is composed of the following MDL's.
// TRANSACT2 and NT TRANSACT exchanges ...
// The composite buffer is made up off atmost four MDL's that are chained together. These
// are the header buffer, the setup buffer, parameter buffer and the data buffer.
// All the secondary requests are made up off atmost three MDL's that are chained together.
// These are the header buffer, the parameter buffer and the data buffer.
// TRANSACT exchanges ....
// The composite buffer is made up off atmost three MDL's that are chained together. These are
// the header buffer ( includes the name and the setup information) , the parameter buffer
// and the data buffer.
// All the secondary requests are made up off atmost three MDL's that are chained together.
// These are the header buffer, the parameter buffer and the data buffer.
// In all of these cases the number of MDL's can go up by 1 if a padding MDL is required
// between the parameter buffer and the data buffer to ensure that all alignment requirements
// are satisfied.
if ((Status == RX_MAP_STATUS(SUCCESS))) {
RxProbeAndLockHeaderPages(pPrimaryRequestSmbMdl,KernelMode,IoModifyAccess,Status);
if ((Status != RX_MAP_STATUS(SUCCESS))) { //do this now. the code below will try to unlock
IoFreeMdl(pPrimaryRequestSmbMdl);
pPrimaryRequestSmbMdl = NULL;
} else {
if (MmGetSystemAddressForMdlSafe(pPrimaryRequestSmbMdl,LowPagePriority) == NULL) { //map it
Status = STATUS_INSUFFICIENT_RESOURCES;
}
}
}
if ((Status == RX_MAP_STATUS(SUCCESS))) {
pLastMdlInChain = pPrimaryRequestSmbMdl;
if (ParamBytesToBeSent > 0) {
RxDbgTrace(
0,
Dbg,
("SmbCeTransactExchangeStart: Sending Param bytes %ld at offset %ld\n",
ParamBytesToBeSent,
ParamOffset)
);
pTransactExchange->ParamBytesSent = ParamBytesToBeSent;
if (!pTransactExchange->fParamsSubsumedInPrimaryRequest) {
IoBuildPartialMdl(
pTransactExchange->pSendParamMdl,
pPartialParamMdl,
(PBYTE)MmGetMdlVirtualAddress(pTransactExchange->pSendParamMdl),
ParamBytesToBeSent);
// Chain the MDL's together
pLastMdlInChain->Next = pPartialParamMdl;
pLastMdlInChain = pPartialParamMdl;
}
}
// Link the data buffer or portions of it if the size constraints are satisfied
// If padding is required between the parameter and data portions in the
// primary request include the padding MDL, otherwise chain the data MDL
// directly.
if (DataBytesToBeSent > 0) {
if (!pTransactExchange->fParamsSubsumedInPrimaryRequest &&
(PaddingLength > 0)) {
RxDbgTrace( 0, Dbg, ("SmbCeTransactExchangeStart: Padding Length %ld\n",PaddingLength));
RxBuildPaddingPartialMdl(pPaddingMdl,PaddingLength);
pLastMdlInChain->Next = pPaddingMdl;
pLastMdlInChain = pPaddingMdl;
}
RxDbgTrace( 0, Dbg,("SmbCeTransactExchangeStart: Sending Data bytes %ld at offset %ld\n",
DataBytesToBeSent, DataOffset) );
pTransactExchange->DataBytesSent = DataBytesToBeSent;
IoBuildPartialMdl(
pTransactExchange->pSendDataMdl,
pPartialDataMdl,
(PBYTE)MmGetMdlVirtualAddress(pTransactExchange->pSendDataMdl),
DataBytesToBeSent);
pLastMdlInChain->Next = pPartialDataMdl;
pLastMdlInChain = pPartialDataMdl;
}
if ((Status == RX_MAP_STATUS(SUCCESS))) {
if (FlagOn(pTransactExchange->SmbCeFlags,SMBCE_EXCHANGE_MAILSLOT_OPERATION)) {
pTransactExchange->SmbCeFlags |= SMBCE_EXCHANGE_MID_VALID;
pTransactExchange->Mid = SMBCE_MAILSLOT_OPERATION_MID;
}
// There are cases in which the transaction exchange can be completed by merely sending
// the primary request SMB. This should be distinguished from those cases in which either
// a response is expected or a number of secondary requests need to be issued based upon
// the parameter buffer length, data buffer length and the flags specified.
if ((pTransactExchange->Flags & SMB_TRANSACTION_NO_RESPONSE ) &&
(pTransactExchange->SendDataBufferSize == DataBytesToBeSent) &&
(pTransactExchange->SendParamBufferSize == ParamBytesToBeSent)) {
// No response is expected in this case. Therefore Send should suffice instead of
// Tranceive
// since we don't expect to do any more here, set the exchange status to success
pExchange->Status = STATUS_SUCCESS;
pTransactExchange->pResumptionContext->FinalStatusFromServer = STATUS_SUCCESS;
RxDbgTrace( 0, Dbg, ("SmbCeTransactExchangeStart: SmbCeSend(No Response expected)\n"));
Status = SmbCeSend(
pExchange,
RXCE_SEND_SYNCHRONOUS,
pPrimaryRequestSmbMdl,
SmbLength);
if ((Status != RX_MAP_STATUS(SUCCESS))) {
RxDbgTrace( 0, Dbg, ("SmbCeTransactExchangeStart: SmbCeSend returned %lx\n",Status));
}
} else {
// This transaction involves ttansmit/receive of multiple SMB's. A tranceive is in
// order.
if ((pTransactExchange->SendDataBufferSize == DataBytesToBeSent) &&
(pTransactExchange->SendParamBufferSize == ParamBytesToBeSent)) {
RxDbgTrace( 0, Dbg, ("SmbCeTransactExchangeStart: No Secondary Requests\n"));
pTransactExchange->State = TRANSACT_EXCHANGE_TRANSMITTED_SECONDARY_REQUESTS;
} else {
pTransactExchange->State = TRANSACT_EXCHANGE_TRANSMITTED_PRIMARY_REQUEST;
}
RxDbgTrace( 0, Dbg, ("SmbCeTransactExchangeStart: SmbCeTranceive(Response expected)\n"));
//CODE.IMPROVEMENT send.sync????.........yeeeeech.
Status = SmbCeTranceive(
pExchange,
RXCE_SEND_SYNCHRONOUS,
pPrimaryRequestSmbMdl,
SmbLength);
if ((Status != RX_MAP_STATUS(SUCCESS))) {
RxDbgTrace( 0, Dbg, ("SmbCeTransactExchangeStart: SmbCeTranceive returned %lx\n",Status));
}
}
}
}
if (pPartialParamMdl != NULL) {
IoFreeMdl(pPartialParamMdl);
}
if (pPartialDataMdl != NULL) {
IoFreeMdl(pPartialDataMdl);
}
if (pPaddingMdl != NULL) {
IoFreeMdl(pPaddingMdl);
}
if (pPrimaryRequestSmbMdl != NULL) {
if (RxMdlIsLocked(pPrimaryRequestSmbMdl))
{
RxUnlockHeaderPages(pPrimaryRequestSmbMdl);
}
IoFreeMdl(pPrimaryRequestSmbMdl);
}
RxFreePool(pActualPrimaryRequestSmbHeader);
if (Status != STATUS_PENDING) {
pExchange->Status = Status;
}
return Status;
}
NTSTATUS
SmbTransactExchangeReceive(
IN struct _SMB_EXCHANGE *pExchange, // The exchange instance
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 recieve indication handling routine for transact 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 - the byte buffer
pDataBufferPointer - the buffer 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.
--*/
{
NTSTATUS Status;
PNTSTATUS pFinalSmbStatus;
BOOLEAN fError = FALSE;
BOOLEAN fIndicationNotSufficient = FALSE;
BOOLEAN fMoreParsingRequired = FALSE;
BOOLEAN fDoErrorProcessing = FALSE; //this is a hack CODE.IMPROVEMENT
SMB_TRANSACT_RESP_FORMAT_DESCRIPTION Format;
GENERIC_ANDX CommandToProcess;
ULONG TransactResponseSize = 0;
ULONG SetupBytesOffsetInResponse = 0;
ULONG SetupBytesInResponse = 0;
ULONG CopyDataSize = 0;
PMDL pSetupMdl = NULL;
PMDL pCopyRequestMdl = NULL;
PSMB_TRANSACT_EXCHANGE pTransactExchange = (PSMB_TRANSACT_EXCHANGE)pExchange;
RxDbgTrace( 0, Dbg,
("SmbTransactExchangeReceive: Entering w/ Bytes Available (%ld) Bytes Indicated (%ld) State (%ld)\n",
BytesAvailable,
BytesIndicated,
pTransactExchange->State
));
RxDbgTrace( 0, Dbg,
("SmbTransactExchangeReceive: Buffer %08lx Consumed (%ld) MDL (%08lx)\n",
pSmbHeader,
*pBytesTaken,
*pDataBufferPointer
));
pFinalSmbStatus = &pTransactExchange->SmbStatus;
Status = SmbCeParseSmbHeader(
pExchange,
pSmbHeader,
&CommandToProcess,
pFinalSmbStatus,
BytesAvailable,
BytesIndicated,
pBytesTaken);
if (Status != STATUS_SUCCESS) {
Status = STATUS_INVALID_NETWORK_RESPONSE;
goto FINALLY;
}
//this need some explanation. parseheader is written so as to take some extra smbs off the from
//of the packet...specifically, stuff like sessionsetup&X and TC&X. since no transact is a valid followon
//it would not make since if (a) not enough were indicated or (b) an early command had an error. so
//we must have success. CODE.REVIEW.JOELINN you should look in parseheader and (1) remove the *taken=avail and
//(b) look for asserts that the server sends stuff back correctly. these must be changed into BAD_RESPONSE_AND_DISCARDs
//the "Status = STATUS_SUCCESS" is to try to get the compiler to optimize.
if (*((PBYTE)(pSmbHeader+1)) == 0 && (pTransactExchange->State!=TRANSACT_EXCHANGE_TRANSMITTED_PRIMARY_REQUEST)) {
RxDbgTrace(0,Dbg,("SmbTransactExchangeReceive: FinalSmbStatus = %lx\n", *pFinalSmbStatus));
if (NT_SUCCESS(*pFinalSmbStatus)) {
Status = STATUS_INVALID_NETWORK_RESPONSE;
goto FINALLY;
}
}
//we know that status is SUCCESS from the assert above. but we will still continue to check so as
//to be more resilient when we don't have msg boundaries. we have the following cases depending on the
//characteristics of the smbresponse
//
// non-error: get the data and then return the stored responsestatus. the process of getting the data
// causes us to update the param and data counts so that we know when we have reached the
// end of the data. the parse routine re-ups the receive if needed.
// error: there are main cases:
// a) the server has sent no data. here we discard the packet and we can just get out. the
// finalize routine will pickup the status correctly.
// b) here, we have to discard the packet AND update the byte counts AND re-up the receive
// if necessary. to discard the packet, we must either compute the apparent msg length from
// the WC and BC parameters (best) OR use our maximum buffer size
fMoreParsingRequired = FALSE;
if ((Status == RX_MAP_STATUS(SUCCESS))) {
if (TRUE) { //maybe sometimes we wont copy!
if (CommandToProcess.WordCount > 0) {
ULONG TransactResponseSize = 0;
// Ensure that at the very least enough bytes have been indicated to determine
// the length of the setup, parameters and data for the transaction.
//CODE.IMPROVEMENT.ASHAMED this is very clumsy....we should have computed this earlier.
// and saved it in the exchange. at a minimum move it to the validateformat routine
switch (CommandToProcess.AndXCommand) {
case SMB_COM_NT_TRANSACT:
case SMB_COM_NT_TRANSACT_SECONDARY:
TransactResponseSize = FIELD_OFFSET(RESP_NT_TRANSACTION,Buffer);
break;
case SMB_COM_TRANSACTION:
case SMB_COM_TRANSACTION2:
case SMB_COM_TRANSACTION_SECONDARY:
case SMB_COM_TRANSACTION2_SECONDARY:
TransactResponseSize = FIELD_OFFSET(RESP_TRANSACTION,Buffer);
break;
default:
TransactResponseSize = 0xffffffff;
Status = RX_MAP_STATUS(INVALID_NETWORK_RESPONSE);
break;
}
if (BytesIndicated >= (sizeof(SMB_HEADER) + TransactResponseSize)) {
fMoreParsingRequired = TRUE;
} else {
fIndicationNotSufficient = TRUE;
*pFinalSmbStatus = STATUS_INVALID_NETWORK_RESPONSE;
}
} else {
// allow a response with wordcount==0 to go thru if we're the right state
fMoreParsingRequired = (pTransactExchange->State==TRANSACT_EXCHANGE_TRANSMITTED_PRIMARY_REQUEST);
}
}
}
if (fMoreParsingRequired) {
// The header was successfully parsed and the SMB response did not contain any errors
// The stage is set for processing the transaction response.
switch (pTransactExchange->State) {
case TRANSACT_EXCHANGE_TRANSMITTED_PRIMARY_REQUEST:
{
// The primary request for the transaction has been sent and there are
// secondary requests to be sent.
// The only response expected at this time is an interim response. Any
// other response will be treated as an error.
PRESP_TRANSACTION_INTERIM pInterimResponse;
RxDbgTrace(0,Dbg,("SmbCeTransactExchangeReceive: Processing interim response\n"));
if ((*pBytesTaken + FIELD_OFFSET(RESP_TRANSACTION_INTERIM,Buffer)) <= BytesIndicated) {
pInterimResponse = (PRESP_TRANSACTION_INTERIM)((PBYTE)pSmbHeader + *pBytesTaken);
if ((NT_SUCCESS(pExchange->SmbStatus)) &&
(pSmbHeader->Command == pTransactExchange->TransactSmbCommand) &&
(SmbGetUshort(&pInterimResponse->WordCount) == 0) &&
(SmbGetUshort(&pInterimResponse->ByteCount) == 0)) {
// The interim response was valid. Transition the state of the exchange
// and transmit the secondary requests.
*pBytesTaken += FIELD_OFFSET(RESP_TRANSACTION_INTERIM,Buffer);
//CODE.IMPROVEMENT that only works if the server doesn't send extra crap
pTransactExchange->State = TRANSACT_EXCHANGE_RECEIVED_INTERIM_RESPONSE;
// Determine if any secondary transaction requests need to be sent. if none are
// required then modify the state
ASSERT((pTransactExchange->ParamBytesSent < pTransactExchange->SendParamBufferSize) ||
(pTransactExchange->DataBytesSent < pTransactExchange->SendDataBufferSize));
ASSERT((pTransactExchange->ParamBytesSent <= pTransactExchange->SendParamBufferSize) &&
(pTransactExchange->DataBytesSent <= pTransactExchange->SendDataBufferSize));
if (!(pTransactExchange->Flags & SMB_TRANSACTION_NO_RESPONSE )) {
Status = SmbCeReceive(pExchange);
}
if ((Status != RX_MAP_STATUS(SUCCESS))) {
pExchange->Status = Status;
} else {
Status = STATUS_SUCCESS;
SmbCeIncrementPendingLocalOperations(pExchange);
RxPostToWorkerThread(
MRxSmbDeviceObject,
CriticalWorkQueue,
&pExchange->WorkQueueItem,
SendSecondaryRequests,
pExchange);
}
} else if( !NT_SUCCESS(pExchange->SmbStatus) ) {
RxDbgTrace(0,Dbg,("SmbCeTransactExchangeReceive: Error on Response\n"));
Status = pExchange->SmbStatus;
} else {
RxDbgTrace(0,Dbg,("SmbCeTransactExchangeReceive: Invalid interim response\n"));
Status = STATUS_INVALID_NETWORK_RESPONSE;
}
} else {
fIndicationNotSufficient = TRUE;
Status = RX_MAP_STATUS(MORE_PROCESSING_REQUIRED);
}
}
break;
case TRANSACT_EXCHANGE_RECEIVED_INTERIM_RESPONSE:
RxDbgTrace(0,Dbg,("SmbCeTransactExchangeReceive: received again while in interim response\n"));
//no break: this is okay
case TRANSACT_EXCHANGE_TRANSMITTED_SECONDARY_REQUESTS:
case TRANSACT_EXCHANGE_RECEIVED_PRIMARY_RESPONSE:
{
BOOLEAN fPrimaryResponse = FALSE;
PRESP_TRANSACTION pTransactResponse;
PRESP_NT_TRANSACTION pNtTransactResponse;
ULONG TotalParamBytesInResponse;
ULONG TotalDataBytesInResponse;
RxDbgTrace(0,Dbg,("SmbCeTransactExchangeReceive: Processing Primary/Secondary response\n"));
//do this here so there's only one copy if the code
pTransactResponse = (PRESP_TRANSACTION)((PBYTE)pSmbHeader +
SmbGetUshort(&CommandToProcess.AndXOffset));
// All the requests ( both primary and secondary have been sent ). The
// only responses expected in this state are (1) a primary response and (2) a
// secondary response. Any other response is an error.
if (pSmbHeader->Command == pTransactExchange->TransactSmbCommand) {
switch (pSmbHeader->Command) {
case SMB_COM_TRANSACTION:
case SMB_COM_TRANSACTION2:
//pTransactResponse = (PRESP_TRANSACTION)((PBYTE)pSmbHeader +
// SmbGetUshort(&CommandToProcess.AndXOffset));
fPrimaryResponse = TRUE;
SetupBytesOffsetInResponse = FIELD_OFFSET(RESP_TRANSACTION,Buffer);
SetupBytesInResponse = sizeof(USHORT) * pTransactResponse->SetupCount;
// Initialize the total count of data and param bytes that will be received from
// the server during the course ofthe transaction response.
TotalParamBytesInResponse = SmbGetUshort(&pTransactResponse->TotalParameterCount);
TotalDataBytesInResponse = SmbGetUshort(&pTransactResponse->TotalDataCount);
// fall through
case SMB_COM_TRANSACTION_SECONDARY:
case SMB_COM_TRANSACTION2_SECONDARY:
TransactResponseSize = FIELD_OFFSET(RESP_TRANSACTION,Buffer);
break;
case SMB_COM_NT_TRANSACT:
//pNtTransactResponse = (PRESP_NT_TRANSACTION)((PBYTE)pSmbHeader +
// SmbGetUshort(&CommandToProcess.AndXOffset));
pNtTransactResponse = (PRESP_NT_TRANSACTION)pTransactResponse;
fPrimaryResponse = TRUE;
SetupBytesOffsetInResponse = FIELD_OFFSET(RESP_NT_TRANSACTION,Buffer);
SetupBytesInResponse = sizeof(USHORT) * pNtTransactResponse->SetupCount;
// Initialize the total count of data and param bytes that will be received from
// the server during the course ofthe transaction response.
TotalParamBytesInResponse = SmbGetUshort(&pNtTransactResponse->TotalParameterCount);
TotalDataBytesInResponse = SmbGetUshort(&pNtTransactResponse->TotalDataCount);
// fall through ..
case SMB_COM_NT_TRANSACT_SECONDARY:
TransactResponseSize = FIELD_OFFSET(RESP_NT_TRANSACTION,Buffer);
break;
default:
// Abort the exchange. An unexpected response was received during the
// course of the transaction.
ASSERT(!"Valid network response");
Status = STATUS_INVALID_NETWORK_RESPONSE;
}
if ((Status == RX_MAP_STATUS(SUCCESS))) {
if (fPrimaryResponse) {
RxDbgTrace( 0,
Dbg,
("SmbTransactExchangeReceive: Primary Response Setup Bytes(%ld) Param Bytes (%ld) Data Bytes (%ld)\n",
SetupBytesInResponse,
TotalParamBytesInResponse,
TotalDataBytesInResponse
)
);
if ((TotalParamBytesInResponse > pTransactExchange->ReceiveParamBufferSize) ||
(TotalDataBytesInResponse > pTransactExchange->ReceiveDataBufferSize)) {
Status = STATUS_INVALID_NETWORK_RESPONSE;
goto FINALLY;
} else {
pTransactExchange->ReceiveParamBufferSize = TotalParamBytesInResponse;
pTransactExchange->ReceiveDataBufferSize = TotalDataBytesInResponse;
}
}
if (Status == STATUS_SUCCESS &&
TransactResponseSize + *pBytesTaken <= BytesIndicated) {
if (fPrimaryResponse &&
(SetupBytesInResponse > 0)) {
PBYTE pSetupStartAddress;
ULONG SetupBytesIndicated = MIN(SetupBytesInResponse,
BytesIndicated - SetupBytesOffsetInResponse);
if( pTransactExchange->pReceiveSetupMdl ) {
pSetupStartAddress = (PBYTE)MmGetSystemAddressForMdlSafe(
pTransactExchange->pReceiveSetupMdl,
LowPagePriority
);
if( pSetupStartAddress == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
} else {
if (SetupBytesInResponse == SetupBytesIndicated) {
RtlCopyMemory(
pSetupStartAddress,
((PBYTE)pSmbHeader + SetupBytesOffsetInResponse),
SetupBytesIndicated);
pSetupStartAddress += SetupBytesIndicated;
SetupBytesInResponse -= SetupBytesIndicated;
SetupBytesOffsetInResponse += SetupBytesIndicated;
pTransactExchange->SetupBytesReceived = SetupBytesInResponse;
} else {
// NTRAID-87018-2/10/2000 yunlin we do a indication_not_sufficient
ASSERT(!"this code doesn't work");
RxDbgTrace(0,Dbg,("SmbTransactExchangeReceive: Setup Bytes Partially Indicated\n"));
// Some setup bytes have not been indicated. An MDL needs to be
// created for copying the data. This MDL should also include the padding
// MDL for copying the padding bytes ...
pSetupMdl = RxAllocateMdl(pSetupStartAddress,SetupBytesInResponse);
if ( pSetupMdl != NULL ) {
IoBuildPartialMdl(
pTransactExchange->pReceiveSetupMdl,
pSetupMdl,
pSetupStartAddress,
SetupBytesInResponse);
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
}
}
}
RxDbgTrace(0,Dbg,("SmbTransactExchangeReceive: Setup Bytes Indicated (%ld)\n",SetupBytesIndicated));
}
if (Status == STATUS_SUCCESS) {
// from here, we cannot go back and redo the header....so we have to change state so
//that the copy routine doesn't try to reparse
pTransactExchange->State = TRANSACT_EXCHANGE_RECEIVED_PRIMARY_RESPONSE;
Status = SmbTransactAccrueAndValidateFormatData(
pTransactExchange,
pSmbHeader,
BytesIndicated,
&Format);
if (Status != STATUS_SUCCESS) {
goto FINALLY;
}
Status = ParseTransactResponse(
pTransactExchange,&Format,
BytesIndicated,
BytesAvailable,
pBytesTaken,
pSmbHeader,
&pCopyRequestMdl,
&CopyDataSize);
if (Status == STATUS_MORE_PROCESSING_REQUIRED) {
// Link the setup MDL with the MDL returned
if (pSetupMdl != NULL) {
if (pCopyRequestMdl != NULL) {
pSetupMdl->Next = pCopyRequestMdl;
}
pCopyRequestMdl = pSetupMdl;
CopyDataSize += SetupBytesInResponse;
}
}
//check if the server has sent extra bytes.....
// ---------------------------------------------------------------------------------------------
{
ULONG ApparentMsgLength = max(BytesAvailable,Format.ApparentMsgLength);
ULONG DeficitBytes = ApparentMsgLength - (*pBytesTaken+CopyDataSize);
if (ApparentMsgLength < *pBytesTaken+CopyDataSize) {
Status = STATUS_INVALID_NETWORK_RESPONSE;
goto FINALLY;
}
if (DeficitBytes > 0) {
RxLog(("XtraBytes %lx %lx",pTransactExchange,DeficitBytes));
SmbLog(LOG,
SmbTransactExchangeReceive_1,
LOGPTR(pTransactExchange)
LOGULONG(DeficitBytes));
if (CopyDataSize==0) {
if (*pBytesTaken > BytesAvailable) {
Status = STATUS_INVALID_NETWORK_RESPONSE;
goto FINALLY;
}
RxLog(("Extra Bytes were sent and copydatasize==0........\n"));
SmbLog(LOG,
SmbTransactExchangeReceive_2,
LOGULONG(CopyDataSize));
*pBytesTaken = BytesAvailable; //cant take more than this
} else {
PMDL LastMdl,TrailingBytesMdl;
if ( DeficitBytes > TRAILING_BYTES_BUFFERSIZE) {
Status = STATUS_INVALID_NETWORK_RESPONSE;
goto FINALLY;
}
TrailingBytesMdl = &pTransactExchange->TrailingBytesMdl;
MmInitializeMdl(
TrailingBytesMdl,
&pTransactExchange->TrailingBytesBuffer.Bytes[0],
DeficitBytes
);
MmBuildMdlForNonPagedPool(TrailingBytesMdl);
LastMdl = pCopyRequestMdl;
ASSERT(LastMdl != NULL);
for (;LastMdl->Next!=NULL;LastMdl=LastMdl->Next) ;
ASSERT(LastMdl != NULL);
ASSERT(LastMdl->Next == NULL);
LastMdl->Next = TrailingBytesMdl;
CopyDataSize += DeficitBytes;
}
}
}
// ---------------------------------------------------------------------------------------------
RxDbgTrace(0,Dbg,("SmbTransactExchangeReceive: ParseTransactResponse returned %lx\n",Status));
}
*pDataBufferPointer = pCopyRequestMdl;
*pDataSize = CopyDataSize;
} else {
RxDbgTrace(0,Dbg,("SmbTransactExchangeReceive: Indication not sufficient: trsz %08lx bytestakn %08lx \n",
TransactResponseSize, *pBytesTaken));
fIndicationNotSufficient = TRUE;
if (Status == STATUS_SUCCESS) {
Status = STATUS_MORE_PROCESSING_REQUIRED;
}
}
}
} else {
Status = STATUS_INVALID_NETWORK_RESPONSE;
}
}
break;
default:
{
ASSERT(!"Valid Transact Exchange State for receiving responses");
RxDbgTrace( 0, Dbg, ("SmbTransactExchangeReceive: Aborting Exchange -- invalid state\n"));
}
break;
}
} else {
// We get here if either the status or the smbstatus is not success.
// If sufficient bytes were not indicated for processing the header a copy data request
// needs to be posted. this occurs if status is status_more_processing_required
RxDbgTrace( 0, Dbg, ("SmbTransactExchangeReceive: bad status(es) from parseheadr %08lx %08lx\n",
Status,*pFinalSmbStatus));
fDoErrorProcessing = TRUE;
}
if ((Status == RX_MAP_STATUS(SUCCESS)) &&
(pTransactExchange->ParamBytesReceived == pTransactExchange->ReceiveParamBufferSize) &&
(pTransactExchange->DataBytesReceived == pTransactExchange->ReceiveDataBufferSize) &&
(pTransactExchange->PendingCopyRequests == 0)) {
NOTHING;
} else if (fDoErrorProcessing) {
BOOLEAN DoItTheShortWay = TRUE;
ULONG ApparentMsgLength;
RxDbgTrace(0,Dbg,("SmbTransactExchangeReceive: Error processing response %lx .. Exchange aborted\n",Status));
if (BytesAvailable > BytesIndicated ||
!FlagOn(ReceiveFlags,TDI_RECEIVE_ENTIRE_MESSAGE)) {
Status = SmbTransactAccrueAndValidateFormatData(
pTransactExchange,
pSmbHeader,
BytesIndicated,
&Format);
if (Status != STATUS_SUCCESS) {
goto FINALLY;
}
ApparentMsgLength = max(BytesAvailable,Format.ApparentMsgLength);
//if wordcount!=0 then the server is sending us bytes.....we have to continue doing
//receives until we have seen all the bytes
if ((pTransactExchange->ParameterBytesSeen<Format.ParameterCount) ||
(pTransactExchange->DataBytesSeen<Format.DataCount)) {
NTSTATUS ReceiveStatus;
// The exchange has been successfully completed. Finalize it.
RxDbgTrace(0,Dbg,("ParseTransactResponse: Register for more error responses\n"));
RxLog(("TxErr: %lx %lx %lx",pTransactExchange,
pTransactExchange->ParameterBytesSeen,pTransactExchange->DataBytesSeen));
SmbLog(LOG,
SmbTransactExchangeReceive_3,
LOGPTR(pTransactExchange)
LOGULONG(pTransactExchange->ParameterBytesSeen)
LOGULONG(pTransactExchange->DataBytesSeen));
ReceiveStatus = SmbCeReceive((PSMB_EXCHANGE)pTransactExchange);
if (ReceiveStatus != STATUS_SUCCESS) {
// There was an error in registering the receive. Abandon the transaction.
Status = ReceiveStatus;
RxLog(("TxErrAbandon %lx",pTransactExchange));
SmbLog(LOG,
SmbTransactExchangeReceive_4,
LOGPTR(pTransactExchange)
LOGULONG(Status));
//Make it fail the next two tests.....
ApparentMsgLength = 0; DoItTheShortWay = FALSE; //CODE.IMPROVEMENT bad coding...use some escape
}
}
//netbt will not allow us to discard the packet by setting taken=available. so, check for
//available>indicated. if true, take the bytes by conjuring up a buffer
if (ApparentMsgLength>BytesIndicated) {
//we'll have to lay down a buffer for this so that NetBT won't blow the session away
//CODE.IMPROVEMENT we should put this code into OE a well.......
//CODE.IMPROVEMENT if we had an smbbuf (as suggested above) we could use that
// to do the copy
ASSERT(pTransactExchange->Status == STATUS_MORE_PROCESSING_REQUIRED);
pTransactExchange->DiscardBuffer = RxAllocatePoolWithTag(
NonPagedPool,
ApparentMsgLength,
MRXSMB_XACT_POOLTAG);
if (pTransactExchange->DiscardBuffer!=NULL) {
*pBytesTaken = 0;
*pDataSize = ApparentMsgLength;
*pDataBufferPointer = &pTransactExchange->TrailingBytesMdl;
MmInitializeMdl(*pDataBufferPointer,
pTransactExchange->DiscardBuffer,
ApparentMsgLength
);
MmBuildMdlForNonPagedPool(*pDataBufferPointer);
pTransactExchange->SaveTheRealStatus = Status;
RxLog(("XRtakebytes %lx %lx\n",pTransactExchange,Status));
SmbLog(LOG,
SmbTransactExchangeReceive_5,
LOGPTR(pTransactExchange)
LOGULONG(Status));
Status = STATUS_MORE_PROCESSING_REQUIRED;
DoItTheShortWay = FALSE;
}
}
}
if (DoItTheShortWay) {
goto FINALLY;
}
}
RxDbgTrace( 0, Dbg,
("SmbTransactExchangeReceiveExit: Bytes Consumed (%ld) Status (%08lx) MDL (%08lx) size(%08lx)\n",
*pBytesTaken, Status, *pDataBufferPointer, *pDataSize
));
if ((Status == STATUS_SUCCESS) ||
(Status == STATUS_MORE_PROCESSING_REQUIRED)) {
return Status;
}
FINALLY:
*pBytesTaken = BytesAvailable;
*pDataBufferPointer = NULL;
// Abort the exchange
pTransactExchange->Status = Status;
Status = STATUS_SUCCESS;
RxDbgTrace(0,Dbg,("SmbTransactExchangeReceive: Exchange aborted.\n",Status));
return Status;
UNREFERENCED_PARAMETER(ReceiveFlags);
}
NTSTATUS
SmbTransactExchangeAbort(
PSMB_EXCHANGE pExchange)
/*++
Routine Description:
This is the abort routine for transact exchanges
Arguments:
pExchange - the exchange instance
Return Value:
RXSTATUS - The return status for the operation
--*/
{
PAGED_CODE();
// The SMB exchange completed with an error. Invoke the RDBSS callback routine
// and scavenge the exchange instance.
pExchange->Status = STATUS_REQUEST_ABORTED;
return STATUS_SUCCESS;
}
NTSTATUS
SmbTransactExchangeErrorHandler(
IN PSMB_EXCHANGE pExchange) // the SMB exchange instance
/*++
Routine Description:
This is the error indication handling routine for transact exchanges
Arguments:
pExchange - the exchange instance
Return Value:
RXSTATUS - The return status for the operation
--*/
{
PAGED_CODE();
// The SMB exchange completed with an error. Invoke the RDBSS callback routine
// and scavenge the exchange instance.
return STATUS_SUCCESS;
UNREFERENCED_PARAMETER(pExchange);
}
NTSTATUS
SmbTransactExchangeSendCallbackHandler(
IN PSMB_EXCHANGE pExchange, // The exchange instance
IN PMDL pXmitBuffer,
IN NTSTATUS SendCompletionStatus)
/*++
Routine Description:
This is the send call back indication handling routine for transact exchanges
Arguments:
pExchange - the exchange instance
Return Value:
RXSTATUS - The return status for the operation
--*/
{
PAGED_CODE();
return STATUS_SUCCESS;
UNREFERENCED_PARAMETER(pExchange);
UNREFERENCED_PARAMETER(pXmitBuffer);
UNREFERENCED_PARAMETER(SendCompletionStatus);
}
NTSTATUS
SmbTransactExchangeCopyDataHandler(
IN PSMB_EXCHANGE pExchange, // The exchange instance
IN PMDL pDataBuffer, // the buffer
IN ULONG DataSize)
/*++
Routine Description:
This is the copy data handling routine for transact exchanges
Arguments:
pExchange - the exchange instance
pDataBuffer - the buffer
DataSize - the amount of data returned
Return Value:
RXSTATUS - The return status for the operation
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
PSMB_TRANSACT_EXCHANGE pTransactExchange = (PSMB_TRANSACT_EXCHANGE)pExchange;
PMDL pCopyRequestMdl = NULL;
PMDL pCurMdl = NULL;
ULONG CopyRequestSize = 0;
PMDL TrailingBytesMdl = &pTransactExchange->TrailingBytesMdl;
ULONG BytesConsumed;
RxDbgTrace(+1,Dbg,("SmbTransactExchangeCopyDataHandler: Entered\n"));
if (pTransactExchange->DiscardBuffer!=NULL) {
//we just copied to get rid of the buffer....
//free the buffer, set the status and get out
RxFreePool(pTransactExchange->DiscardBuffer);
Status = pTransactExchange->SaveTheRealStatus;
RxDbgTrace(-1,Dbg,("SmbTransactExchangeCopyDataHandler: Discard Exit, status =%08lx\n"));
DbgPrint("copyHandlerDiscard, st=%08lx\n",Status);
return Status;
}
switch (pTransactExchange->State) {
case TRANSACT_EXCHANGE_TRANSMITTED_PRIMARY_REQUEST :
case TRANSACT_EXCHANGE_TRANSMITTED_SECONDARY_REQUESTS :
{
PSMB_HEADER pSmbHeader = (PSMB_HEADER)MmGetSystemAddressForMdlSafe(pDataBuffer,LowPagePriority);
RxDbgTrace(0,Dbg,("SmbTransactExchangeCopyDataHandler: Reparsing response\n"));
if (pSmbHeader == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
} else {
// The response could not be parsed with the indicated bytes. Invoke
// the receive method to resume parsing of the complete SMB
Status = SmbTransactExchangeReceive(
pExchange,
DataSize,
DataSize,
&BytesConsumed,
pSmbHeader,
&pCopyRequestMdl,
&CopyRequestSize,
TDI_RECEIVE_ENTIRE_MESSAGE);
}
if ((Status == RX_MAP_STATUS(SUCCESS))) {
ASSERT(BytesConsumed == DataSize);
ASSERT(pCopyRequestMdl == NULL);
ASSERT(CopyRequestSize == 0);
}
}
break;
case TRANSACT_EXCHANGE_RECEIVED_PRIMARY_RESPONSE :
{
RxDbgTrace(0,Dbg,("SmbTransactExchangeCopyDataHandler: Completing secondary response processing\n"));
// In this state only secondary responses will be received. All the secondary
// responses can be parsed from the indication. Therefore it is sufficient to
// merely free the MDL's and re-register with the connection engine for
// receiving subsequent requests.
InterlockedDecrement(&pTransactExchange->PendingCopyRequests);
if ((pTransactExchange->ParamBytesReceived == pTransactExchange->ReceiveParamBufferSize) &&
(pTransactExchange->DataBytesReceived == pTransactExchange->ReceiveDataBufferSize) &&
(pTransactExchange->PendingCopyRequests == 0)) {
// The exchange has been successfully completed. Finalize it.
RxDbgTrace(0,Dbg,("SmbTransactExchangeCopyDataHandler: Processed last secondary response successfully\n"));
pExchange->Status = STATUS_SUCCESS;
}
}
break;
default:
{
ASSERT(!"Valid State fore receiving copy data completion indication");
pExchange->Status = STATUS_INVALID_NETWORK_RESPONSE;
}
break;
}
// Free up the data buffers.
pCurMdl = pDataBuffer;
while (pCurMdl != NULL) {
PMDL pPrevMdl = pCurMdl;
pCurMdl = pCurMdl->Next;
if (pPrevMdl!=TrailingBytesMdl) {
IoFreeMdl(pPrevMdl);
}
}
RxDbgTrace(-1,Dbg,("SmbTransactExchangeCopyDataHandler: Exit\n"));
return Status;
}
NTSTATUS
SmbCeInitializeTransactExchange(
PSMB_TRANSACT_EXCHANGE pTransactExchange,
PRX_CONTEXT RxContext,
PSMB_TRANSACTION_OPTIONS pOptions,
PSMB_TRANSACTION_SEND_PARAMETERS pSendParameters,
PSMB_TRANSACTION_RECEIVE_PARAMETERS pReceiveParameters,
PSMB_TRANSACTION_RESUMPTION_CONTEXT pResumptionContext)
/*++
Routine Description:
This routine initializes a transact exchange instance
Arguments:
pTransactExchange - the exchange instance
RxContext - RDBSS context for the file involved in the transaction.
pOptions - the transaction options
pSendParameters - the parameters to be sent to the server
pReceiveParameters - the results from the server
pResumptionContext - the resumption context
Return Value:
RXSTATUS - The return status for the operation
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
RxCaptureFobx;
UCHAR SmbCommand;
PMDL pSendDataMdl;
PMDL pSendParamMdl; //used if we can't subsume
PMDL pReceiveDataMdl;
PMDL pReceiveParamMdl;
PVOID pSendSetupBuffer;
ULONG SendSetupBufferSize;
PMDL pReceiveSetupMdl;
ULONG ReceiveSetupBufferSize;
ULONG SendDataBufferSize;
ULONG ReceiveDataBufferSize;
PVOID pSendParamBuffer;
ULONG SendParamBufferSize;
ULONG ReceiveParamBufferSize;
ULONG MaxSmbBufferSize = 0;
ULONG PrimaryRequestSmbSize = 0;
// The fields in theSMB request that are dialect independent and need to be filled in
PUSHORT pBcc; // the byte count field
PUSHORT pSetup; // the setup data
PBYTE pParam; // the param data
BOOLEAN fTransactionNameInUnicode = FALSE;
PSMB_EXCHANGE pExchange = (PSMB_EXCHANGE)pTransactExchange;
PVOID pActualPrimaryRequestSmbHeader;
PSMB_HEADER pPrimaryRequestSmbHeader;
PSMBCEDB_SERVER_ENTRY pServerEntry;
PAGED_CODE();
ASSERT(pTransactExchange->Type == TRANSACT_EXCHANGE);
pTransactExchange->RxContext = RxContext;
pServerEntry = SmbCeGetExchangeServerEntry(pExchange);
{
PMRXSMB_RX_CONTEXT pMRxSmbContext = MRxSmbGetMinirdrContext(RxContext);
pMRxSmbContext->pExchange = (PSMB_EXCHANGE)pTransactExchange;
}
ASSERT(pSendParameters != NULL);
if (pSendParameters != NULL) {
pSendDataMdl = pSendParameters->pDataMdl;
pSendParamBuffer = pSendParameters->pParam;
SendParamBufferSize = pSendParameters->ParamLength;
pSendParamMdl = pSendParameters->pParamMdl;
pSendSetupBuffer = pSendParameters->pSetup;
SendSetupBufferSize = pSendParameters->SetupLength;
SendDataBufferSize = pSendParameters->DataLength;
ASSERT( !((pSendDataMdl == NULL)&&(SendDataBufferSize!=0)) );
RxDbgTrace( 0, Dbg, ("SmbTransactExchangeInitialize: at the top pbuf/psize/dsize=%08lx/%08lx\n"
,pSendParamBuffer,SendParamBufferSize,SendDataBufferSize));
} else {
Status = STATUS_INVALID_PARAMETER;
RxDbgTrace( 0, Dbg, ("SmbTransactExchangeInitialize: Invalid Parameters\n",Status));
return Status;
}
if (pReceiveParameters != NULL) {
pReceiveDataMdl = pReceiveParameters->pDataMdl;
pReceiveParamMdl = pReceiveParameters->pParamMdl;
pReceiveSetupMdl = pReceiveParameters->pSetupMdl;
ReceiveDataBufferSize = ((pReceiveDataMdl != NULL) ? MmGetMdlByteCount(pReceiveDataMdl) : 0);
ASSERT (ReceiveDataBufferSize==pReceiveParameters->DataLength);
ReceiveParamBufferSize = ((pReceiveParamMdl != NULL) ? MmGetMdlByteCount(pReceiveParamMdl) : 0);
ReceiveSetupBufferSize = ((pReceiveSetupMdl != NULL) ? MmGetMdlByteCount(pReceiveSetupMdl) : 0);
} else {
pReceiveDataMdl = pReceiveParamMdl = pReceiveSetupMdl = NULL;
ReceiveDataBufferSize = ReceiveParamBufferSize = ReceiveDataBufferSize = 0;
}
MaxSmbBufferSize = MIN (pServerEntry->Server.MaximumBufferSize,
pOptions->MaximumTransmitSmbBufferSize);
pTransactExchange->MaximumTransmitSmbBufferSize = MaxSmbBufferSize;
//CODE.IMPROVEMENT this switch should be replace by four ifs each testing for the right df-flag....
// Ensure that the SMB dialect supports the exchange capability.
switch (pServerEntry->Server.Dialect) {
case NTLANMAN_DIALECT:
{
if (!FlagOn(pOptions->Flags,SMB_XACT_FLAGS_MAILSLOT_OPERATION) &&
FlagOn(pServerEntry->Server.DialectFlags,DF_UNICODE)) {
fTransactionNameInUnicode = TRUE;
}
}
break;
case LANMAN10_DIALECT:
case WFW10_DIALECT:
{
// these guys only support transact...not T2 or NT. look for the name.....
if (pOptions->pTransactionName == NULL) {
RxDbgTrace( 0, Dbg, ("SmbTransactExchangeInitialize: Server Dialect does not support nameless transactions\n"));
return STATUS_NOT_SUPPORTED;
}
}
//no break intentional........
case LANMAN12_DIALECT:
case LANMAN21_DIALECT:
{
// The NT_TRANSACT SMB is supported by NT servers only. Ensure that no attempt is being made
// to send an NT_TRANSACT SMB to a non NT server aka downlevel
if (pOptions->NtTransactFunction != 0) {
RxDbgTrace( 0, Dbg, ("SmbTransactExchangeInitialize: Server Dialect does not support transactions\n"));
return STATUS_NOT_SUPPORTED;
}
fTransactionNameInUnicode = FALSE;
}
break;
default:
RxDbgTrace( 0, Dbg, ("SmbTransactExchangeInitialize: Server Dialect does not support transactions\n"));
return STATUS_NOT_SUPPORTED;
}
PrimaryRequestSmbSize = sizeof(SMB_HEADER) + SendSetupBufferSize;
// Ensure that the parameter sizes are all valid. The parameter and the data buffer
// must be less than the maximum size to begin with.
if ( pOptions->NtTransactFunction == 0) {
if ((SendParamBufferSize > SMB_TRANSACT_MAXIMUM_PARAMETER_SIZE) ||
(ReceiveParamBufferSize > SMB_TRANSACT_MAXIMUM_PARAMETER_SIZE) ||
(SendDataBufferSize > SMB_TRANSACT_MAXIMUM_DATA_SIZE) ||
(ReceiveDataBufferSize > SMB_TRANSACT_MAXIMUM_DATA_SIZE)) {
RxDbgTrace( 0, Dbg, ("SmbTransactExchangeInitialize: Parameters exceed maximum value\n"));
return STATUS_INVALID_PARAMETER;
}
PrimaryRequestSmbSize += sizeof(REQ_TRANSACTION);
// In all cases the name is sent as a UNICODE string if the appropriate capability is
// supported. The only exception to this rule is for mail slots for which the name is
// always transmitted as an ANSI string. Account for the null character as well in the
// transaction name length.
if (pOptions->pTransactionName != NULL) {
if (!fTransactionNameInUnicode) {
pTransactExchange->TransactionNameLength = RtlUnicodeStringToAnsiSize(pOptions->pTransactionName);
} else {
pTransactExchange->TransactionNameLength = pOptions->pTransactionName->Length + sizeof(WCHAR);
PrimaryRequestSmbSize += (ULONG)((PBYTE)ALIGN_SMB_WSTR(PrimaryRequestSmbSize)
- (PBYTE)(ULONG_PTR)PrimaryRequestSmbSize);
}
SmbCommand = SMB_COM_TRANSACTION;
} else {
// SMB protocol requires that a single NULL byte be sent as part of all
// TRANSACT2 transactions.
pTransactExchange->TransactionNameLength = 1;
SmbCommand = SMB_COM_TRANSACTION2;
}
PrimaryRequestSmbSize += pTransactExchange->TransactionNameLength;
} else {
PrimaryRequestSmbSize += sizeof(REQ_NT_TRANSACTION);
SmbCommand = SMB_COM_NT_TRANSACT;
pTransactExchange->TransactionNameLength = 0;
}
// The header, setup bytes and the name if specified must be part of the primary
// request SMB for a transaction to be successful. The secondary requests have no
// provision for sending setup/name.
if (PrimaryRequestSmbSize > MaxSmbBufferSize) {
RxDbgTrace( 0, Dbg, ("SmbTransactExchangeInitialize: Primary request + setup exceeds maximum buffer size\n"));
return STATUS_INVALID_PARAMETER;
}
// Include the byte count size and then align the size to a DWORD boundary.
PrimaryRequestSmbSize = ROUND_UP_COUNT(PrimaryRequestSmbSize+sizeof(USHORT),ALIGN_DWORD);
// Try to allocate for the param buffer as well if possible. The additional DWORD
// takes into account the worst case of alignment padding required.
//if ( (PrimaryRequestSmbSize + SendParamBufferSize + sizeof(DWORD)) > MaxSmbBufferSize)
if ((SendParamBufferSize!=0)
&& (((PrimaryRequestSmbSize + SendParamBufferSize) > MaxSmbBufferSize)
|| (DONTSUBSUME_PARAMS)) ){
// The param will spill over to a secondary request. Do not attempt to over
// allocate the primary request. if we can't subsume the params, then we'll need an MDL
// to partial from.
RxDbgTrace( 0, Dbg, ("SmbTransactExchangeInitialize: cannot subsume params\n"));
pTransactExchange->fParamsSubsumedInPrimaryRequest = FALSE;
pSendParamMdl = RxAllocateMdl(pSendParamBuffer,SendParamBufferSize);
if (pSendParamMdl == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
} else {
RxDbgTrace( 0, Dbg, ("SmbTransactExchangeInitialize: BIGPARAMMDL %08lx\n",pSendParamMdl));
RxProbeAndLockPages(pSendParamMdl,KernelMode,IoModifyAccess,Status);
if ((Status != RX_MAP_STATUS(SUCCESS))) {
IoFreeMdl(pSendParamMdl);
} else {
if (MmGetSystemAddressForMdlSafe(pSendParamMdl,LowPagePriority) == NULL) { //map it
Status = STATUS_INSUFFICIENT_RESOURCES;
}
pSendParameters->pParamMdl = pSendParamMdl; // save it away
}
}
} else {
PrimaryRequestSmbSize = ROUND_UP_COUNT(PrimaryRequestSmbSize+SendParamBufferSize,ALIGN_DWORD);
// Update the transact exchange to reflect the fact that no separate param MDL is
// required.
pTransactExchange->fParamsSubsumedInPrimaryRequest = TRUE;
}
//CODE.IMPROVEMENT this should be replaced by a call to get a smbbuf as in OrdExchg
pActualPrimaryRequestSmbHeader = (PSMB_HEADER)RxAllocatePoolWithTag(
PagedPool,
(PrimaryRequestSmbSize + 4 + TRANSPORT_HEADER_SIZE),
MRXSMB_XACT_POOLTAG); //up to 4 pad bytes
if (pActualPrimaryRequestSmbHeader == NULL) {
RxDbgTrace( 0, Dbg, ("SmbTransactExchangeInitialize: Cannot allocate primary request SMB\n"));
Status = STATUS_INSUFFICIENT_RESOURCES;
}
else {
(PCHAR) pPrimaryRequestSmbHeader =
(PCHAR) pActualPrimaryRequestSmbHeader + TRANSPORT_HEADER_SIZE;
}
if (Status == STATUS_SUCCESS) {
switch (SmbCommand) {
case SMB_COM_TRANSACTION :
case SMB_COM_TRANSACTION2:
{
PREQ_TRANSACTION pTransactRequest;
pTransactRequest = (PREQ_TRANSACTION)(pPrimaryRequestSmbHeader + 1);
pTransactRequest->WordCount = (UCHAR)(14 + (SendSetupBufferSize/sizeof(USHORT)));
SmbPutUshort(
&pTransactRequest->TotalParameterCount,
(USHORT)SendParamBufferSize);
SmbPutUshort(
&pTransactRequest->TotalDataCount,
(USHORT)SendDataBufferSize);
SmbPutUshort(
&pTransactRequest->MaxParameterCount,
(USHORT)ReceiveParamBufferSize);
SmbPutUshort(
&pTransactRequest->MaxDataCount,
(USHORT)ReceiveDataBufferSize);
pTransactRequest->MaxSetupCount = (UCHAR)(ReceiveSetupBufferSize/sizeof(USHORT));
pTransactRequest->Reserved = 0;
pTransactRequest->Reserved3 = 0;
SmbPutUshort(&pTransactRequest->Reserved2, 0);
SmbPutUshort( &pTransactRequest->Flags, pOptions->Flags&~SMB_XACT_INTERNAL_FLAGS_MASK );
pTransactRequest->SetupCount = (UCHAR)(SendSetupBufferSize/sizeof(USHORT));
SmbPutUlong(&pTransactRequest->Timeout, pOptions->TimeoutIntervalInMilliSeconds);
pSetup = (PUSHORT)pTransactRequest->Buffer;
// Copy the transact name and align the buffer if required.
if (pOptions->pTransactionName != NULL) {
PBYTE pName;
ULONG TransactionNameLength = pTransactExchange->TransactionNameLength;
// Set the name field in the SMB.
pName = (PBYTE)pSetup +
SendSetupBufferSize +
sizeof(USHORT); // account for the bcc field
ASSERT(SmbCommand == SMB_COM_TRANSACTION);
RxDbgTrace( 0, Dbg, ("SmbTransactExchangeInitialize: TransactionName(Length %ld) %ws\n",
TransactionNameLength,
pOptions->pTransactionName->Buffer));
if (fTransactionNameInUnicode) {
pName = ALIGN_SMB_WSTR(pName);
Status = SmbPutUnicodeString(&pName,
pOptions->pTransactionName,
&TransactionNameLength);
} else {
Status = SmbPutUnicodeStringAsOemString(&pName,
pOptions->pTransactionName,
&TransactionNameLength);
}
}
pParam = (PBYTE)pSetup +
SendSetupBufferSize +
sizeof(USHORT) + // the bcc field
pTransactExchange->TransactionNameLength;
pParam = ROUND_UP_POINTER(pParam, ALIGN_DWORD);
}
break;
case SMB_COM_NT_TRANSACT:
{
PREQ_NT_TRANSACTION pNtTransactRequest;
pNtTransactRequest = (PREQ_NT_TRANSACTION)(pPrimaryRequestSmbHeader + 1);
pNtTransactRequest->WordCount = (UCHAR)(19 + (SendSetupBufferSize/sizeof(USHORT)));
SmbPutUlong( &pNtTransactRequest->TotalParameterCount, SendParamBufferSize);
SmbPutUlong( &pNtTransactRequest->TotalDataCount, SendDataBufferSize);
SmbPutUlong( &pNtTransactRequest->MaxParameterCount, ReceiveParamBufferSize);
SmbPutUlong( &pNtTransactRequest->MaxDataCount, ReceiveDataBufferSize);
RxDbgTrace( 0, Dbg, ("SmbTransactExchangeInitialize: init for NT_T (p,d,mp,md) %d %d %d %d\n",
pNtTransactRequest->TotalParameterCount, pNtTransactRequest->TotalDataCount,
pNtTransactRequest->MaxParameterCount, pNtTransactRequest->MaxDataCount));
pNtTransactRequest->MaxSetupCount = (UCHAR)(ReceiveSetupBufferSize / sizeof(USHORT));
SmbPutUshort( &pNtTransactRequest->Flags, pOptions->Flags&~SMB_XACT_INTERNAL_FLAGS_MASK );
SmbPutUshort( &pNtTransactRequest->Function, pOptions->NtTransactFunction );
pNtTransactRequest->SetupCount = (UCHAR)(SendSetupBufferSize/sizeof(USHORT));
pSetup = (PUSHORT)pNtTransactRequest->Buffer;
pParam = (PBYTE)pSetup +
SendSetupBufferSize +
sizeof(USHORT); // the bcc field
pParam = ROUND_UP_POINTER(pParam, ALIGN_DWORD);
}
break;
default:
ASSERT(!"Valid Smb Command Type for Transact exchange");
Status = STATUS_INVALID_PARAMETER;
}
}
if (Status == STATUS_SUCCESS) {
// All related initialization of a transaction exchange has been
// completed. At this point the transact exchange assumes ownership
// of the various buffers ( specified as MDLs ) in the receive and
// send parameters. It will get rid of them during finalization
// of the exchange. In order to ensure that the caller does not
// attempt to free any of these buffers they are reset in the
// receive/send parameters.
// Copy the setup data
RtlCopyMemory(pSetup,pSendSetupBuffer,SendSetupBufferSize);
if (pTransactExchange->fParamsSubsumedInPrimaryRequest) {
RxDbgTrace( 0, Dbg, ("SmbTransactExchangeInitialize: subsuming where/size=%08lx/%08lx\n"
,pSendParamBuffer,SendParamBufferSize));
RtlCopyMemory(pParam,pSendParamBuffer,SendParamBufferSize);
}
// Initialize the transact exchange.
pTransactExchange->Status = STATUS_MORE_PROCESSING_REQUIRED;
pTransactExchange->Mid = 0;
pTransactExchange->TransactSmbCommand = SmbCommand;
pTransactExchange->pActualPrimaryRequestSmbHeader = pActualPrimaryRequestSmbHeader;
pTransactExchange->pPrimaryRequestSmbHeader = pPrimaryRequestSmbHeader;
pTransactExchange->PrimaryRequestSmbSize = PrimaryRequestSmbSize;
pTransactExchange->pSendDataMdl = pSendDataMdl;
pTransactExchange->SendDataBufferSize = SendDataBufferSize;
pTransactExchange->pReceiveDataMdl = pReceiveDataMdl;
pTransactExchange->ReceiveDataBufferSize = ReceiveDataBufferSize;
pTransactExchange->DataBytesSent = 0;
pTransactExchange->DataBytesReceived = 0;
pTransactExchange->pSendParamBuffer = pSendParamBuffer;
pTransactExchange->SendParamBufferSize = SendParamBufferSize;
pTransactExchange->pSendParamMdl = pSendParamMdl;
pTransactExchange->pReceiveParamMdl = pReceiveParamMdl;
pTransactExchange->ReceiveParamBufferSize = ReceiveParamBufferSize;
pTransactExchange->ParamBytesSent = 0;
pTransactExchange->ParamBytesReceived = 0;
pTransactExchange->pReceiveSetupMdl = pReceiveSetupMdl;
pTransactExchange->ReceiveSetupBufferSize = ReceiveSetupBufferSize;
pTransactExchange->SetupBytesReceived = 0;
pTransactExchange->NtTransactFunction = pOptions->NtTransactFunction;
pTransactExchange->Flags = pOptions->Flags;
if ((capFobx != NULL) &&
BooleanFlagOn(capFobx->Flags,FOBX_FLAG_DFS_OPEN)) {
pTransactExchange->Flags |= SMB_XACT_FLAGS_DFS_AWARE;
} else if (RxContext->MajorFunction == IRP_MJ_CREATE) {
PMRX_NET_ROOT pNetRoot = RxContext->pFcb->pNetRoot;
if (FlagOn(pNetRoot->Flags,NETROOT_FLAG_DFS_AWARE_NETROOT) &&
RxContext->Create.NtCreateParameters.DfsContext == UIntToPtr(DFS_OPEN_CONTEXT)) {
pTransactExchange->Flags |= SMB_XACT_FLAGS_DFS_AWARE;
}
}
pTransactExchange->pResumptionContext = pResumptionContext;
// Reset the Send and Receive parameter data structures to transfer
// the ownership of the MDLs to the exchange.
if (pSendParameters->Flags & SMB_XACT_FLAGS_CALLERS_SENDDATAMDL) {
pTransactExchange->Flags |= SMB_XACT_FLAGS_CALLERS_SENDDATAMDL;
}
RtlZeroMemory(
pSendParameters,
sizeof(SMB_TRANSACTION_SEND_PARAMETERS));
RtlZeroMemory(
pReceiveParameters,
sizeof(SMB_TRANSACTION_RECEIVE_PARAMETERS));
}
if (Status != STATUS_SUCCESS) {
// Clean up the memory allocated in an effort to initialize the transact exchange
if (pActualPrimaryRequestSmbHeader) {
RxFreePool(pActualPrimaryRequestSmbHeader);
}
} else {
PMRXSMB_RX_CONTEXT pMRxSmbContext = MRxSmbGetMinirdrContext(RxContext);
pMRxSmbContext->pExchange = (PSMB_EXCHANGE)pTransactExchange;
if (!FlagOn(pTransactExchange->Flags,SMB_XACT_FLAGS_MAILSLOT_OPERATION)) {
// No reconnection attempts are allowed in transact exchanges except mailslot
pTransactExchange->SmbCeFlags &= ~SMBCE_EXCHANGE_ATTEMPT_RECONNECTS;
}
if (pOptions->Flags & SMB_XACT_FLAGS_INDEFINITE_DELAY_IN_RESPONSE ) {
pTransactExchange->SmbCeFlags |= SMBCE_EXCHANGE_INDEFINITE_DELAY_IN_RESPONSE;
}
}
return Status;
}
NTSTATUS
SmbTransactExchangeFinalize(
PSMB_EXCHANGE pExchange,
BOOLEAN *pPostFinalize)
/*++
Routine Description:
This routine finalizes the transact exchange. It resumes the RDBSS by invoking
the call back and discards the exchange
Arguments:
pExchange - the exchange instance
CurrentIrql - the interrupt request level
pPostFinalize - set to TRUE if the request is to be posted
Return Value:
RXSTATUS - The return status for the operation
--*/
{
PSMB_TRANSACT_EXCHANGE pTransactExchange;
PSMB_TRANSACTION_RESUMPTION_CONTEXT pResumptionContext;
LONG References;
PSMBCEDB_SERVER_ENTRY pServerEntry = SmbCeGetExchangeServerEntry(pExchange);
ASSERT(pExchange->Type == TRANSACT_EXCHANGE);
pTransactExchange = (PSMB_TRANSACT_EXCHANGE)pExchange;
RxLog((">>>XE %lx",pTransactExchange));
SmbLog(LOG,
SmbTransactExchangeFinalize,
LOGPTR(pTransactExchange));
// Disassociate the MID associated with the exchange
if (pExchange->SmbCeFlags & SMBCE_EXCHANGE_MID_VALID) {
SmbCeDissociateMidFromExchange(pExchange->SmbCeContext.pServerEntry,pExchange);
}
if ((pTransactExchange->ReceiveParamBufferSize > 0) &&
(pTransactExchange->ReceiveParamBufferSize !=
pTransactExchange->ParamBytesReceived)) {
RxDbgTrace(0, Dbg,
("SmbCeTransactExchangeFinalize: Param Bytes Receive error ... expected(%ld) received(%ld)\n",
pTransactExchange->ReceiveParamBufferSize, pTransactExchange->ParamBytesReceived
));
}
if ((pTransactExchange->ReceiveDataBufferSize > 0) &&
(pTransactExchange->ReceiveDataBufferSize !=
pTransactExchange->DataBytesReceived)) {
RxDbgTrace(0, Dbg,
("SmbCeTransactExchangeFinalize: Data Bytes Receive error ... expected(%ld) received(%ld)\n",
pTransactExchange->ReceiveDataBufferSize, pTransactExchange->DataBytesReceived
));
}
if (RxShouldPostCompletion()) {
RxPostToWorkerThread(
MRxSmbDeviceObject,
CriticalWorkQueue,
&pExchange->WorkQueueItem,
SmbCeDiscardTransactExchange,
pTransactExchange);
} else {
SmbCeDiscardTransactExchange(pTransactExchange);
}
return STATUS_SUCCESS;
UNREFERENCED_PARAMETER(pPostFinalize);
}
NTSTATUS
SmbTransactAccrueAndValidateFormatData(
IN struct _SMB_TRANSACT_EXCHANGE *pTransactExchange, // The exchange instance
IN PSMB_HEADER pSmbHeader,
IN ULONG BytesIndicated,
OUT PSMB_TRANSACT_RESP_FORMAT_DESCRIPTION Format
)
/*++
Routine Description:
This is the recieve indication handling routine for net root construction exchanges
Arguments:
Return Value:
RXSTATUS - The return status for the operation
STATUS_SUCCESS -- all the data was indicated and it was valid
STATUS_INVALID_NETWORK_RESPONSE -- something about the format parameters is untoward.
Notes:
This routine is called at DPC level.
--*/
{
NTSTATUS Status = RX_MAP_STATUS(SUCCESS);
PRESP_TRANSACTION pTransactResponse = (PRESP_TRANSACTION)(pSmbHeader+1);
PBYTE WordCountPtr;
UCHAR WordCount;
PBYTE ByteCountPtr;
USHORT ByteCount;
RtlZeroMemory(Format,sizeof(*Format));
Format->WordCount = WordCount = pTransactResponse->WordCount;
ByteCountPtr = (&pTransactResponse->WordCount)+1+(sizeof(USHORT)*WordCount);
if (((ULONG)(ByteCountPtr+sizeof(USHORT)-((PBYTE)pSmbHeader)))>BytesIndicated) {
ByteCount = SmbGetUshort(ByteCountPtr);
DbgPrint("ExtraTransactBytes wc,bcp,bc,smbh %lx,%lx,%lx,%lx\n",
WordCount,ByteCountPtr,ByteCount,pSmbHeader);
return STATUS_INVALID_NETWORK_RESPONSE;
}
Format->ByteCount = ByteCount = SmbGetUshort(ByteCountPtr);
Format->ApparentMsgLength = (ULONG)((ByteCountPtr+sizeof(USHORT)-((PBYTE)pSmbHeader))+ByteCount);
if (WordCount==0) {
return(STATUS_SUCCESS);
}
#if 0
ULONG WordCount;
ULONG TotalParameterCount;
ULONG TotalDataCount;
ULONG ParameterCount;
ULONG ParameterOffset;
ULONG ParameterDisplacement;
ULONG DataCount;
ULONG DataOffset;
ULONG DataDisplacement;
ULONG ByteCount;
ULONG ApparentMsgLength;
#endif
// where is the validation: stuff that should be checked is
// a) that the values fit in the params and data spcified
// b) that we are not over the limit on bytes received
// c) that the response that we are receiving is valid for the command that we sent
// we didn't compute ApparentMsgLength.......
// The validation has not been done here. We rely on Transact Receive routine to detect the invalid response.
//CODE.IMPROVEMENT we could save some space with an unstuffer here........... but
// we'd have to amortize the cost over a lot more places. we should look on RISC machines to see if they turn it
// into a single copy.
switch (pSmbHeader->Command) {
case SMB_COM_TRANSACTION2:
case SMB_COM_TRANSACTION:
case SMB_COM_TRANSACTION_SECONDARY:
case SMB_COM_TRANSACTION2_SECONDARY:
{
Format->TotalParameterCount = SmbGetUshort(&pTransactResponse->TotalParameterCount);
Format->TotalDataCount = SmbGetUshort(&pTransactResponse->TotalDataCount);
Format->ParameterCount = SmbGetUshort(&pTransactResponse->ParameterCount);
Format->ParameterOffset = SmbGetUshort(&pTransactResponse->ParameterOffset);
Format->ParameterDisplacement = SmbGetUshort(&pTransactResponse->ParameterDisplacement);
Format->DataCount = SmbGetUshort(&pTransactResponse->DataCount);
Format->DataOffset = SmbGetUshort(&pTransactResponse->DataOffset);
Format->DataDisplacement = SmbGetUshort(&pTransactResponse->DataDisplacement);
}
break;
case SMB_COM_NT_TRANSACT:
case SMB_COM_NT_TRANSACT_SECONDARY:
{
PRESP_NT_TRANSACTION pNtTransactResponse;
pNtTransactResponse = (PRESP_NT_TRANSACTION)(pTransactResponse);
Format->TotalParameterCount = SmbGetUlong(&pNtTransactResponse->TotalParameterCount);
Format->TotalDataCount = SmbGetUlong(&pNtTransactResponse->TotalDataCount);
Format->ParameterCount = SmbGetUlong(&pNtTransactResponse->ParameterCount);
Format->ParameterOffset = SmbGetUlong(&pNtTransactResponse->ParameterOffset);
Format->ParameterDisplacement = SmbGetUlong(&pNtTransactResponse->ParameterDisplacement);
Format->DataCount = SmbGetUlong(&pNtTransactResponse->DataCount);
Format->DataOffset = SmbGetUlong(&pNtTransactResponse->DataOffset);
Format->DataDisplacement = SmbGetUlong(&pNtTransactResponse->DataDisplacement);
}
break;
default:
// Bug Check
return STATUS_INVALID_NETWORK_RESPONSE;
}
//do this here so we can use it as validation criterion
pTransactExchange->ParameterBytesSeen += Format->ParameterCount;
pTransactExchange->DataBytesSeen += Format->DataCount;
return Status;
}
NTSTATUS
ParseTransactResponse(
IN struct _SMB_TRANSACT_EXCHANGE *pTransactExchange, // The exchange instance
IN PSMB_TRANSACT_RESP_FORMAT_DESCRIPTION Format,
IN ULONG BytesIndicated,
IN ULONG BytesAvailable,
OUT ULONG *pBytesTaken,
IN PSMB_HEADER pSmbHeader,
OUT PMDL *pCopyRequestMdlPointer,
OUT PULONG pCopyRequestSize)
/*++
Routine Description:
This is the recieve indication handling routine for net root construction exchanges
Arguments:
pTransactExchange - the exchange instance
BytesIndicated - the number of bytes indicated
Bytes Available - the number of bytes available
pBytesTaken - the number of bytes consumed
pSmbHeader - the byte buffer
pCopyRequestMdlPointer - the buffer into which the remaining data is to be copied.
pCopyRequestSize - the buffer size.
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 all the data was not indicated
STATUS_SUCCESS -- all the data was indicated and it was valid
STATUS_* -- They indicate an error which would normally leads to the abortion of the
exchange.
Notes:
This routine is called at DPC level.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
ULONG ParamBytesInResponse = 0;
ULONG ParamOffsetInResponse = 0;
ULONG DataBytesInResponse = 0;
ULONG DataOffsetInResponse = 0;
ULONG PaddingLength = 0;
PMDL pFirstMdlInCopyDataRequestChain = NULL;
PMDL pLastMdlInCopyDataRequestChain = NULL;
PMDL pParamMdl = NULL;
PMDL pPaddingMdl = NULL;
PMDL pDataMdl = NULL;
PBYTE pParamStartAddress;
PBYTE pDataStartAddress;
PBYTE pSmbBuffer = (PBYTE)pSmbHeader;
switch (pSmbHeader->Command) {
case SMB_COM_TRANSACTION2:
case SMB_COM_TRANSACTION:
case SMB_COM_TRANSACTION_SECONDARY:
case SMB_COM_TRANSACTION2_SECONDARY:
{
PRESP_TRANSACTION pTransactResponse;
pTransactResponse = (PRESP_TRANSACTION)(pSmbBuffer + *pBytesTaken);
*pBytesTaken = *pBytesTaken + sizeof(RESP_TRANSACTION);
}
break;
case SMB_COM_NT_TRANSACT:
case SMB_COM_NT_TRANSACT_SECONDARY:
{
PRESP_NT_TRANSACTION pNtTransactResponse;
pNtTransactResponse = (PRESP_NT_TRANSACTION)(pSmbBuffer + *pBytesTaken);
*pBytesTaken = *pBytesTaken + sizeof(RESP_NT_TRANSACTION);
}
break;
default:
// Bug Check
ASSERT(!"Valid SMB command in Transaction response");
return STATUS_INVALID_NETWORK_RESPONSE;
}
#if 0
ULONG WordCount;
ULONG TotalParameterCount;
ULONG TotalDataCount;
ULONG ParameterCount;
ULONG ParameterOffset;
ULONG ParameterDisplacement;
ULONG DataCount;
ULONG DataOffset;
ULONG DataDisplacement;
ULONG ByteCount;
ULONG ApparentMsgLength;
#endif
ParamBytesInResponse = Format->ParameterCount;
ParamOffsetInResponse = Format->ParameterOffset;
DataBytesInResponse = Format->DataCount;
DataOffsetInResponse = Format->DataOffset;
if (ParamBytesInResponse > 0) {
ASSERT(pTransactExchange->pReceiveParamMdl != NULL);
pParamStartAddress = (PBYTE)MmGetSystemAddressForMdlSafe(pTransactExchange->pReceiveParamMdl,LowPagePriority);
if (pParamStartAddress == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
} else {
pParamStartAddress += Format->ParameterDisplacement;
}
} else {
pParamStartAddress = NULL;
}
if (DataBytesInResponse > 0) {
ASSERT(pTransactExchange->pReceiveDataMdl != NULL);
pDataStartAddress = (PBYTE)MmGetSystemAddressForMdlSafe(pTransactExchange->pReceiveDataMdl,LowPagePriority);
if (pDataStartAddress == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
} else {
pDataStartAddress += Format->DataDisplacement;
}
} else {
pDataStartAddress = NULL;
}
RxDbgTrace( 0, Dbg, ("ParseTransactResponse: Param Bytes(%ld) Param Offset (%ld) Data Bytes (%ld) Data Offset(%ld)\n",
ParamBytesInResponse,
ParamOffsetInResponse,
DataBytesInResponse,
DataOffsetInResponse));
// If either the param bytes or the data bytes have already been indicated, copy
// them into the respective buffers and trim the size of the MDL for the copy
// data request.
if (ParamOffsetInResponse <= BytesIndicated) {
*pBytesTaken = ParamOffsetInResponse;
if (ParamBytesInResponse > 0) {
ULONG ParamBytesIndicated = MIN(
ParamBytesInResponse,
BytesIndicated - ParamOffsetInResponse);
RxDbgTrace( 0, Dbg, ("ParseTransactResponse: Param Bytes indicated %ld\n",ParamBytesIndicated));
RtlCopyMemory(
pParamStartAddress,
(pSmbBuffer + ParamOffsetInResponse),
ParamBytesIndicated);
*pBytesTaken = *pBytesTaken + ParamBytesIndicated;
pParamStartAddress += ParamBytesIndicated;
ParamBytesInResponse -= ParamBytesIndicated;
ParamOffsetInResponse += ParamBytesIndicated;
pTransactExchange->ParamBytesReceived += ParamBytesIndicated;
}
}
if ( (DataOffsetInResponse <= BytesIndicated) &&
(DataOffsetInResponse > 0) ) {
*pBytesTaken = DataOffsetInResponse; //you have to move up EVEN IF NO BYTES!!!!!
if (DataBytesInResponse > 0) {
ULONG DataBytesIndicated = MIN(
DataBytesInResponse,
BytesIndicated - DataOffsetInResponse);
RxDbgTrace( 0, Dbg, ("ParseTransactResponse: Data Bytes indicated %ld\n",DataBytesIndicated));
RtlCopyMemory(
pDataStartAddress,
(pSmbBuffer + DataOffsetInResponse),
DataBytesIndicated);
*pBytesTaken = *pBytesTaken + DataBytesIndicated;
pDataStartAddress += DataBytesIndicated;
DataBytesInResponse -= DataBytesIndicated;
DataOffsetInResponse += DataBytesIndicated;
pTransactExchange->DataBytesReceived += DataBytesIndicated;
}
}
RxDbgTrace( 0, Dbg, ("ParseTransactResponse: Made it past the copies......... \n"));
if (ParamBytesInResponse > 0) {
// There are more param bytes that have not been indicated. Set up an MDL
// to copy them over.
RxDbgTrace( 0, Dbg, ("ParseTransactResponse: Posting Copy request for Param Bytes %ld\n",ParamBytesInResponse));
pParamMdl = RxAllocateMdl(
((PBYTE)MmGetMdlVirtualAddress(pTransactExchange->pReceiveParamMdl)
+ pTransactExchange->ParamBytesReceived),
ParamBytesInResponse);
if (pParamMdl != NULL) {
IoBuildPartialMdl(
pTransactExchange->pReceiveParamMdl,
pParamMdl,
((PBYTE)MmGetMdlVirtualAddress(pTransactExchange->pReceiveParamMdl)
+ pTransactExchange->ParamBytesReceived),
ParamBytesInResponse);
pFirstMdlInCopyDataRequestChain = pParamMdl;
pLastMdlInCopyDataRequestChain = pParamMdl;
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
pTransactExchange->ParamBytesReceived += ParamBytesInResponse;
}
if ((Status == RX_MAP_STATUS(SUCCESS)) &&
(DataBytesInResponse > 0)) {
RxDbgTrace( 0, Dbg, ("ParseTransactResponse: Posting Copy request for Data Bytes %ld\n",DataBytesInResponse));
// In certain cases a padding MDL needs to be inserted between the param and data portions
// of the response to consume the padding bytes sent by the server.
if ((ParamBytesInResponse > 0) &&
((PaddingLength = DataOffsetInResponse -
(ParamBytesInResponse + ParamOffsetInResponse)) > 0)) {
RxDbgTrace( 0, Dbg, ("ParseTransactResponse: Posting Copy request for padding bytes %ld\n",PaddingLength));
// There are some padding bytes present. Construct an MDL to consume them
//pPaddingMdl = RxAllocateMdl(&MRxSmb_pPaddingData,PaddingLength);
ASSERT(!"this doesn't work");
if (pPaddingMdl != NULL) {
if (pLastMdlInCopyDataRequestChain != NULL) {
pLastMdlInCopyDataRequestChain->Next = pPaddingMdl;
} else {
pFirstMdlInCopyDataRequestChain = pPaddingMdl;
}
pLastMdlInCopyDataRequestChain = pPaddingMdl;
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
}
// There are more data bytes which have not been indicated. Set up an MDL
// to copy them over.
if ((Status == RX_MAP_STATUS(SUCCESS))) {
if (pTransactExchange->pReceiveDataMdl->ByteCount >= DataBytesInResponse) {
pDataMdl = RxAllocateMdl(
((PBYTE)MmGetMdlVirtualAddress(pTransactExchange->pReceiveDataMdl)
+ pTransactExchange->DataBytesReceived),
DataBytesInResponse);
if (pDataMdl != NULL) {
IoBuildPartialMdl(
pTransactExchange->pReceiveDataMdl,
pDataMdl,
((PBYTE)MmGetMdlVirtualAddress(pTransactExchange->pReceiveDataMdl)
+ pTransactExchange->DataBytesReceived),
DataBytesInResponse);
if (pLastMdlInCopyDataRequestChain != NULL) {
pLastMdlInCopyDataRequestChain->Next = pDataMdl;
} else {
pFirstMdlInCopyDataRequestChain = pDataMdl;
}
pLastMdlInCopyDataRequestChain = pDataMdl;
pTransactExchange->DataBytesReceived += DataBytesInResponse;
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
} else {
Status = STATUS_INVALID_NETWORK_RESPONSE;
}
}
}
if ((Status != RX_MAP_STATUS(SUCCESS))) {
if (pDataMdl != NULL) {
IoFreeMdl(pDataMdl);
}
if (pPaddingMdl != NULL) {
IoFreeMdl(pPaddingMdl);
}
if (pParamMdl != NULL) {
IoFreeMdl(pParamMdl);
}
} else {
if (pFirstMdlInCopyDataRequestChain != NULL) {
ULONG MdlLength = ParamBytesInResponse+PaddingLength+DataBytesInResponse;
*pCopyRequestMdlPointer = pFirstMdlInCopyDataRequestChain;
*pCopyRequestSize = MdlLength;
RxDbgTrace( 0, Dbg, ("ParseTransactResponse: final mdl and copy size %08lx %08lx(%ld)\n",
pFirstMdlInCopyDataRequestChain,MdlLength,MdlLength));
IF_DEBUG {
PMDL imdl = pFirstMdlInCopyDataRequestChain;
ULONG mdllength = MdlLength;
mdllength -= MmGetMdlByteCount(imdl);
for (;;) {
if (!(imdl=imdl->Next)) break;
mdllength -= MmGetMdlByteCount(imdl);
}
ASSERT(mdllength==0);
}
InterlockedIncrement(&pTransactExchange->PendingCopyRequests);
Status = STATUS_MORE_PROCESSING_REQUIRED;
}
if ((pTransactExchange->ParamBytesReceived < pTransactExchange->ReceiveParamBufferSize) ||
(pTransactExchange->DataBytesReceived < pTransactExchange->ReceiveDataBufferSize)) {
NTSTATUS ReceiveStatus;
// The exchange has been successfully completed. Finalize it.
RxDbgTrace(0,Dbg,("ParseTransactResponse: Register for more responses\n"));
ReceiveStatus = SmbCeReceive((PSMB_EXCHANGE)pTransactExchange);
if (ReceiveStatus != STATUS_SUCCESS) {
// There was an error in registering the receive. Abandon the
// transaction.
Status = ReceiveStatus;
}
}
}
return Status;
UNREFERENCED_PARAMETER(BytesAvailable);
}
#if DBG
ULONG SmbSendBadSecondary = 0;
#endif
NTSTATUS
SendSecondaryRequests(PVOID pContext)
/*++
Routine Description:
This routine sends all the secondary requests associated with the transaction
Arguments:
pTransactExchange - the exchange instance
Return Value:
RXSTATUS - The return status for the operation
Notes:
--*/
{
PSMB_EXCHANGE pExchange = (PSMB_EXCHANGE)pContext;
PSMB_TRANSACT_EXCHANGE pTransactExchange = (PSMB_TRANSACT_EXCHANGE)pExchange;
NTSTATUS Status = STATUS_SUCCESS;
ULONG MaximumSmbBufferSize;
// The MDL's used in sending the primary request associated with the TRANSACT SMB
PMDL pPartialDataMdl = NULL;
PMDL pPartialParamMdl = NULL;
PMDL pPaddingMdl = NULL;
PMDL pSecondaryRequestSmbMdl = NULL;
PMDL pLastMdlInChain = NULL;
ULONG SecondaryRequestSmbSize = 0;
ULONG SmbLength;
ULONG PaddingLength;
ULONG ParamOffset,ParamDisplacement;
ULONG DataOffset,DataDisplacement;
ULONG ByteCountOffset;
USHORT ByteCount;
PUSHORT pByteCount;
ULONG ParamBytesToBeSent; // Param bytes to be sent per request
ULONG DataBytesToBeSent; // data bytes to be sent per request
ULONG SendParamBufferSize; // Total param bytes to be sent in secondary requests
ULONG SendDataBufferSize; // Total data bytes to be sent in secondary requests
PBYTE pSendParamStartAddress = NULL;
PBYTE pSendDataStartAddress = NULL;
PBYTE pOriginalParamBuffer = NULL;
PBYTE pOriginalDataBuffer = NULL;
ULONG TotalParamBytes,TotalDataBytes;
BOOLEAN ParamPartialMdlAlreadyUsed = FALSE;
BOOLEAN DataPartialMdlAlreadyUsed = FALSE;
PVOID pActualSecondaryRequestSmbHeader = NULL;
PSMB_HEADER pSecondaryRequestSmbHeader = NULL;
PAGED_CODE();
ASSERT(pTransactExchange->State == TRANSACT_EXCHANGE_RECEIVED_INTERIM_RESPONSE);
TotalParamBytes = pTransactExchange->SendParamBufferSize;
SendParamBufferSize = TotalParamBytes - pTransactExchange->ParamBytesSent;
TotalDataBytes = pTransactExchange->SendDataBufferSize;
SendDataBufferSize = TotalDataBytes - pTransactExchange->DataBytesSent;
ASSERT((SendParamBufferSize > 0) || (SendDataBufferSize > 0));
switch (pTransactExchange->TransactSmbCommand) {
case SMB_COM_TRANSACTION:
SecondaryRequestSmbSize = sizeof(SMB_HEADER) +
FIELD_OFFSET(REQ_TRANSACTION_SECONDARY,Buffer);
break;
case SMB_COM_TRANSACTION2:
//CODE.IMPROVEMENT.ASHAMED smb.h should containa REQ_TRANSACTION2_SECONDARY instead
// of this bogus comment about how the server can ignore it
SecondaryRequestSmbSize = sizeof(SMB_HEADER) +
FIELD_OFFSET(REQ_TRANSACTION_SECONDARY,Buffer)
+ sizeof(USHORT); //add in the extra word
break;
case SMB_COM_NT_TRANSACT:
SecondaryRequestSmbSize = sizeof(SMB_HEADER) +
FIELD_OFFSET(REQ_NT_TRANSACTION_SECONDARY,Buffer);
break;
default:
ASSERT(!"Valid Smb Command in transaction exchange");
Status = STATUS_TRANSACTION_ABORTED;
}
SecondaryRequestSmbSize = QuadAlign(SecondaryRequestSmbSize); //pad to quadword boundary
//CODE.IMPROVEMENT we could overallocate here....sometimes the copy would be faster
pActualSecondaryRequestSmbHeader = (PSMB_HEADER)
RxAllocatePoolWithTag(
NonPagedPool,
SecondaryRequestSmbSize + TRANSPORT_HEADER_SIZE,
MRXSMB_XACT_POOLTAG);
if ((Status == RX_MAP_STATUS(SUCCESS)) && pActualSecondaryRequestSmbHeader != NULL) {
(PCHAR) pSecondaryRequestSmbHeader =
(PCHAR) pActualSecondaryRequestSmbHeader + TRANSPORT_HEADER_SIZE;
// Initialize the SMB header ...
ASSERT(
((SMB_COM_TRANSACTION+1) == SMB_COM_TRANSACTION_SECONDARY)
&&((SMB_COM_TRANSACTION2+1)== SMB_COM_TRANSACTION2_SECONDARY)
&&((SMB_COM_NT_TRANSACT+1) == SMB_COM_NT_TRANSACT_SECONDARY)
);
Status = SmbTransactBuildHeader(
pTransactExchange, // the exchange instance
(UCHAR)(pTransactExchange->TransactSmbCommand+1), // the SMB command ..see the asserts above
pSecondaryRequestSmbHeader); // the SMB buffer
RxDbgTrace( 0, Dbg, ("SendSecondaryRequests: SmbCeBuildSmbHeader returned %lx\n",Status));
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
if ((Status == RX_MAP_STATUS(SUCCESS))) {
MaximumSmbBufferSize = pTransactExchange->MaximumTransmitSmbBufferSize;
// Ensure that the MDL's have been probed & locked. The new MDL's have been allocated.
// The partial MDL's are allocated to be large enough to span the maximum buffer
// length possible.
// Initialize the data related MDL's for the secondary request
if (SendDataBufferSize > 0) {
RxDbgTrace( 0, Dbg, ("SendSecondaryRequests: Data Bytes remaining %ld\n",SendDataBufferSize));
pOriginalDataBuffer = (PBYTE)MmGetMdlVirtualAddress(pTransactExchange->pSendDataMdl);
pSendDataStartAddress = pOriginalDataBuffer + pTransactExchange->DataBytesSent;
pPartialDataMdl = RxAllocateMdl(
0,
(MIN(pTransactExchange->SendDataBufferSize,
MaximumSmbBufferSize) +
PAGE_SIZE - 1));
if (pPartialDataMdl == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
}
// Initialize the parameter related MDL's for the secondary request
if ((SendParamBufferSize > 0) && (Status == RX_MAP_STATUS(SUCCESS))) {
RxDbgTrace( 0, Dbg, ("SendSecondaryRequests: Param Bytes remaining %ld\n",SendParamBufferSize));
pOriginalParamBuffer = (PBYTE)MmGetMdlVirtualAddress(pTransactExchange->pSendParamMdl);
pSendParamStartAddress = pOriginalParamBuffer + pTransactExchange->ParamBytesSent;
pPartialParamMdl = RxAllocateMdl(
0,
(MIN(pTransactExchange->SendParamBufferSize,
MaximumSmbBufferSize) +
PAGE_SIZE - 1));
//CODE.IMPROVEMENT we shouldn't allocate this if datasize==0
pPaddingMdl = RxAllocateMdl(0,(sizeof(DWORD) + PAGE_SIZE - 1));
if ((pPartialParamMdl == NULL) ||
(pPaddingMdl == NULL)) {
RxDbgTrace( 0, Dbg, ("SendSecondaryRequests: Error allocating param MDLS\n"));
Status = STATUS_INSUFFICIENT_RESOURCES;
}
}
// Initialize the secondary request SMB MDL
if ((Status == RX_MAP_STATUS(SUCCESS))) {
RxAllocateHeaderMdl(
pSecondaryRequestSmbHeader,
SecondaryRequestSmbSize,
pSecondaryRequestSmbMdl
);
if (pSecondaryRequestSmbMdl != NULL) {
RxProbeAndLockHeaderPages(
pSecondaryRequestSmbMdl,
KernelMode,
IoModifyAccess,
Status);
if ((Status != RX_MAP_STATUS(SUCCESS))) {
IoFreeMdl(pSecondaryRequestSmbMdl);
pSecondaryRequestSmbMdl = NULL;
} else {
if (MmGetSystemAddressForMdlSafe(pSecondaryRequestSmbMdl,LowPagePriority) == NULL) { //map it
Status = STATUS_INSUFFICIENT_RESOURCES;
}
}
} else {
RxDbgTrace( 0, Dbg, ("SendSecondaryRequests: Error allocating 2ndsmb MDL\n"));
Status = STATUS_INSUFFICIENT_RESOURCES;
}
}
}
while ((Status == RX_MAP_STATUS(SUCCESS)) &&
((SendParamBufferSize > 0) || (SendDataBufferSize > 0))) {
PaddingLength = 0;
DataBytesToBeSent = 0;
ParamBytesToBeSent = 0;
ParamDisplacement = 0;
pLastMdlInChain = pSecondaryRequestSmbMdl;
ParamOffset = DataOffset = SecondaryRequestSmbSize;
ParamBytesToBeSent = MIN((MaximumSmbBufferSize - ParamOffset),
SendParamBufferSize);
if (ParamBytesToBeSent > 0) {
// Form a MDL for the portion of the parameter buffer being transmitted
if (ParamPartialMdlAlreadyUsed) {
MmPrepareMdlForReuse(pPartialParamMdl);
}
ParamPartialMdlAlreadyUsed = TRUE;
IoBuildPartialMdl(
pTransactExchange->pSendParamMdl,
pPartialParamMdl,
pSendParamStartAddress,
ParamBytesToBeSent);
ParamDisplacement = (ULONG)(pSendParamStartAddress - pOriginalParamBuffer);
pSendParamStartAddress += ParamBytesToBeSent;
SendParamBufferSize -= ParamBytesToBeSent;
DataOffset += QuadAlign(ParamBytesToBeSent);
pLastMdlInChain->Next = pPartialParamMdl;
pLastMdlInChain = pPartialParamMdl;
} else {
// don't do this! the padding stuff uses it. you can set it later
// ParamOffset = 0;
}
if ((DataOffset < MaximumSmbBufferSize) && (SendDataBufferSize > 0) ) {
// There is room for data bytes to be sent
// Check if we need a padding MDL ....
PaddingLength = DataOffset - (ParamOffset + ParamBytesToBeSent);
if (PaddingLength > 0) {
RxDbgTrace( 0, Dbg, ("SmbCeTransactExchangeStart: Padding Length %ld\n",PaddingLength));
RxBuildPaddingPartialMdl(pPaddingMdl,PaddingLength);
pLastMdlInChain->Next = pPaddingMdl;
pLastMdlInChain = pPaddingMdl;
}
// Link the data buffer or portions of it if the size constraints are satisfied
DataBytesToBeSent = MIN((MaximumSmbBufferSize - DataOffset),
SendDataBufferSize);
ASSERT (DataBytesToBeSent > 0);
// Form a MDL for the portions of the data buffer being sent
if (DataPartialMdlAlreadyUsed) {
MmPrepareMdlForReuse(pPartialDataMdl);
}
DataPartialMdlAlreadyUsed = TRUE;
IoBuildPartialMdl(
pTransactExchange->pSendDataMdl,
pPartialDataMdl,
pSendDataStartAddress,
DataBytesToBeSent);
// chain the data MDL
pLastMdlInChain->Next = pPartialDataMdl;
pLastMdlInChain = pPartialDataMdl;
DataDisplacement = (ULONG)(pSendDataStartAddress - pOriginalDataBuffer);
pSendDataStartAddress += DataBytesToBeSent;
SendDataBufferSize -= DataBytesToBeSent;
} else {
DataOffset = DataDisplacement = 0;
DbgDoit(if (SmbSendBadSecondary){DataOffset = QuadAlign(ParamOffset + ParamBytesToBeSent);});
}
if (ParamBytesToBeSent == 0) {
ParamOffset = 0;
}
RxDbgTrace( 0, Dbg, ("SendSecondaryRequests: Secondary Request Param(%ld) padding(%ld) Data(%ld)\n",
ParamBytesToBeSent,
PaddingLength,
DataBytesToBeSent));
RxDbgTrace( 0, Dbg, ("SendSecondaryRequests: ParamO(%ld) DataO(%ld)\n",ParamOffset,DataOffset));
RxDbgTrace( 0, Dbg, ("SendSecondaryRequests: ParamD(%ld) DataD(%ld)\n",ParamDisplacement,DataDisplacement));
RxDbgTrace( 0, Dbg, ("SendSecondaryRequests: TotParam(%ld) TotData(%ld)\n",TotalParamBytes,TotalDataBytes));
// Update the secondary request buffer with the final sizes of the data/parameter etc.
switch (pTransactExchange->TransactSmbCommand) {
case SMB_COM_TRANSACTION:
case SMB_COM_TRANSACTION2:
{
PREQ_TRANSACTION_SECONDARY pTransactRequest;
//ASSERT(!"this has not been tested");
pTransactRequest = (PREQ_TRANSACTION_SECONDARY)(pSecondaryRequestSmbHeader + 1);
pTransactRequest->WordCount = 8; // Count of parameter words = 8
SmbPutUshort(&pTransactRequest->TotalParameterCount, (USHORT)TotalParamBytes); // Total parameter bytes being sent
SmbPutUshort(&pTransactRequest->TotalDataCount, (USHORT)TotalDataBytes); // Total data bytes being sent
SmbPutUshort(&pTransactRequest->ParameterCount, (USHORT)ParamBytesToBeSent); // Parameter bytes sent this buffer
SmbPutUshort(&pTransactRequest->ParameterOffset, (USHORT)ParamOffset); // Offset (from header start) to params
SmbPutUshort(&pTransactRequest->ParameterDisplacement, (USHORT)ParamDisplacement); // Displacement of these param bytes
SmbPutUshort(&pTransactRequest->DataCount, (USHORT)DataBytesToBeSent); // Parameter bytes sent this buffer
SmbPutUshort(&pTransactRequest->DataOffset, (USHORT)DataOffset); // Offset (from header start) to Datas
SmbPutUshort(&pTransactRequest->DataDisplacement, (USHORT)DataDisplacement); // Displacement of these Data bytes
ByteCountOffset = FIELD_OFFSET(REQ_TRANSACTION_SECONDARY,ByteCount);
if (pTransactExchange->TransactSmbCommand == SMB_COM_TRANSACTION2 ) {
//see CODE.IMPROVEMENT.ASHAMED above.......
ByteCountOffset += sizeof(USHORT);
pTransactRequest->WordCount++; //one extra word
SmbPutUshort((&pTransactRequest->DataDisplacement)+1, 0); //the +1 is to move up 1 USHORT
}
}
break;
case SMB_COM_NT_TRANSACT:
{
PREQ_NT_TRANSACTION_SECONDARY pNtTransactRequest;
pNtTransactRequest= (PREQ_NT_TRANSACTION_SECONDARY)(pSecondaryRequestSmbHeader + 1);
//CODE.IMPROVEMENT this should be stufferized.....the whole thing should be.
// (6/15 there are unimplemented things in the stuffer that'd make it hard
//CODE.IMPROVEMENT move the constant things to the top
//CODE.IMPROVEMENT you don't need the macros here because things are alinged.....put in asserts
// actually, use the aligned stuff
pNtTransactRequest->WordCount = 18; // Count of parameter words = 18
pNtTransactRequest->Reserved1 = 0; // MBZ
SmbPutUshort(&pNtTransactRequest->Reserved2, 0); // MBZ
SmbPutUlong(&pNtTransactRequest->TotalParameterCount, TotalParamBytes); // Total parameter bytes being sent
SmbPutUlong(&pNtTransactRequest->TotalDataCount, TotalDataBytes); // Total data bytes being sent
SmbPutUlong(&pNtTransactRequest->ParameterCount, ParamBytesToBeSent); // Parameter bytes sent this buffer
SmbPutUlong(&pNtTransactRequest->ParameterOffset, ParamOffset); // Offset (from header start) to params
SmbPutUlong(&pNtTransactRequest->ParameterDisplacement, ParamDisplacement); // Displacement of these param bytes
SmbPutUlong(&pNtTransactRequest->DataCount, DataBytesToBeSent); // Parameter bytes sent this buffer
SmbPutUlong(&pNtTransactRequest->DataOffset, DataOffset); // Offset (from header start) to Datas
SmbPutUlong(&pNtTransactRequest->DataDisplacement, DataDisplacement); // Displacement of these Data bytes
pNtTransactRequest->Reserved3 = 0; // MBZ
ByteCountOffset = FIELD_OFFSET(REQ_NT_TRANSACTION_SECONDARY,ByteCount);
}
break;
default:
ASSERT(!"Valid Smb Command for initiating Transaction");
Status = STATUS_INVALID_PARAMETER;
break;
}
// Send the secondary SMB
SmbLength = SecondaryRequestSmbSize +
ParamBytesToBeSent +
PaddingLength +
DataBytesToBeSent;
ByteCount = (USHORT)(SmbLength-(sizeof(SMB_HEADER)+ByteCountOffset+sizeof(USHORT)));
pByteCount = (PUSHORT)((PBYTE)pSecondaryRequestSmbHeader+sizeof(SMB_HEADER)+ByteCountOffset);
SmbPutUshort(pByteCount,ByteCount);
RxDbgTrace( 0, Dbg, ("SendSecondaryRequests: len %d bytecount %d(%x)\n", SmbLength, ByteCount, ByteCount));
RxDbgTrace( 0, Dbg, ("SendSecondaryRequests: msgmdl=%08lx\n", pSecondaryRequestSmbHeader));
RxLog(("2nd: %lx %lx %lx %lx %lx %lx",ParamOffset,ParamDisplacement,TotalParamBytes,DataOffset,DataDisplacement,TotalDataBytes));
RxLog(("2nd:: %lx %lx",ByteCount,SmbLength));
SmbLog(LOG,
SendSecondaryRequests,
LOGULONG(ParamOffset)
LOGULONG(ParamDisplacement)
LOGULONG(TotalParamBytes)
LOGULONG(DataOffset)
LOGULONG(DataDisplacement)
LOGULONG(TotalDataBytes)
LOGXSHORT(ByteCount)
LOGULONG(SmbLength));
Status = SmbCeSend(
pExchange,
RXCE_SEND_SYNCHRONOUS,
pSecondaryRequestSmbMdl,
SmbLength);
RxDbgTrace( 0, Dbg, ("SendSecondaryRequests: SmbCeSend returned %lx\n",Status));
if ((Status != RX_MAP_STATUS(PENDING)) && (Status != RX_MAP_STATUS(SUCCESS))) {
RxDbgTrace( 0, Dbg, ("SendSecondaryRequests: SmbCeSend returned bad status %lx\n",Status));
//here we should just get out
goto FINALLY; //yes we cold have said break....but that's not what we're doing
} else {
Status = RX_MAP_STATUS(SUCCESS);
}
}
FINALLY:
if (pPartialDataMdl != NULL) {
IoFreeMdl(pPartialDataMdl);
}
if (pActualSecondaryRequestSmbHeader != NULL) {
RxFreePool(pActualSecondaryRequestSmbHeader);
}
if (pPartialParamMdl != NULL) {
IoFreeMdl(pPartialParamMdl);
}
if (pPaddingMdl != NULL) {
IoFreeMdl(pPaddingMdl);
}
if (pSecondaryRequestSmbMdl != NULL) {
RxUnlockHeaderPages(pSecondaryRequestSmbMdl);
IoFreeMdl(pSecondaryRequestSmbMdl);
}
//we always finalize......but we only set the status if there's an error or
// we expect no response
if ((Status != RX_MAP_STATUS(SUCCESS)) || (pTransactExchange->Flags & SMB_TRANSACTION_NO_RESPONSE )) {
pExchange->Status = Status;
if (!(pTransactExchange->Flags & SMB_TRANSACTION_NO_RESPONSE)) {
SmbCeDecrementPendingReceiveOperations(pExchange);
}
}
SmbCeDecrementPendingLocalOperationsAndFinalize(pExchange);
return Status;
}
SMB_EXCHANGE_DISPATCH_VECTOR
TransactExchangeDispatch = {
SmbTransactExchangeStart,
SmbTransactExchangeReceive,
SmbTransactExchangeCopyDataHandler,
NULL, // SmbTransactExchangeSendCallbackHandler
SmbTransactExchangeFinalize,
NULL
};
#ifndef RX_NO_DBGFIELD_HLPRS
#define DECLARE_FIELD_HLPR(x) ULONG SmbPseTxeField_##x = FIELD_OFFSET(SMB_TRANSACT_EXCHANGE,x);
#define DECLARE_FIELD_HLPR2(x,y) ULONG SmbPseTxeField_##x##y = FIELD_OFFSET(SMB_TRANSACT_EXCHANGE,x.y);
DECLARE_FIELD_HLPR(RxContext);
DECLARE_FIELD_HLPR(ReferenceCount);
DECLARE_FIELD_HLPR(State);
DECLARE_FIELD_HLPR(pSendDataMdl);
DECLARE_FIELD_HLPR(pReceiveDataMdl);
DECLARE_FIELD_HLPR(pSendParamMdl);
DECLARE_FIELD_HLPR(pReceiveParamMdl);
DECLARE_FIELD_HLPR(pSendSetupMdl);
DECLARE_FIELD_HLPR(pReceiveSetupMdl);
DECLARE_FIELD_HLPR(PrimaryRequestSmbSize);
DECLARE_FIELD_HLPR(SmbCommand);
DECLARE_FIELD_HLPR(NtTransactFunction);
DECLARE_FIELD_HLPR(Flags);
DECLARE_FIELD_HLPR(Fid);
#endif