You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4004 lines
111 KiB
4004 lines
111 KiB
/*++
|
|
|
|
Copyright (c) 1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
smbmpx.c
|
|
|
|
Abstract:
|
|
|
|
This module contains routines for processing the following SMBs:
|
|
|
|
Read Block Multiplexed
|
|
Write Block Multiplexed
|
|
|
|
Note that core and raw mode SMB processors are not contained in this
|
|
module. Check smbrdwrt.c and smbraw.c instead. SMB commands that
|
|
pertain exclusively to locking (LockByteRange, UnlockByteRange, and
|
|
LockingAndX) are processed in smblock.c.
|
|
|
|
Author:
|
|
|
|
Chuck Lenzmeier (chuckl) 4-Nov-1993
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#include "smbmpx.tmh"
|
|
#pragma hdrstop
|
|
|
|
#define BugCheckFileId SRV_FILE_SMBMPX
|
|
|
|
#if 0
|
|
BOOLEAN MpxDelay = TRUE;
|
|
#endif
|
|
|
|
//
|
|
// Stack overflow threshold. This is used to determine when we are
|
|
// getting close to the end of our stack and need to stop recursing
|
|
// in SendCopy/MdlReadMpxFragment.
|
|
//
|
|
|
|
#define STACK_THRESHOLD 0xE00
|
|
|
|
//
|
|
// Forward declarations
|
|
//
|
|
|
|
VOID SRVFASTCALL
|
|
RestartReadMpx (
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
);
|
|
|
|
NTSTATUS
|
|
SendCopyReadMpxFragment (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
);
|
|
|
|
VOID SRVFASTCALL
|
|
SendCopyReadMpxFragment2 (
|
|
IN OUT PWORK_CONTEXT
|
|
);
|
|
|
|
NTSTATUS
|
|
SendMdlReadMpxFragment (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
);
|
|
|
|
VOID SRVFASTCALL
|
|
SendMdlReadMpxFragment2 (
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
);
|
|
|
|
VOID SRVFASTCALL
|
|
RestartMdlReadMpxComplete (
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
);
|
|
|
|
VOID SRVFASTCALL
|
|
RestartWriteMpx (
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
);
|
|
|
|
BOOLEAN
|
|
CheckForWriteMpxComplete (
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
);
|
|
|
|
VOID SRVFASTCALL
|
|
RestartPrepareMpxMdlWrite (
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
);
|
|
|
|
VOID SRVFASTCALL
|
|
AddPacketToGlom (
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
);
|
|
|
|
VOID SRVFASTCALL
|
|
RestartAfterGlomDelay (
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
);
|
|
|
|
VOID SRVFASTCALL
|
|
RestartCompleteGlommingInIndication(
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
);
|
|
|
|
VOID SRVFASTCALL
|
|
RestartWriteMpxCompleteRfcbClose (
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
);
|
|
|
|
VOID SRVFASTCALL
|
|
WriteMpxMdlWriteComplete (
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text( PAGE, SrvSmbReadMpx )
|
|
#pragma alloc_text( PAGE, RestartMdlReadMpxComplete )
|
|
#pragma alloc_text( PAGE, SrvRestartReceiveWriteMpx )
|
|
#pragma alloc_text( PAGE, SrvSmbWriteMpxSecondary )
|
|
#pragma alloc_text( PAGE, SendCopyReadMpxFragment2 )
|
|
#pragma alloc_text( PAGE, SendMdlReadMpxFragment2 )
|
|
#pragma alloc_text( PAGE8FIL, RestartReadMpx )
|
|
#pragma alloc_text( PAGE8FIL, SendCopyReadMpxFragment )
|
|
#pragma alloc_text( PAGE8FIL, RestartCopyReadMpxComplete )
|
|
#pragma alloc_text( PAGE8FIL, SendMdlReadMpxFragment )
|
|
#endif
|
|
#if 0
|
|
NOT PAGEABLE -- SrvSmbWriteMpx
|
|
NOT PAGEABLE -- RestartWriteMpx
|
|
NOT PAGEABLE -- CheckForWriteMpxComplete
|
|
NOT PAGEABLE -- RestartCompleteGlommingInIndication
|
|
NOT PAGEABLE -- RestartWriteMpxCompleteRfcbClose
|
|
NOT PAGEABLE -- WriteMpxMdlWriteComplete
|
|
#endif
|
|
|
|
#if DBG
|
|
VOID
|
|
DumpMdlChain(
|
|
IN PMDL mdl
|
|
);
|
|
#endif
|
|
|
|
|
|
SMB_PROCESSOR_RETURN_TYPE
|
|
SrvSmbReadMpx (
|
|
SMB_PROCESSOR_PARAMETERS
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Processes the Read Mpx SMB.
|
|
|
|
Arguments:
|
|
|
|
WorkContext - Supplies a pointer to the work context block
|
|
representing the work item
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PSMB_HEADER header;
|
|
PREQ_READ_MPX request;
|
|
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
SMB_STATUS SmbStatus = SmbStatusInProgress;
|
|
USHORT fid;
|
|
PRFCB rfcb;
|
|
PLFCB lfcb;
|
|
ULONG bufferOffset;
|
|
PCHAR readAddress;
|
|
CLONG readLength;
|
|
ULONG key;
|
|
LARGE_INTEGER offset;
|
|
PMDL mdl;
|
|
PVOID mpxBuffer;
|
|
UCHAR minorFunction;
|
|
|
|
PAGED_CODE( );
|
|
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
|
|
WorkContext->PreviousSMB = EVENT_TYPE_SMB_READ_MPX;
|
|
SrvWmiStartContext(WorkContext);
|
|
|
|
header = WorkContext->RequestHeader;
|
|
request = (PREQ_READ_MPX)WorkContext->RequestParameters;
|
|
|
|
fid = SmbGetUshort( &request->Fid );
|
|
|
|
IF_SMB_DEBUG(MPX1) {
|
|
KdPrint(( "Read Block Multiplexed request; FID 0x%lx, "
|
|
"count %ld, offset %ld\n",
|
|
fid, SmbGetUshort( &request->MaxCount ),
|
|
SmbGetUlong( &request->Offset ) ));
|
|
}
|
|
|
|
//
|
|
// Verify the FID. If verified, the RFCB is referenced and its
|
|
// address is stored in the WorkContext block, and the RFCB address
|
|
// is returned.
|
|
//
|
|
|
|
rfcb = SrvVerifyFid(
|
|
WorkContext,
|
|
fid,
|
|
TRUE,
|
|
SrvRestartSmbReceived, // serialize with raw write
|
|
&status
|
|
);
|
|
|
|
if ( rfcb == SRV_INVALID_RFCB_POINTER ) {
|
|
|
|
if ( !NT_SUCCESS( status )) {
|
|
|
|
//
|
|
// Invalid file ID or write behind error. Reject the request.
|
|
//
|
|
|
|
IF_DEBUG(ERRORS) {
|
|
KdPrint((
|
|
"SrvSmbReadMpx: Status %X on FID: 0x%lx\n",
|
|
status,
|
|
fid
|
|
));
|
|
}
|
|
|
|
SrvSetSmbError( WorkContext, status );
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// The work item has been queued because a raw write is in
|
|
// progress.
|
|
//
|
|
|
|
SmbStatus = SmbStatusInProgress;
|
|
goto Cleanup;
|
|
}
|
|
|
|
lfcb = rfcb->Lfcb;
|
|
|
|
if( lfcb->Session->IsSessionExpired )
|
|
{
|
|
status = SESSION_EXPIRED_STATUS_CODE;
|
|
SrvSetSmbError( WorkContext, status );
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Verify that the client has read access to the file via the
|
|
// specified handle.
|
|
//
|
|
|
|
if( rfcb->MpxReadsOk == FALSE ) {
|
|
|
|
if ( !rfcb->ReadAccessGranted ) {
|
|
CHECK_PAGING_IO_ACCESS(
|
|
WorkContext,
|
|
rfcb->GrantedAccess,
|
|
&status );
|
|
if ( !NT_SUCCESS( status ) ) {
|
|
SrvStatistics.GrantedAccessErrors++;
|
|
IF_DEBUG(ERRORS) {
|
|
KdPrint(( "SrvSmbReadMpx: Read access not granted.\n"));
|
|
}
|
|
SrvSetSmbError( WorkContext, status );
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If this is not a disk file, tell the client to use core read.
|
|
//
|
|
|
|
if ( rfcb->ShareType != ShareTypeDisk ) {
|
|
SrvSetSmbError( WorkContext, STATUS_SMB_USE_STANDARD );
|
|
status = STATUS_SMB_USE_STANDARD;
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
|
|
rfcb->MpxReadsOk = TRUE;
|
|
}
|
|
|
|
//
|
|
// Form the lock key using the FID and the PID.
|
|
//
|
|
// *** The FID must be included in the key in order to account for
|
|
// the folding of multiple remote compatibility mode opens into
|
|
// a single local open.
|
|
//
|
|
|
|
key = rfcb->ShiftedFid | SmbGetAlignedUshort( &header->Pid );
|
|
|
|
//
|
|
// See if the direct host IPX smart card can handle this read. If so,
|
|
// return immediately, and the card will call our restart routine at
|
|
// SrvIpxSmartCardReadComplete
|
|
//
|
|
if( rfcb->PagedRfcb->IpxSmartCardContext ) {
|
|
IF_DEBUG( SIPX ) {
|
|
KdPrint(( "SrvSmbReadMpx: calling SmartCard Read for context %p\n",
|
|
WorkContext ));
|
|
}
|
|
|
|
WorkContext->Parameters.SmartCardRead.MdlReadComplete = lfcb->MdlReadComplete;
|
|
WorkContext->Parameters.SmartCardRead.DeviceObject = lfcb->DeviceObject;
|
|
|
|
if( SrvIpxSmartCard.Read( WorkContext->RequestBuffer->Buffer,
|
|
rfcb->PagedRfcb->IpxSmartCardContext,
|
|
key,
|
|
WorkContext ) == TRUE ) {
|
|
|
|
IF_DEBUG( SIPX ) {
|
|
KdPrint(( " SrvSmbReadMpx: SmartCard Read returns TRUE\n" ));
|
|
}
|
|
|
|
SmbStatus = SmbStatusInProgress;
|
|
goto Cleanup;
|
|
}
|
|
|
|
IF_DEBUG( SIPX ) {
|
|
KdPrint(( " SrvSmbReadMpx: SmartCard Read returns FALSE\n" ));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the file offset.
|
|
//
|
|
|
|
WorkContext->Parameters.ReadMpx.Offset = SmbGetUlong( &request->Offset );
|
|
offset.QuadPart = WorkContext->Parameters.ReadMpx.Offset;
|
|
|
|
//
|
|
// Calculate the address in the buffer at which to put the data.
|
|
// This must be rounded up to a dword boundary. (The -1 below is
|
|
// because sizeof(RESP_READ_MPX) includes one byte of Buffer.)
|
|
//
|
|
|
|
bufferOffset = (sizeof(SMB_HEADER) + FIELD_OFFSET(RESP_READ_MPX, Buffer) + 3) & ~3;
|
|
|
|
//
|
|
// Calculate how much data we can send back in each fragment. This
|
|
// is the size of the client's buffer, rounded down to a dword multiple.
|
|
//
|
|
// *** Because we use the SMB buffer's partial MDL to describe the
|
|
// data fragments that we return, we need to limit the fragment
|
|
// size to the SMB buffer size. Normally the client's buffer
|
|
// size is <= ours, so this shouldn't be a factor.
|
|
//
|
|
|
|
WorkContext->Parameters.ReadMpx.FragmentSize =
|
|
(USHORT)((MIN( lfcb->Session->MaxBufferSize,
|
|
SrvReceiveBufferLength ) - bufferOffset) & ~3);
|
|
|
|
//
|
|
// If the SMB buffer is large enough, use it to do the local read.
|
|
//
|
|
|
|
readLength = SmbGetUshort( &request->MaxCount );
|
|
|
|
if ( //0 &&
|
|
(readLength <= SrvMpxMdlReadSwitchover) ) {
|
|
|
|
do_copy_read:
|
|
|
|
WorkContext->Parameters.ReadMpx.MdlRead = FALSE;
|
|
WorkContext->Parameters.ReadMpx.MpxBuffer = NULL;
|
|
WorkContext->Parameters.ReadMpx.MpxBufferMdl =
|
|
WorkContext->ResponseBuffer->Mdl;
|
|
|
|
readAddress = (PCHAR)WorkContext->ResponseHeader + bufferOffset;
|
|
WorkContext->Parameters.ReadMpx.NextFragmentAddress = readAddress;
|
|
|
|
//
|
|
// Try the fast I/O path first.
|
|
//
|
|
|
|
if ( lfcb->FastIoRead != NULL ) {
|
|
|
|
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastReadsAttempted );
|
|
|
|
try {
|
|
if ( lfcb->FastIoRead(
|
|
lfcb->FileObject,
|
|
&offset,
|
|
readLength,
|
|
TRUE,
|
|
key,
|
|
readAddress,
|
|
&WorkContext->Irp->IoStatus,
|
|
lfcb->DeviceObject
|
|
) ) {
|
|
|
|
//
|
|
// The fast I/O path worked. Send the data.
|
|
//
|
|
|
|
WorkContext->bAlreadyTrace = TRUE;
|
|
RestartReadMpx( WorkContext );
|
|
SmbStatus = SmbStatusInProgress;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
// Fall through to the slow path on an exception
|
|
status = GetExceptionCode();
|
|
IF_DEBUG(ERRORS) {
|
|
KdPrint(("FastIoRead threw exception %x\n", status ));
|
|
}
|
|
}
|
|
|
|
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastReadsFailed );
|
|
|
|
}
|
|
|
|
//
|
|
// The fast I/O path failed, so we need to use a regular copy
|
|
// I/O request. Build an MDL describing the read buffer.
|
|
//
|
|
// *** Note the assumption that the response buffer already has
|
|
// a valid full MDL from which a partial MDL can be built.
|
|
//
|
|
|
|
IoBuildPartialMdl(
|
|
WorkContext->ResponseBuffer->Mdl,
|
|
WorkContext->ResponseBuffer->PartialMdl,
|
|
readAddress,
|
|
readLength
|
|
);
|
|
mdl = WorkContext->ResponseBuffer->PartialMdl;
|
|
minorFunction = 0;
|
|
|
|
} else {
|
|
|
|
//
|
|
// The SMB buffer isn't big enough. Does the target file system
|
|
// support the cache manager routines?
|
|
//
|
|
|
|
if ( //0 &&
|
|
(lfcb->FileObject->Flags & FO_CACHE_SUPPORTED) ) {
|
|
|
|
WorkContext->Parameters.ReadMpx.MdlRead = TRUE;
|
|
|
|
//
|
|
// We can use an MDL read. Try the fast I/O path first.
|
|
//
|
|
|
|
WorkContext->Irp->MdlAddress = NULL;
|
|
WorkContext->Irp->IoStatus.Information = 0;
|
|
WorkContext->Parameters.ReadMpx.ReadLength = readLength;
|
|
|
|
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastReadsAttempted );
|
|
|
|
if ( lfcb->MdlRead &&
|
|
lfcb->MdlRead(
|
|
lfcb->FileObject,
|
|
&offset,
|
|
readLength,
|
|
key,
|
|
&WorkContext->Irp->MdlAddress,
|
|
&WorkContext->Irp->IoStatus,
|
|
lfcb->DeviceObject
|
|
) && WorkContext->Irp->MdlAddress != NULL ) {
|
|
|
|
//
|
|
// The fast I/O path worked. Send the data.
|
|
//
|
|
|
|
WorkContext->bAlreadyTrace = TRUE;
|
|
RestartReadMpx( WorkContext );
|
|
SmbStatus = SmbStatusInProgress;
|
|
goto Cleanup;
|
|
}
|
|
|
|
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastReadsFailed );
|
|
|
|
//
|
|
// The fast I/O path failed. We need to issue a regular MDL
|
|
// read request.
|
|
//
|
|
// The fast path may have partially succeeded, returning a
|
|
// partial MDL chain. We need to adjust our read request
|
|
// to account for that.
|
|
//
|
|
|
|
offset.QuadPart += WorkContext->Irp->IoStatus.Information;
|
|
readLength -= (ULONG)WorkContext->Irp->IoStatus.Information;
|
|
|
|
mdl = WorkContext->Irp->MdlAddress;
|
|
minorFunction = IRP_MN_MDL;
|
|
readAddress = NULL; // Not used for MDL read
|
|
|
|
} else if (readLength > (WorkContext->ResponseBuffer->BufferLength -
|
|
bufferOffset)) {
|
|
|
|
//
|
|
// We have to use a normal "copy" read. We need to allocate
|
|
// a separate buffer.
|
|
//
|
|
|
|
WorkContext->Parameters.ReadMpx.MdlRead = FALSE;
|
|
|
|
mpxBuffer = ALLOCATE_NONPAGED_POOL(
|
|
readLength,
|
|
BlockTypeDataBuffer
|
|
);
|
|
if ( mpxBuffer == NULL ) {
|
|
SrvSetSmbError( WorkContext, STATUS_INSUFF_SERVER_RESOURCES );
|
|
status = STATUS_INSUFF_SERVER_RESOURCES;
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
WorkContext->Parameters.ReadMpx.MpxBuffer = mpxBuffer;
|
|
WorkContext->Parameters.ReadMpx.NextFragmentAddress = mpxBuffer;
|
|
readAddress = mpxBuffer;
|
|
|
|
//
|
|
// We also need an MDL to describe the buffer.
|
|
//
|
|
|
|
mdl = IoAllocateMdl( mpxBuffer, readLength, FALSE, FALSE, NULL );
|
|
if ( mdl == NULL ) {
|
|
DEALLOCATE_NONPAGED_POOL( mpxBuffer );
|
|
SrvSetSmbError( WorkContext, STATUS_INSUFF_SERVER_RESOURCES );
|
|
status = STATUS_INSUFF_SERVER_RESOURCES;
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
|
|
WorkContext->Parameters.ReadMpx.MpxBufferMdl = mdl;
|
|
|
|
//
|
|
// Build the mdl.
|
|
//
|
|
|
|
MmBuildMdlForNonPagedPool( mdl );
|
|
|
|
//
|
|
// Try the fast I/O path first.
|
|
//
|
|
|
|
if ( lfcb->FastIoRead != NULL ) {
|
|
|
|
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastReadsAttempted );
|
|
|
|
try {
|
|
if ( lfcb->FastIoRead(
|
|
lfcb->FileObject,
|
|
&offset,
|
|
readLength,
|
|
TRUE,
|
|
key,
|
|
mpxBuffer,
|
|
&WorkContext->Irp->IoStatus,
|
|
lfcb->DeviceObject
|
|
) ) {
|
|
|
|
//
|
|
// The fast I/O path worked. Send the data.
|
|
//
|
|
|
|
WorkContext->bAlreadyTrace = TRUE;
|
|
RestartReadMpx( WorkContext );
|
|
SmbStatus = SmbStatusInProgress;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
// Fall through to the slow path on an exception
|
|
status = GetExceptionCode();
|
|
IF_DEBUG(ERRORS) {
|
|
KdPrint(("FastIoRead threw exception %x\n", status ));
|
|
}
|
|
}
|
|
|
|
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastReadsFailed );
|
|
|
|
}
|
|
|
|
//
|
|
// The fast I/O path failed, so we need to use a regular copy
|
|
// I/O request.
|
|
//
|
|
|
|
minorFunction = 0;
|
|
|
|
} else {
|
|
|
|
goto do_copy_read;
|
|
}
|
|
|
|
} // read fits in SMB buffer?
|
|
|
|
//
|
|
// Build the read request, reusing the receive IRP.
|
|
//
|
|
|
|
SrvBuildReadOrWriteRequest(
|
|
WorkContext->Irp, // input IRP address
|
|
lfcb->FileObject, // target file object address
|
|
WorkContext, // context
|
|
IRP_MJ_READ, // major function code
|
|
minorFunction, // minor function code
|
|
readAddress, // buffer address
|
|
readLength, // buffer length
|
|
mdl, // MDL address
|
|
offset, // byte offset
|
|
key // lock key
|
|
);
|
|
|
|
//
|
|
// Pass the request to the file system.
|
|
//
|
|
|
|
WorkContext->bAlreadyTrace = TRUE;
|
|
WorkContext->FsdRestartRoutine = RestartReadMpx;
|
|
DEBUG WorkContext->FspRestartRoutine = NULL;
|
|
|
|
(VOID)IoCallDriver( lfcb->DeviceObject, WorkContext->Irp );
|
|
|
|
//
|
|
// The read has been started. Control will return to the restart
|
|
// routine when the read completes.
|
|
//
|
|
SmbStatus = SmbStatusInProgress;
|
|
|
|
Cleanup:
|
|
SrvWmiEndContext(WorkContext);
|
|
return SmbStatus;
|
|
} // SrvSmbReadMpx
|
|
|
|
|
|
VOID SRVFASTCALL
|
|
RestartReadMpx (
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Processes file read completion for a Read MPX SMB.
|
|
|
|
Arguments:
|
|
|
|
WorkContext - Supplies a pointer to the work context block
|
|
describing server-specific context for the request.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PRESP_READ_MPX response;
|
|
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
SMB_STATUS SmbStatus = SmbStatusInProgress;
|
|
LARGE_INTEGER position;
|
|
KIRQL oldIrql;
|
|
USHORT readLength;
|
|
ULONG offset;
|
|
PMDL mdl;
|
|
BOOLEAN mdlRead;
|
|
PIRP irp = WorkContext->Irp;
|
|
BOOLEAN bNeedTrace = (WorkContext->bAlreadyTrace == FALSE);
|
|
|
|
UNLOCKABLE_CODE( 8FIL );
|
|
if (bNeedTrace) {
|
|
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
|
|
WorkContext->PreviousSMB = EVENT_TYPE_SMB_READ_MPX;
|
|
SrvWmiStartContext(WorkContext);
|
|
}
|
|
else
|
|
WorkContext->bAlreadyTrace = FALSE;
|
|
|
|
IF_DEBUG(FSD2) KdPrint(( " - RestartReadMpx\n" ));
|
|
|
|
//
|
|
// If we just completed an MDL read, we need to remember the address
|
|
// of the first MDL in the returned chain, so that we can give it
|
|
// back to the cache manager when we're done.
|
|
//
|
|
|
|
mdlRead = WorkContext->Parameters.ReadMpx.MdlRead;
|
|
if ( mdlRead ) {
|
|
mdl = irp->MdlAddress;
|
|
//KdPrint(( "Read MDL chain:\n" ));
|
|
//DumpMdlChain( mdl );
|
|
WorkContext->Parameters.ReadMpx.FirstMdl = mdl;
|
|
}
|
|
|
|
//
|
|
// If the read failed, set an error status in the response header.
|
|
// (If we tried to read entirely beyond the end of file, we return a
|
|
// normal response indicating that nothing was read.)
|
|
//
|
|
|
|
status = irp->IoStatus.Status;
|
|
|
|
if ( !NT_SUCCESS(status) && (status != STATUS_END_OF_FILE) ) {
|
|
|
|
IF_DEBUG(ERRORS) KdPrint(( "Read failed: %X\n", status ));
|
|
if ( KeGetCurrentIrql() >= DISPATCH_LEVEL ) {
|
|
WorkContext->FspRestartRoutine = RestartReadMpx;
|
|
SrvQueueWorkToFsp( WorkContext );
|
|
goto Cleanup;
|
|
}
|
|
|
|
SrvSetSmbError( WorkContext, status );
|
|
respond:
|
|
if ( mdlRead ) {
|
|
SrvFsdSendResponse2( WorkContext, RestartMdlReadMpxComplete );
|
|
} else {
|
|
WorkContext->ResponseBuffer->DataLength =
|
|
(CLONG)( (PCHAR)WorkContext->ResponseParameters -
|
|
(PCHAR)WorkContext->ResponseHeader );
|
|
WorkContext->ResponseHeader->Flags |= SMB_FLAGS_SERVER_TO_REDIR;
|
|
SRV_START_SEND_2(
|
|
WorkContext,
|
|
RestartCopyReadMpxComplete,
|
|
NULL,
|
|
NULL );
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Get the amount of data actually read.
|
|
//
|
|
|
|
if ( status == STATUS_END_OF_FILE ) {
|
|
|
|
//
|
|
// The read started beyond the end of the file.
|
|
//
|
|
|
|
readLength = 0;
|
|
|
|
} else if ( mdlRead ) {
|
|
|
|
//
|
|
// For an MDL read, we have to walk the MDL chain in order to
|
|
// determine how much data was read. This is because the
|
|
// operation may have happened in multiple steps, with the MDLs
|
|
// being chained together. For example, part of the read may
|
|
// have been satisfied by the fast path, while the rest was
|
|
// satisfied using an IRP.
|
|
//
|
|
|
|
readLength = 0;
|
|
while ( mdl != NULL ) {
|
|
readLength += (USHORT)MmGetMdlByteCount(mdl);
|
|
mdl = mdl->Next;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Copy read. The I/O status block has the length.
|
|
//
|
|
|
|
readLength = (USHORT)irp->IoStatus.Information;
|
|
|
|
}
|
|
|
|
//
|
|
// Update the file position.
|
|
//
|
|
|
|
offset = WorkContext->Parameters.ReadMpx.Offset;
|
|
|
|
WorkContext->Rfcb->CurrentPosition = offset + readLength;
|
|
|
|
//
|
|
// Update statistics.
|
|
//
|
|
|
|
UPDATE_READ_STATS( WorkContext, readLength );
|
|
|
|
//
|
|
// Special-case 0 bytes read.
|
|
//
|
|
|
|
response = (PRESP_READ_MPX)WorkContext->ResponseParameters;
|
|
response->WordCount = 8;
|
|
SmbPutUshort( &response->DataCompactionMode, 0 );
|
|
SmbPutUshort( &response->Reserved, 0 );
|
|
|
|
if ( readLength == 0 ) {
|
|
|
|
SmbPutUlong( &response->Offset, offset );
|
|
SmbPutUshort( &response->Count, 0 );
|
|
SmbPutUshort( &response->Remaining, 0 );
|
|
SmbPutUshort( &response->DataLength, 0 );
|
|
SmbPutUshort( &response->DataOffset, 0 );
|
|
SmbPutUshort( &response->ByteCount, 0 );
|
|
|
|
WorkContext->ResponseParameters = NEXT_LOCATION(
|
|
response,
|
|
RESP_READ_MPX,
|
|
0
|
|
);
|
|
goto respond;
|
|
}
|
|
|
|
//
|
|
// Build the static response header/parameters.
|
|
//
|
|
|
|
SmbPutUshort( &response->Count, readLength );
|
|
SmbPutUshort(
|
|
&response->DataOffset,
|
|
(sizeof(SMB_HEADER) + FIELD_OFFSET(RESP_READ_MPX, Buffer) + 3) & ~3
|
|
);
|
|
|
|
//
|
|
// We will use two MDLs to describe the packet we're sending -- one
|
|
// for the header and parameters, and another for the data. So we
|
|
// set the "response length" to not include the data. This is what
|
|
// SrvStartSend uses to set the first MDL's length.
|
|
//
|
|
// Handling of the second MDL varies depending on whether we did a
|
|
// copy read or an MDL read.
|
|
//
|
|
|
|
ASSERT( ((sizeof(SMB_HEADER) + FIELD_OFFSET(RESP_READ_MPX,Buffer)) & 3) == 3 );
|
|
WorkContext->ResponseParameters = NEXT_LOCATION(
|
|
response,
|
|
RESP_READ_MPX,
|
|
1 // pad byte
|
|
);
|
|
WorkContext->ResponseBuffer->Mdl->ByteCount =
|
|
(CLONG)( (PCHAR)WorkContext->ResponseParameters -
|
|
(PCHAR)WorkContext->ResponseHeader );
|
|
WorkContext->ResponseHeader->Flags |= SMB_FLAGS_SERVER_TO_REDIR;
|
|
|
|
//
|
|
// Start sending fragments.
|
|
//
|
|
|
|
WorkContext->Parameters.ReadMpx.RemainingLength = readLength;
|
|
ASSERT( WorkContext->ResponseBuffer->Mdl->Next == NULL );
|
|
WorkContext->ResponseBuffer->Mdl->Next =
|
|
WorkContext->ResponseBuffer->PartialMdl;
|
|
WorkContext->ResponseBuffer->PartialMdl->Next = NULL;
|
|
|
|
if ( mdlRead ) {
|
|
|
|
WorkContext->Parameters.ReadMpx.CurrentMdl =
|
|
WorkContext->Parameters.ReadMpx.FirstMdl;
|
|
WorkContext->Parameters.ReadMpx.CurrentMdlOffset = 0;
|
|
(VOID)SendMdlReadMpxFragment( NULL, irp, WorkContext );
|
|
|
|
} else {
|
|
|
|
(VOID)SendCopyReadMpxFragment( NULL, irp, WorkContext );
|
|
}
|
|
|
|
Cleanup:
|
|
if (bNeedTrace) {
|
|
SrvWmiEndContext(WorkContext);
|
|
}
|
|
return;
|
|
|
|
} // RestartReadMpx
|
|
|
|
VOID SRVFASTCALL
|
|
SendCopyReadMpxFragment2 (
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Stub to call actual routine.
|
|
|
|
Arguments:
|
|
|
|
WorkContext - Supplies a pointer to the work context block
|
|
representing the work item
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE( );
|
|
|
|
(VOID) SendCopyReadMpxFragment( NULL, WorkContext->Irp, WorkContext );
|
|
|
|
} // SendCopyReadMpxFragment2
|
|
|
|
NTSTATUS
|
|
SendCopyReadMpxFragment (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sends a Read Mpx response fragment when copy read was used.
|
|
|
|
Arguments:
|
|
|
|
WorkContext - Supplies a pointer to the work context block
|
|
describing server-specific context for the request.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PRESP_READ_MPX response;
|
|
|
|
USHORT fragmentSize;
|
|
USHORT remainingLength;
|
|
ULONG offset;
|
|
PCHAR fragmentAddress;
|
|
|
|
PIO_COMPLETION_ROUTINE sendCompletionRoutine;
|
|
|
|
UNLOCKABLE_CODE( 8FIL );
|
|
|
|
//
|
|
// Check the status of the send completion.
|
|
//
|
|
|
|
CHECK_SEND_COMPLETION_STATUS( Irp->IoStatus.Status );
|
|
|
|
//
|
|
// Turn off cancel boolean
|
|
//
|
|
|
|
Irp->Cancel = FALSE;
|
|
|
|
//
|
|
// Get context.
|
|
//
|
|
|
|
fragmentSize = WorkContext->Parameters.ReadMpx.FragmentSize;
|
|
remainingLength = WorkContext->Parameters.ReadMpx.RemainingLength;
|
|
offset = WorkContext->Parameters.ReadMpx.Offset;
|
|
fragmentAddress = WorkContext->Parameters.ReadMpx.NextFragmentAddress;
|
|
|
|
//
|
|
// If the amount left to send is less than the fragment size, only
|
|
// send the remaining amount. Update the remaining amount.
|
|
//
|
|
|
|
if ( remainingLength < fragmentSize ) {
|
|
fragmentSize = remainingLength;
|
|
}
|
|
ASSERT( fragmentSize != 0 );
|
|
remainingLength -= fragmentSize;
|
|
|
|
//
|
|
// Build the response parameters.
|
|
//
|
|
|
|
response = (PRESP_READ_MPX)(WorkContext->ResponseHeader + 1);
|
|
SmbPutUshort( &response->Remaining, remainingLength );
|
|
SmbPutUlong( &response->Offset, offset );
|
|
SmbPutUshort( &response->DataLength, fragmentSize );
|
|
ASSERT( ((sizeof(SMB_HEADER) + FIELD_OFFSET(RESP_READ_MPX, Buffer)) & 3) == 3 );
|
|
SmbPutUshort( &response->ByteCount, fragmentSize + 1 ); // account for padding
|
|
|
|
//
|
|
// Build a partial MDL describing the data.
|
|
//
|
|
|
|
IoBuildPartialMdl(
|
|
WorkContext->Parameters.ReadMpx.MpxBufferMdl,
|
|
WorkContext->ResponseBuffer->PartialMdl,
|
|
fragmentAddress,
|
|
fragmentSize
|
|
);
|
|
|
|
//
|
|
// Final preparation for the send depends on whether this is the
|
|
// last fragment.
|
|
//
|
|
|
|
if ( remainingLength != 0 ) {
|
|
|
|
//
|
|
// Not done. Update context. Set up to restart after the send
|
|
// in this routine. We want do this as an FSD restart routine.
|
|
// But this may recurse, if the send doesn't pend, so we may use
|
|
// up the stack. If we are running out of stack, restart here
|
|
// in the FSP.
|
|
//
|
|
|
|
WorkContext->Parameters.ReadMpx.RemainingLength = remainingLength;
|
|
WorkContext->Parameters.ReadMpx.Offset += fragmentSize;
|
|
WorkContext->Parameters.ReadMpx.NextFragmentAddress += fragmentSize;
|
|
|
|
if ( IoGetRemainingStackSize() >= STACK_THRESHOLD ) {
|
|
DEBUG WorkContext->FsdRestartRoutine = NULL;
|
|
sendCompletionRoutine = SendCopyReadMpxFragment;
|
|
} else {
|
|
DEBUG WorkContext->FsdRestartRoutine = NULL;
|
|
WorkContext->FspRestartRoutine = SendCopyReadMpxFragment2;
|
|
sendCompletionRoutine = SrvQueueWorkToFspAtSendCompletion;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// This is the last fragment. Restart in the cleanup routine.
|
|
//
|
|
|
|
DEBUG WorkContext->FsdRestartRoutine = NULL;
|
|
DEBUG WorkContext->FspRestartRoutine = NULL;
|
|
sendCompletionRoutine = RestartCopyReadMpxComplete;
|
|
}
|
|
|
|
//
|
|
// Send the fragment.
|
|
//
|
|
|
|
WorkContext->ResponseBuffer->DataLength = // +1 for pad
|
|
sizeof(SMB_HEADER) + FIELD_OFFSET(RESP_READ_MPX,Buffer) + 1 + fragmentSize;
|
|
|
|
if ( WorkContext->Endpoint->IsConnectionless ) {
|
|
SrvIpxStartSend( WorkContext, sendCompletionRoutine );
|
|
} else {
|
|
SrvStartSend2( WorkContext, sendCompletionRoutine );
|
|
}
|
|
|
|
return(STATUS_MORE_PROCESSING_REQUIRED);
|
|
|
|
} // SendCopyReadMpxFragment
|
|
|
|
VOID SRVFASTCALL
|
|
SendMdlReadMpxFragment2 (
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Stub to call actual routine.
|
|
|
|
Arguments:
|
|
|
|
WorkContext - Supplies a pointer to the work context block
|
|
representing the work item
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE( );
|
|
|
|
(VOID) SendMdlReadMpxFragment( NULL, WorkContext->Irp, WorkContext );
|
|
|
|
} // SendMdlReadMpxFragment2
|
|
|
|
NTSTATUS
|
|
SendMdlReadMpxFragment (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sends a Read Mpx response fragment when MDL read was used.
|
|
|
|
Arguments:
|
|
|
|
WorkContext - Supplies a pointer to the work context block
|
|
describing server-specific context for the request.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PRESP_READ_MPX response;
|
|
PIO_COMPLETION_ROUTINE sendCompletionRoutine;
|
|
|
|
USHORT fragmentSize;
|
|
USHORT remainingLength;
|
|
ULONG offset;
|
|
PCHAR fragmentAddress;
|
|
PMDL mdl;
|
|
ULONG mdlOffset;
|
|
ULONG partialLength;
|
|
ULONG lengthNeeded;
|
|
PCHAR startVa;
|
|
PCHAR systemVa;
|
|
|
|
UNLOCKABLE_CODE( 8FIL );
|
|
|
|
//
|
|
// Check the status of the send completion.
|
|
//
|
|
|
|
CHECK_SEND_COMPLETION_STATUS( Irp->IoStatus.Status );
|
|
|
|
//
|
|
// Turn off cancel boolean
|
|
//
|
|
|
|
Irp->Cancel = FALSE;
|
|
|
|
//
|
|
// Get context.
|
|
//
|
|
|
|
fragmentSize = WorkContext->Parameters.ReadMpx.FragmentSize,
|
|
remainingLength = WorkContext->Parameters.ReadMpx.RemainingLength;
|
|
offset = WorkContext->Parameters.ReadMpx.Offset;
|
|
|
|
//
|
|
// If the amount left to send is less than the fragment size, only
|
|
// send the remaining amount. Update the remaining amount.
|
|
//
|
|
|
|
if ( remainingLength < fragmentSize ) {
|
|
fragmentSize = remainingLength;
|
|
}
|
|
ASSERT( fragmentSize != 0 );
|
|
remainingLength -= fragmentSize;
|
|
|
|
//
|
|
// Build the response parameters.
|
|
//
|
|
|
|
response = (PRESP_READ_MPX)(WorkContext->ResponseHeader + 1);
|
|
SmbPutUshort( &response->Remaining, remainingLength );
|
|
SmbPutUlong( &response->Offset, offset );
|
|
SmbPutUshort( &response->DataLength, fragmentSize );
|
|
ASSERT( ((sizeof(SMB_HEADER) + FIELD_OFFSET(RESP_READ_MPX,Buffer)) & 3) == 3 );
|
|
SmbPutUshort( &response->ByteCount, fragmentSize + 1 ); // account for padding
|
|
|
|
//
|
|
// If the current MDL doesn't describe all of the data we need to
|
|
// send, we need to play some games.
|
|
//
|
|
|
|
MmPrepareMdlForReuse( WorkContext->ResponseBuffer->PartialMdl );
|
|
|
|
mdl = WorkContext->Parameters.ReadMpx.CurrentMdl;
|
|
startVa = MmGetMdlVirtualAddress( mdl );
|
|
mdlOffset = WorkContext->Parameters.ReadMpx.CurrentMdlOffset;
|
|
partialLength = MmGetMdlByteCount(mdl) - mdlOffset;
|
|
|
|
if ( partialLength >= fragmentSize ) {
|
|
|
|
//
|
|
// The current MDL has all of the data we need to send. Build
|
|
// a partial MDL describing that data.
|
|
//
|
|
|
|
IoBuildPartialMdl(
|
|
mdl,
|
|
WorkContext->ResponseBuffer->PartialMdl,
|
|
startVa + mdlOffset,
|
|
fragmentSize
|
|
);
|
|
|
|
//
|
|
// Indicate how much data we're taking out of the current MDL.
|
|
//
|
|
|
|
partialLength = fragmentSize;
|
|
|
|
} else {
|
|
|
|
//
|
|
// The data we need is spread across more than one MDL. Painful
|
|
// as this seems, we need to copy the data into the standard
|
|
// response buffer. It's possible that we could play some games
|
|
// with the MDLs and avoid the copy, but it doesn't seem worth it.
|
|
// There is, after all, additional cost in the NDIS driver for
|
|
// chaining MDLs together.
|
|
//
|
|
// *** Note that we still send a second MDL, even though the data
|
|
// for this send will abut the response parameters.
|
|
//
|
|
// Calculate the address of the buffer. Build a partial MDL
|
|
// describing it.
|
|
//
|
|
|
|
fragmentAddress = (PCHAR)WorkContext->ResponseBuffer->Buffer +
|
|
sizeof(SMB_HEADER) + FIELD_OFFSET(RESP_READ_MPX,Buffer) + 1;
|
|
|
|
IoBuildPartialMdl(
|
|
WorkContext->ResponseBuffer->Mdl,
|
|
WorkContext->ResponseBuffer->PartialMdl,
|
|
fragmentAddress,
|
|
fragmentSize
|
|
);
|
|
ASSERT( WorkContext->ResponseBuffer->PartialMdl->Next == NULL );
|
|
|
|
//
|
|
// Copy from the current MDL into the buffer.
|
|
//
|
|
|
|
systemVa = MmGetSystemAddressForMdl( mdl );
|
|
RtlCopyMemory( fragmentAddress, systemVa + mdlOffset, partialLength );
|
|
|
|
//
|
|
// Update the destination address and set the remaining copy
|
|
// amount.
|
|
//
|
|
|
|
fragmentAddress += partialLength;
|
|
lengthNeeded = fragmentSize - partialLength;
|
|
ASSERT( lengthNeeded != 0 );
|
|
|
|
do {
|
|
|
|
//
|
|
// Move to the next MDL.
|
|
//
|
|
|
|
mdl = mdl->Next;
|
|
ASSERT( mdl != NULL );
|
|
|
|
//
|
|
// Calculate how much we can (and need to) copy out of this
|
|
// MDL, and do the copy.
|
|
//
|
|
|
|
startVa = MmGetMdlVirtualAddress( mdl );
|
|
partialLength = MIN( MmGetMdlByteCount(mdl), lengthNeeded );
|
|
systemVa = MmGetSystemAddressForMdl( mdl );
|
|
RtlCopyMemory( fragmentAddress, systemVa, partialLength );
|
|
|
|
//
|
|
// Update the destination address and the remaining copy
|
|
// amount. We may be done.
|
|
//
|
|
|
|
fragmentAddress += partialLength;
|
|
lengthNeeded -= partialLength;
|
|
|
|
} while ( lengthNeeded != 0 );
|
|
|
|
//
|
|
// We just copied from the beginning of the current MDL.
|
|
//
|
|
|
|
mdlOffset = 0;
|
|
|
|
}
|
|
|
|
//
|
|
// Final preparation for the send depends on whether this is the
|
|
// last fragment.
|
|
//
|
|
|
|
if ( remainingLength != 0 ) {
|
|
|
|
//
|
|
// Not done. Update the current MDL position. If we have
|
|
// finished off the current MDL, move to the next one.
|
|
//
|
|
|
|
mdlOffset += partialLength;
|
|
if ( mdlOffset >= MmGetMdlByteCount(mdl) ) {
|
|
mdl = mdl->Next;
|
|
ASSERT( mdl != NULL );
|
|
mdlOffset = 0;
|
|
}
|
|
|
|
//
|
|
// Update context. Set up to restart after the send in this
|
|
// routine. We want do this as an FSD restart routine. But
|
|
// this may recurse, if the send doesn't pend, so we may use up
|
|
// the stack. If we are running out of stack, restart here in
|
|
// the FSP.
|
|
//
|
|
|
|
WorkContext->Parameters.ReadMpx.CurrentMdl = mdl;
|
|
WorkContext->Parameters.ReadMpx.CurrentMdlOffset = (USHORT)mdlOffset;
|
|
WorkContext->Parameters.ReadMpx.RemainingLength = remainingLength;
|
|
WorkContext->Parameters.ReadMpx.Offset += fragmentSize;
|
|
|
|
if ( IoGetRemainingStackSize() >= STACK_THRESHOLD ) {
|
|
DEBUG WorkContext->FsdRestartRoutine = NULL;
|
|
sendCompletionRoutine = SendMdlReadMpxFragment;
|
|
} else {
|
|
DEBUG WorkContext->FsdRestartRoutine = NULL;
|
|
WorkContext->FspRestartRoutine = SendMdlReadMpxFragment2;
|
|
sendCompletionRoutine = SrvQueueWorkToFspAtSendCompletion;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// This is the last fragment. Restart in the cleanup routine.
|
|
//
|
|
|
|
DEBUG WorkContext->FsdRestartRoutine = NULL;
|
|
WorkContext->FspRestartRoutine = RestartMdlReadMpxComplete;
|
|
sendCompletionRoutine = SrvQueueWorkToFspAtSendCompletion;
|
|
}
|
|
|
|
//
|
|
// Send the fragment.
|
|
//
|
|
|
|
WorkContext->ResponseBuffer->DataLength = // +1 for pad
|
|
sizeof(SMB_HEADER) + FIELD_OFFSET(RESP_READ_MPX,Buffer) + 1 + fragmentSize;
|
|
|
|
if ( WorkContext->Endpoint->IsConnectionless ) {
|
|
SrvIpxStartSend( WorkContext, sendCompletionRoutine );
|
|
} else {
|
|
SrvStartSend2( WorkContext, sendCompletionRoutine );
|
|
}
|
|
|
|
return(STATUS_MORE_PROCESSING_REQUIRED);
|
|
|
|
} // SendMdlReadMpxFragment
|
|
|
|
|
|
NTSTATUS
|
|
RestartCopyReadMpxComplete (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the final completion routine for Read Mpx when copy read is
|
|
used. It is called after the send of the last fragment completes.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to target device object for the request.
|
|
|
|
Irp - Pointer to I/O request packet
|
|
|
|
WorkContext - Caller-specified context parameter associated with IRP.
|
|
This is actually a pointer to a Work Context block.
|
|
|
|
Return Value:
|
|
|
|
STATUS_MORE_PROCESSING_REQUIRED.
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL oldIrql;
|
|
UNLOCKABLE_CODE( 8FIL );
|
|
|
|
//
|
|
// Check the status of the send completion.
|
|
//
|
|
|
|
CHECK_SEND_COMPLETION_STATUS( Irp->IoStatus.Status );
|
|
|
|
//
|
|
// Reset the IRP cancelled bit.
|
|
//
|
|
|
|
Irp->Cancel = FALSE;
|
|
|
|
ASSERT( !WorkContext->Parameters.ReadMpx.MdlRead );
|
|
|
|
//
|
|
// If we allocated a separate buffer to do the read, free it and its
|
|
// MDL now.
|
|
//
|
|
|
|
if ( WorkContext->Parameters.ReadMpx.MpxBuffer != NULL ) {
|
|
DEALLOCATE_NONPAGED_POOL( WorkContext->Parameters.ReadMpx.MpxBuffer );
|
|
IoFreeMdl( WorkContext->Parameters.ReadMpx.MpxBufferMdl );
|
|
}
|
|
|
|
WorkContext->ResponseBuffer->Mdl->Next = NULL;
|
|
|
|
//
|
|
// Complete and requeue the work item.
|
|
//
|
|
|
|
KeRaiseIrql( DISPATCH_LEVEL, &oldIrql );
|
|
SrvFsdRestartSmbComplete( WorkContext );
|
|
KeLowerIrql( oldIrql );
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
} // RestartCopyReadMpxComplete
|
|
|
|
|
|
VOID SRVFASTCALL
|
|
RestartMdlReadMpxComplete (
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the final completion routine for Read Mpx when MDL read is
|
|
used. It is called after the send of the last fragment completes.
|
|
|
|
Arguments:
|
|
|
|
WorkContext - Supplies a pointer to the work context block
|
|
describing server-specific context for the request.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
LARGE_INTEGER offset;
|
|
|
|
PAGED_CODE( );
|
|
|
|
ASSERT( WorkContext->Parameters.ReadMpx.MdlRead );
|
|
|
|
//
|
|
// Give the MDL back to the cache manager. (If the read failed or
|
|
// returned no data, there will be no MDL.)
|
|
//
|
|
|
|
MmPrepareMdlForReuse( WorkContext->ResponseBuffer->PartialMdl );
|
|
|
|
if ( WorkContext->Parameters.ReadMpx.FirstMdl != NULL ) {
|
|
//KdPrint(( "Freeing MDL chain:\n" ));
|
|
//DumpMdlChain( WorkContext->Parameters.ReadMpx.FirstMdl );
|
|
|
|
if( WorkContext->Rfcb->Lfcb->MdlReadComplete == NULL ||
|
|
|
|
WorkContext->Rfcb->Lfcb->MdlReadComplete(
|
|
WorkContext->Rfcb->Lfcb->FileObject,
|
|
WorkContext->Parameters.ReadMpx.FirstMdl,
|
|
WorkContext->Rfcb->Lfcb->DeviceObject ) == FALSE ) {
|
|
|
|
offset.QuadPart = WorkContext->Parameters.ReadMpx.Offset;
|
|
|
|
//
|
|
// Fast path didn't work, try an IRP...
|
|
//
|
|
status = SrvIssueMdlCompleteRequest( WorkContext, NULL,
|
|
WorkContext->Parameters.ReadMpx.FirstMdl,
|
|
IRP_MJ_READ,
|
|
&offset,
|
|
WorkContext->Parameters.ReadMpx.ReadLength
|
|
);
|
|
if( !NT_SUCCESS( status ) ) {
|
|
//
|
|
// All we can do is complain now!
|
|
//
|
|
SrvLogServiceFailure( SRV_SVC_MDL_COMPLETE, status );
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
WorkContext->ResponseBuffer->Mdl->Next = NULL;
|
|
|
|
//
|
|
// Free the work item by dereferencing it.
|
|
//
|
|
|
|
SrvDereferenceWorkItem( WorkContext );
|
|
return;
|
|
|
|
} // RestartMdlReadMpxComplete
|
|
|
|
VOID SRVFASTCALL
|
|
SrvRestartReceiveWriteMpx (
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine replaces the normal restart routine for TDI Receive
|
|
completion when a Write Mpx SMB is received over IPX. If a receive
|
|
error occurs, or if the SMB is invalid, it cleans up the active
|
|
write mpx state that was set up in SrvIpxServerDatagramHandler.
|
|
|
|
Arguments:
|
|
|
|
WorkContext - Supplies a pointer to the work context block
|
|
describing server-specific context for the request.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
SMB_STATUS smbStatus;
|
|
PCONNECTION connection;
|
|
PIRP irp;
|
|
PSMB_HEADER header;
|
|
ULONG length;
|
|
|
|
PAGED_CODE( );
|
|
|
|
connection = WorkContext->Connection;
|
|
irp = WorkContext->Irp;
|
|
|
|
//
|
|
// Save the length of the received message. Store the length
|
|
// in the work context block for statistics gathering.
|
|
//
|
|
|
|
length = (ULONG)irp->IoStatus.Information;
|
|
WorkContext->RequestBuffer->DataLength = length;
|
|
WorkContext->CurrentWorkQueue->stats.BytesReceived += length;
|
|
|
|
//
|
|
// Store in the work context block the time at which processing
|
|
// of the request began. Use the time that the work item was
|
|
// queued to the FSP for this purpose.
|
|
//
|
|
|
|
WorkContext->StartTime = WorkContext->Timestamp;
|
|
|
|
//
|
|
// Update the server network error count. If the TDI receive
|
|
// failed or was canceled, don't try to process an SMB.
|
|
//
|
|
|
|
status = irp->IoStatus.Status;
|
|
if ( irp->Cancel || !NT_SUCCESS(status) ) {
|
|
IF_DEBUG(NETWORK_ERRORS) {
|
|
KdPrint(( "SrvRestartReceiveWriteMpx: status = %X for IRP %p\n",
|
|
irp->IoStatus.Status, irp ));
|
|
}
|
|
SrvUpdateErrorCount( &SrvNetworkErrorRecord, TRUE );
|
|
if ( NT_SUCCESS(status) ) status = STATUS_CANCELLED;
|
|
goto cleanup;
|
|
}
|
|
|
|
SrvUpdateErrorCount( &SrvNetworkErrorRecord, FALSE );
|
|
|
|
//
|
|
// Initialize the error class and code fields in the header to
|
|
// indicate success.
|
|
//
|
|
|
|
header = WorkContext->ResponseHeader;
|
|
|
|
SmbPutUlong( &header->ErrorClass, STATUS_SUCCESS );
|
|
|
|
//
|
|
// If the connection is closing or the server is shutting down,
|
|
// ignore this SMB.
|
|
//
|
|
|
|
if ( (GET_BLOCK_STATE(connection) != BlockStateActive) ||
|
|
SrvFspTransitioning ) {
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Verify the SMB to make sure that it has a valid header, and that
|
|
// the word count and byte count are within range.
|
|
//
|
|
|
|
WorkContext->NextCommand = header->Command;
|
|
|
|
if ( !SrvValidateSmb( WorkContext ) ) {
|
|
IF_DEBUG(SMB_ERRORS) {
|
|
KdPrint(( "SrvRestartReceiveWriteMpx: Invalid SMB.\n" ));
|
|
KdPrint(( " SMB received from %z\n",
|
|
(PCSTRING)&WorkContext->Connection->OemClientMachineNameString ));
|
|
}
|
|
|
|
//
|
|
// The SMB is invalid. We send back an INVALID_SMB status.
|
|
//
|
|
|
|
status = STATUS_INVALID_SMB;
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Clear the flag that indicates the we just sent an oplock break II
|
|
// to none. This allows subsequent raw reads to be processed.
|
|
//
|
|
|
|
//not needed on IPX//connection->BreakIIToNoneJustSent = FALSE;
|
|
|
|
//
|
|
// Process the received SMB. The called routine is responsible
|
|
// for sending any response(s) that are needed and for getting
|
|
// the receive buffer back onto the receive queue as soon as
|
|
// possible.
|
|
//
|
|
|
|
smbStatus = SrvSmbWriteMpx( WorkContext );
|
|
ASSERT( smbStatus != SmbStatusMoreCommands );
|
|
|
|
if ( smbStatus != SmbStatusInProgress ) {
|
|
//
|
|
// Return the TransportContext
|
|
//
|
|
if ( WorkContext->Parameters.WriteMpx.TransportContext ) {
|
|
TdiReturnChainedReceives( &WorkContext->Parameters.WriteMpx.TransportContext,
|
|
1
|
|
);
|
|
}
|
|
SrvEndSmbProcessing( WorkContext, smbStatus );
|
|
}
|
|
|
|
return;
|
|
|
|
cleanup:
|
|
|
|
//
|
|
// We will not be processing this write. We still need to check
|
|
// for whether this is the last Write Mpx active on the RFCB, and
|
|
// if so, send the response to the write.
|
|
//
|
|
// *** Note that if we are here because we received an invalid
|
|
// SMB, the completion of the Write Mpx overrides the sending
|
|
// of an error response.
|
|
//
|
|
|
|
//
|
|
// Return the TransportContext
|
|
//
|
|
if ( WorkContext->Parameters.WriteMpx.TransportContext ) {
|
|
TdiReturnChainedReceives( &WorkContext->Parameters.WriteMpx.TransportContext,
|
|
1
|
|
);
|
|
}
|
|
|
|
if ( CheckForWriteMpxComplete( WorkContext ) ) {
|
|
SrvFsdSendResponse( WorkContext );
|
|
} else if ( status == STATUS_INVALID_SMB ) {
|
|
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
|
|
SrvFsdSendResponse( WorkContext );
|
|
} else {
|
|
SrvDereferenceWorkItem( WorkContext );
|
|
}
|
|
|
|
return;
|
|
|
|
} // SrvRestartReceiveWriteMpx
|
|
|
|
|
|
SMB_PROCESSOR_RETURN_TYPE
|
|
SrvSmbWriteMpx (
|
|
SMB_PROCESSOR_PARAMETERS
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Processes the Write Mpx SMB.
|
|
|
|
*** The server currently does not support multiplexed mode reads and
|
|
writes on connection-based transports. When such requests are
|
|
received, the error "use standard mode" is returned.
|
|
Multiplexed mode turns out not to be the performance win it was
|
|
thought to be (on local nets), so we haven't implemented it,
|
|
except over IPX.
|
|
|
|
Arguments:
|
|
|
|
WorkContext - Supplies a pointer to the work context block
|
|
representing the work item
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PSMB_HEADER header;
|
|
PREQ_WRITE_MPX request;
|
|
PRESP_WRITE_MPX_DATAGRAM response;
|
|
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
SMB_STATUS SmbStatus = SmbStatusInProgress;
|
|
USHORT fid;
|
|
USHORT mid;
|
|
PRFCB rfcb;
|
|
PLFCB lfcb;
|
|
PWRITE_MPX_CONTEXT writeMpx;
|
|
CLONG bufferOffset;
|
|
PCHAR writeAddress;
|
|
USHORT writeLength;
|
|
ULONG key;
|
|
LARGE_INTEGER offset;
|
|
USHORT writeMode;
|
|
BOOLEAN writeThrough;
|
|
KIRQL oldIrql;
|
|
PMDL mdl;
|
|
|
|
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
|
|
WorkContext->PreviousSMB = EVENT_TYPE_SMB_WRITE_MPX;
|
|
SrvWmiStartContext(WorkContext);
|
|
|
|
header = WorkContext->RequestHeader;
|
|
request = (PREQ_WRITE_MPX)WorkContext->RequestParameters;
|
|
|
|
fid = SmbGetUshort( &request->Fid );
|
|
|
|
IF_SMB_DEBUG(MPX1) {
|
|
KdPrint(( "Write Block Multipliexed request; FID 0x%lx, "
|
|
"count %ld, offset %ld\n",
|
|
fid, SmbGetUshort( &request->Count ),
|
|
SmbGetUlong( &request->Offset ) ));
|
|
}
|
|
|
|
//
|
|
// Verify the FID. If verified, the RFCB is referenced and its
|
|
// address is stored in the WorkContext block, and the RFCB
|
|
// address is returned.
|
|
//
|
|
|
|
writeMode = SmbGetUshort( &request->WriteMode );
|
|
|
|
if( (writeMode & SMB_WMODE_DATAGRAM) == 0 ||
|
|
!WorkContext->Endpoint->IsConnectionless ) {
|
|
|
|
SrvFsdBuildWriteCompleteResponse( WorkContext, STATUS_SMB_USE_STANDARD, 0 );
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
|
|
rfcb = SrvVerifyFid(
|
|
WorkContext,
|
|
fid,
|
|
TRUE,
|
|
SrvRestartSmbReceived, // serialize with raw write
|
|
&status
|
|
);
|
|
|
|
if ( rfcb == SRV_INVALID_RFCB_POINTER) {
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
//
|
|
// Invalid file ID or write behind error. Reject the request.
|
|
//
|
|
|
|
IF_DEBUG(ERRORS) {
|
|
KdPrint((
|
|
"SrvSmbWriteMpx: Status %X on FID: 0x%lx\n",
|
|
status,
|
|
fid
|
|
));
|
|
}
|
|
|
|
goto error;
|
|
}
|
|
|
|
//
|
|
// The work item has been queued because a raw write is in
|
|
// progress.
|
|
//
|
|
|
|
SmbStatus = SmbStatusInProgress;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if( rfcb->Lfcb->Session->IsSessionExpired )
|
|
{
|
|
status = SESSION_EXPIRED_STATUS_CODE;
|
|
SrvFsdBuildWriteCompleteResponse( WorkContext, status, 0 );
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Verify that the client has write access to the file via the
|
|
// specified handle.
|
|
//
|
|
|
|
if( !rfcb->MpxWritesOk ) {
|
|
|
|
if ( !rfcb->WriteAccessGranted && !rfcb->AppendAccessGranted ) {
|
|
SrvStatistics.GrantedAccessErrors++;
|
|
IF_DEBUG(ERRORS) {
|
|
KdPrint(( "SrvSmbWriteMpx: Write access not granted.\n"));
|
|
}
|
|
status = STATUS_ACCESS_DENIED;
|
|
goto error;
|
|
}
|
|
|
|
//
|
|
// If this is not a disk or a print file tell the client to use core write.
|
|
//
|
|
|
|
if ( rfcb->ShareType != ShareTypeDisk &&
|
|
rfcb->ShareType != ShareTypePrint ) {
|
|
|
|
status = STATUS_SMB_USE_STANDARD;
|
|
goto error;
|
|
}
|
|
|
|
rfcb->MpxWritesOk = TRUE;
|
|
}
|
|
|
|
rfcb->WrittenTo = TRUE;
|
|
|
|
//
|
|
// If this a stale packet, ignore it. Stale here means that the MID
|
|
// of the packet is not equal to the MID of the current write mux.
|
|
// Such a packet can be received if a duplicate packet from a
|
|
// previous write mux is delivered after a new write mux starts.
|
|
//
|
|
|
|
writeMpx = &rfcb->WriteMpx;
|
|
|
|
mid = SmbGetAlignedUshort( &header->Mid );
|
|
|
|
if ( mid != writeMpx->Mid ) {
|
|
|
|
//
|
|
// Set the sequence number to 0 so that we don't send a response
|
|
// unless we have to because the Write Mpx refcount drops to 0.
|
|
//
|
|
|
|
SmbPutAlignedUshort( &header->SequenceNumber, 0 );
|
|
goto error;
|
|
}
|
|
|
|
//
|
|
// Get the file offset.
|
|
//
|
|
|
|
offset.QuadPart = SmbGetUlong( &request->Offset );
|
|
|
|
//
|
|
// Determine the amount of data to write. This is the minimum of
|
|
// the amount requested by the client and the amount of data
|
|
// actually sent in the request buffer.
|
|
//
|
|
|
|
bufferOffset = SmbGetUshort( &request->DataOffset );
|
|
|
|
//
|
|
// If we have the transport context, then setup WriteAddress accordingly.
|
|
//
|
|
|
|
WorkContext->Parameters.WriteMpx.DataMdl = NULL;
|
|
|
|
if ( WorkContext->Parameters.WriteMpx.TransportContext ) {
|
|
|
|
writeAddress = (PCHAR)WorkContext->Parameters.WriteMpx.Buffer + bufferOffset;
|
|
|
|
} else {
|
|
|
|
writeAddress = (PCHAR)header + bufferOffset;
|
|
|
|
}
|
|
|
|
writeLength =
|
|
(USHORT)(MIN( (CLONG)SmbGetUshort( &request->DataLength ),
|
|
WorkContext->ResponseBuffer->DataLength - bufferOffset ));
|
|
|
|
//
|
|
// Save context for the restart routine.
|
|
//
|
|
|
|
WorkContext->Parameters.WriteMpx.WriteLength = writeLength;
|
|
|
|
//
|
|
// Form the lock key using the FID and the PID.
|
|
//
|
|
// *** The FID must be included in the key in order to account for
|
|
// the folding of multiple remote compatibility mode opens into
|
|
// a single local open.
|
|
//
|
|
|
|
key = rfcb->ShiftedFid | SmbGetAlignedUshort( &header->Pid );
|
|
|
|
//
|
|
// If this is the first packet of a new MID, set up to glom the
|
|
// packets into one big write.
|
|
//
|
|
|
|
lfcb = rfcb->Lfcb;
|
|
|
|
if ( WorkContext->Parameters.WriteMpx.FirstPacketOfGlom ) {
|
|
|
|
//
|
|
// Try the fast path first.
|
|
//
|
|
|
|
WorkContext->Irp->MdlAddress = NULL;
|
|
WorkContext->Irp->IoStatus.Information = 0;
|
|
|
|
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastWritesAttempted );
|
|
|
|
ASSERT( lfcb->FileObject->Flags & FO_CACHE_SUPPORTED );
|
|
|
|
writeLength = SmbGetUshort( &request->Count );
|
|
|
|
writeMpx->StartOffset = offset.LowPart;
|
|
writeMpx->Length = writeLength;
|
|
|
|
if ( lfcb->PrepareMdlWrite &&
|
|
lfcb->PrepareMdlWrite(
|
|
lfcb->FileObject,
|
|
&offset,
|
|
writeLength,
|
|
key,
|
|
&WorkContext->Irp->MdlAddress,
|
|
&WorkContext->Irp->IoStatus,
|
|
lfcb->DeviceObject
|
|
) && WorkContext->Irp->MdlAddress != NULL ) {
|
|
|
|
//
|
|
// The fast I/O path worked.
|
|
//
|
|
|
|
WorkContext->bAlreadyTrace = TRUE;
|
|
RestartPrepareMpxMdlWrite( WorkContext );
|
|
SmbStatus = SmbStatusInProgress;
|
|
goto Cleanup;
|
|
}
|
|
|
|
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastWritesFailed );
|
|
|
|
//
|
|
// The fast I/O path failed. Build the write request, reusing the
|
|
// receive IRP.
|
|
//
|
|
// The fast path may have partially succeeded, returning a partial
|
|
// MDL chain. We need to adjust our write request to account for
|
|
// that.
|
|
//
|
|
|
|
offset.QuadPart += WorkContext->Irp->IoStatus.Information;
|
|
|
|
writeLength -= (USHORT)WorkContext->Irp->IoStatus.Information;
|
|
|
|
SrvBuildReadOrWriteRequest(
|
|
WorkContext->Irp, // input IRP address
|
|
lfcb->FileObject, // target file object address
|
|
WorkContext, // context
|
|
IRP_MJ_WRITE, // major function code
|
|
IRP_MN_MDL, // minor function code
|
|
NULL, // buffer address (ignored)
|
|
writeLength, // buffer length
|
|
WorkContext->Irp->MdlAddress, // MDL address
|
|
offset, // byte offset
|
|
key // lock key
|
|
);
|
|
|
|
//
|
|
// Pass the request to the file system.
|
|
//
|
|
|
|
WorkContext->bAlreadyTrace = TRUE;
|
|
WorkContext->FsdRestartRoutine = RestartPrepareMpxMdlWrite;
|
|
DEBUG WorkContext->FspRestartRoutine = NULL;
|
|
|
|
(VOID)IoCallDriver( lfcb->DeviceObject, WorkContext->Irp );
|
|
|
|
//
|
|
// The MDL write has been started. When it completes, processing
|
|
// resumes at RestartPrepareMpxMdlWrite.
|
|
//
|
|
|
|
SmbStatus = SmbStatusInProgress;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Save context for the restart routine.
|
|
//
|
|
|
|
WorkContext->Parameters.WriteMpx.Offset = offset.LowPart;
|
|
WorkContext->Parameters.WriteMpx.Mid = mid;
|
|
|
|
if ( writeMpx->GlomPending ) {
|
|
|
|
//
|
|
// A glom setup is pending. Wait for that to complete.
|
|
//
|
|
|
|
ACQUIRE_SPIN_LOCK( &rfcb->Connection->SpinLock, &oldIrql );
|
|
|
|
if ( writeMpx->GlomPending ) {
|
|
InsertTailList(
|
|
&writeMpx->GlomDelayList,
|
|
&WorkContext->ListEntry
|
|
);
|
|
RELEASE_SPIN_LOCK( &rfcb->Connection->SpinLock, oldIrql );
|
|
SmbStatus = SmbStatusInProgress;
|
|
goto Cleanup;
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK( &rfcb->Connection->SpinLock, oldIrql );
|
|
|
|
}
|
|
|
|
if ( writeMpx->Glomming ) {
|
|
|
|
//
|
|
// We're glomming this into one big write. Add the data from
|
|
// this packet.
|
|
//
|
|
|
|
AddPacketToGlom( WorkContext );
|
|
SmbStatus = SmbStatusInProgress;
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// We are not glomming this write, because we missed the first
|
|
// packet of the write. So we write each block as it arrives.
|
|
//
|
|
// If the file's writethrough mode needs to be changed, do so now.
|
|
//
|
|
|
|
writeThrough = (BOOLEAN)((writeMode & SMB_WMODE_WRITE_THROUGH) != 0);
|
|
|
|
if ( writeThrough && (lfcb->FileMode & FILE_WRITE_THROUGH) == 0
|
|
|| !writeThrough && (lfcb->FileMode & FILE_WRITE_THROUGH) != 0 ) {
|
|
|
|
SrvSetFileWritethroughMode( lfcb, writeThrough );
|
|
|
|
}
|
|
|
|
//
|
|
// Try the fast I/O path first. If that fails, fall through to the
|
|
// normal build-an-IRP path.
|
|
//
|
|
|
|
if ( lfcb->FastIoWrite != NULL ) {
|
|
|
|
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastWritesAttempted );
|
|
|
|
try {
|
|
if ( lfcb->FastIoWrite(
|
|
lfcb->FileObject,
|
|
&offset,
|
|
writeLength,
|
|
TRUE,
|
|
key,
|
|
writeAddress,
|
|
&WorkContext->Irp->IoStatus,
|
|
lfcb->DeviceObject
|
|
) ) {
|
|
|
|
//
|
|
// The fast I/O path worked. Call the restart routine directly
|
|
// to do postprocessing (including sending the response).
|
|
//
|
|
|
|
WorkContext->bAlreadyTrace = TRUE;
|
|
RestartWriteMpx( WorkContext );
|
|
SmbStatus = SmbStatusInProgress;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
// Fall through to the slow path on an exception
|
|
status = GetExceptionCode();
|
|
IF_DEBUG(ERRORS) {
|
|
KdPrint(("FastIoRead threw exception %x\n", status ));
|
|
}
|
|
}
|
|
|
|
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastWritesFailed );
|
|
|
|
}
|
|
|
|
//
|
|
// The turbo path failed. Build the write request, reusing the
|
|
// receive IRP.
|
|
//
|
|
// Build an MDL describing the write buffer. Note that if the file
|
|
// system can complete the write immediately, the MDL isn't really
|
|
// needed, but if the file system must send the request to its FSP,
|
|
// the MDL _is_ needed.
|
|
//
|
|
// *** Note the assumption that the request buffer already has a
|
|
// valid full MDL from which a partial MDL can be built.
|
|
//
|
|
|
|
if ( WorkContext->Parameters.WriteMpx.TransportContext ) {
|
|
|
|
mdl = IoAllocateMdl(
|
|
writeAddress,
|
|
writeLength,
|
|
FALSE,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
|
|
if ( mdl == NULL ) {
|
|
status = STATUS_INSUFF_SERVER_RESOURCES;
|
|
goto error;
|
|
}
|
|
|
|
//
|
|
// Build the mdl.
|
|
//
|
|
|
|
MmBuildMdlForNonPagedPool( mdl );
|
|
|
|
WorkContext->Parameters.WriteMpx.DataMdl = mdl;
|
|
|
|
} else {
|
|
|
|
mdl = WorkContext->RequestBuffer->PartialMdl;
|
|
|
|
IoBuildPartialMdl(
|
|
WorkContext->RequestBuffer->Mdl,
|
|
mdl,
|
|
writeAddress,
|
|
writeLength
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// Build the IRP.
|
|
//
|
|
|
|
SrvBuildReadOrWriteRequest(
|
|
WorkContext->Irp, // input IRP address
|
|
lfcb->FileObject, // target file object address
|
|
WorkContext, // context
|
|
IRP_MJ_WRITE, // major function code
|
|
0, // minor function code
|
|
writeAddress, // buffer address
|
|
writeLength, // buffer length
|
|
mdl, // MDL address
|
|
offset, // byte offset
|
|
key // lock key
|
|
);
|
|
|
|
//
|
|
// Pass the request to the file system.
|
|
//
|
|
|
|
WorkContext->bAlreadyTrace = TRUE;
|
|
WorkContext->FsdRestartRoutine = RestartWriteMpx;
|
|
DEBUG WorkContext->FspRestartRoutine = NULL;
|
|
|
|
IoCallDriver( lfcb->DeviceObject, WorkContext->Irp );
|
|
|
|
//
|
|
// The write has been started. Control will return to
|
|
// RestartWriteMpx when the write completes.
|
|
//
|
|
|
|
SmbStatus = SmbStatusInProgress;
|
|
goto Cleanup;
|
|
|
|
error:
|
|
|
|
//
|
|
// There is an error of some sort. We still need to check for
|
|
// whether this is the last Write Mpx active on the RFCB, and if so,
|
|
// send the response to the write instead of the error. If this is
|
|
// not the last active mux request, then we either send an error
|
|
// response (non-datagram write mux or sequenced write mux) or
|
|
// ignore this request (unsequenced datagram). Note that if this is
|
|
// a non-datagram write mux, then we didn't come in over IPX, and we
|
|
// didn't bump the Write Mpx refcount.
|
|
//
|
|
|
|
//
|
|
// Return the TransportContext
|
|
//
|
|
if ( WorkContext->Parameters.WriteMpx.TransportContext ) {
|
|
TdiReturnChainedReceives( &WorkContext->Parameters.WriteMpx.TransportContext,
|
|
1
|
|
);
|
|
}
|
|
|
|
if ( WorkContext->Rfcb && CheckForWriteMpxComplete( WorkContext ) ) {
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( SmbGetAlignedUshort(&header->SequenceNumber) != 0 ) {
|
|
SrvSetSmbError2( WorkContext, status, TRUE );
|
|
response = (PRESP_WRITE_MPX_DATAGRAM)WorkContext->ResponseParameters;
|
|
response->WordCount = 2;
|
|
SmbPutUlong( &response->Mask, 0 );
|
|
SmbPutUshort( &response->ByteCount, 0 );
|
|
WorkContext->ResponseParameters = NEXT_LOCATION(
|
|
response,
|
|
RESP_WRITE_MPX_DATAGRAM,
|
|
0
|
|
);
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
|
|
SmbStatus = SmbStatusNoResponse;
|
|
|
|
Cleanup:
|
|
SrvWmiEndContext(WorkContext);
|
|
return SmbStatus;
|
|
} // SrvSmbWriteMpx
|
|
|
|
|
|
VOID SRVFASTCALL
|
|
RestartWriteMpx (
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Processes file write completion for a Write MPX SMB.
|
|
|
|
Arguments:
|
|
|
|
WorkContext - Supplies a pointer to the work context block
|
|
describing server-specific context for the request.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PSMB_HEADER header;
|
|
PREQ_WRITE_MPX request;
|
|
BOOLEAN rfcbClosing;
|
|
PRESP_WRITE_MPX_DATAGRAM response;
|
|
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
SMB_STATUS SmbStatus = SmbStatusInProgress;
|
|
PRFCB rfcb;
|
|
PWRITE_MPX_CONTEXT writeMpx;
|
|
PCONNECTION connection;
|
|
KIRQL oldIrql;
|
|
USHORT writeLength;
|
|
LARGE_INTEGER position;
|
|
USHORT sequenceNumber;
|
|
BOOLEAN bNeedTrace = (WorkContext->bAlreadyTrace == FALSE);
|
|
|
|
IF_DEBUG(FSD2) KdPrint(( " - RestartWriteMpx\n" ));
|
|
if (bNeedTrace) {
|
|
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
|
|
WorkContext->PreviousSMB = EVENT_TYPE_SMB_WRITE_MPX;
|
|
SrvWmiStartContext(WorkContext);
|
|
}
|
|
else
|
|
WorkContext->bAlreadyTrace = FALSE;
|
|
|
|
header = WorkContext->RequestHeader;
|
|
request = (PREQ_WRITE_MPX)WorkContext->RequestParameters;
|
|
response = (PRESP_WRITE_MPX_DATAGRAM)WorkContext->ResponseParameters;
|
|
|
|
rfcb = WorkContext->Rfcb;
|
|
connection = WorkContext->Connection;
|
|
|
|
status = WorkContext->Irp->IoStatus.Status;
|
|
|
|
//
|
|
// Return the TransportContext
|
|
//
|
|
if ( WorkContext->Parameters.WriteMpx.TransportContext ) {
|
|
TdiReturnChainedReceives( &WorkContext->Parameters.WriteMpx.TransportContext,
|
|
1
|
|
);
|
|
WorkContext->Parameters.WriteMpx.TransportContext = NULL;
|
|
}
|
|
|
|
//
|
|
// Free the data Mdl.
|
|
//
|
|
|
|
if ( WorkContext->Parameters.WriteMpx.DataMdl ) {
|
|
IoFreeMdl( WorkContext->Parameters.WriteMpx.DataMdl );
|
|
WorkContext->Parameters.WriteMpx.DataMdl = NULL;
|
|
}
|
|
|
|
KeRaiseIrql( DISPATCH_LEVEL, &oldIrql );
|
|
ACQUIRE_DPC_SPIN_LOCK( &connection->SpinLock );
|
|
|
|
//
|
|
// If we're entered at dispatch level, and the write failed,
|
|
// or there is a saved error, or the rfcb is closing, then
|
|
// we need to have a worker thread call this routine.
|
|
//
|
|
|
|
if ( ((status != STATUS_SUCCESS) ||
|
|
(rfcb->SavedError != STATUS_SUCCESS) ||
|
|
(GET_BLOCK_STATE(rfcb) != BlockStateActive)) &&
|
|
(oldIrql >= DISPATCH_LEVEL) ) {
|
|
|
|
RELEASE_DPC_SPIN_LOCK( &connection->SpinLock );
|
|
WorkContext->FspRestartRoutine = RestartWriteMpx;
|
|
QUEUE_WORK_TO_FSP( WorkContext );
|
|
KeLowerIrql( oldIrql );
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// If this write is from a previous mux (meaning that a new one was
|
|
// started while we were doing this write), toss this request.
|
|
//
|
|
|
|
writeMpx = &rfcb->WriteMpx;
|
|
|
|
if ( WorkContext->Parameters.WriteMpx.Mid != writeMpx->Mid ) {
|
|
goto check_for_mux_end;
|
|
}
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
//
|
|
// The write failed. Remember the failure in the RFCB.
|
|
//
|
|
|
|
IF_DEBUG(ERRORS) KdPrint(( "Write failed: %X\n", status ));
|
|
|
|
if ( rfcb->SavedError == STATUS_SUCCESS ) {
|
|
rfcb->SavedError = status;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// The write succeeded. Update the information in the write mpx
|
|
// context block.
|
|
//
|
|
// !!! Need to deal with mask shifting by the redir and delayed
|
|
// packets.
|
|
//
|
|
|
|
#if 0
|
|
MpxDelay = !MpxDelay;
|
|
if ( MpxDelay ) {
|
|
LARGE_INTEGER interval;
|
|
interval.QuadPart = -10*1000*100;
|
|
KeDelayExecutionThread( KernelMode, FALSE, &interval );
|
|
}
|
|
#endif
|
|
writeMpx->Mask |= SmbGetUlong( &request->Mask );
|
|
|
|
}
|
|
|
|
//
|
|
// Save the count of bytes written, to be used to update the server
|
|
// statistics database.
|
|
//
|
|
|
|
writeLength = (USHORT)WorkContext->Irp->IoStatus.Information;
|
|
UPDATE_WRITE_STATS( WorkContext, writeLength );
|
|
|
|
IF_SMB_DEBUG(MPX1) {
|
|
KdPrint(( "RestartWriteMpx: Fid 0x%lx, wrote %ld bytes\n",
|
|
rfcb->Fid, writeLength ));
|
|
}
|
|
|
|
//
|
|
// If this is an unsequenced request, we're done. We don't respond
|
|
// until we get a sequenced request.
|
|
//
|
|
|
|
sequenceNumber = SmbGetAlignedUshort( &header->SequenceNumber );
|
|
|
|
if ( sequenceNumber == 0 ) {
|
|
goto check_for_mux_end;
|
|
}
|
|
|
|
//
|
|
// This is the last request in this mux sent by the client. Save
|
|
// the sequence number and update the file position.
|
|
//
|
|
|
|
writeMpx->SequenceNumber = sequenceNumber;
|
|
|
|
rfcb->CurrentPosition = WorkContext->Parameters.WriteMpx.Offset + writeLength;
|
|
|
|
|
|
check_for_mux_end:
|
|
|
|
//
|
|
// If we have received the sequenced command for this write mux,
|
|
// and this is the last active command, then it's time to send
|
|
// the response. Otherwise, we are done with this SMB.
|
|
//
|
|
|
|
if ( --writeMpx->ReferenceCount != 0 ) {
|
|
|
|
RELEASE_DPC_SPIN_LOCK( &connection->SpinLock );
|
|
SrvFsdRestartSmbComplete( WorkContext );
|
|
KeLowerIrql( oldIrql );
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// WriteMpx refcount is 0.
|
|
//
|
|
|
|
rfcbClosing = (GET_BLOCK_STATE(rfcb) != BlockStateActive);
|
|
|
|
if ( writeMpx->SequenceNumber == 0 ) {
|
|
|
|
//
|
|
// If the rfcb is closing, complete the cleanup.
|
|
//
|
|
|
|
RELEASE_DPC_SPIN_LOCK( &connection->SpinLock );
|
|
KeLowerIrql( oldIrql );
|
|
|
|
if ( rfcbClosing ) {
|
|
RestartWriteMpxCompleteRfcbClose( WorkContext );
|
|
}
|
|
|
|
if( oldIrql >= DISPATCH_LEVEL ) {
|
|
SrvFsdRestartSmbComplete( WorkContext );
|
|
} else {
|
|
SrvRestartFsdComplete( WorkContext );
|
|
}
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// We are done with this write mux. Save the accumulated mask, the
|
|
// sequence number, and the original MID, then clear the mask and
|
|
// sequence number to indicate that we no longer are in the middle
|
|
// of a write mux.
|
|
//
|
|
|
|
SmbPutUlong( &response->Mask, writeMpx->Mask );
|
|
writeMpx->Mask = 0;
|
|
|
|
SmbPutAlignedUshort( &header->SequenceNumber, writeMpx->SequenceNumber );
|
|
writeMpx->SequenceNumber = 0;
|
|
|
|
SmbPutAlignedUshort( &header->Mid, writeMpx->Mid );
|
|
|
|
//
|
|
// Save the status.
|
|
//
|
|
|
|
status = rfcb->SavedError;
|
|
rfcb->SavedError = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Now we can release the lock.
|
|
//
|
|
|
|
RELEASE_DPC_SPIN_LOCK( &connection->SpinLock );
|
|
KeLowerIrql( oldIrql );
|
|
|
|
//
|
|
// Complete the rfcb close.
|
|
//
|
|
|
|
if ( rfcbClosing ) {
|
|
|
|
RestartWriteMpxCompleteRfcbClose( WorkContext );
|
|
}
|
|
|
|
//
|
|
// Build the response message.
|
|
//
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
SrvSetSmbError2( WorkContext, status, TRUE );
|
|
}
|
|
|
|
response->WordCount = 2;
|
|
SmbPutUshort( &response->ByteCount, 0 );
|
|
WorkContext->ResponseParameters = NEXT_LOCATION(
|
|
response,
|
|
RESP_WRITE_MPX_DATAGRAM,
|
|
0
|
|
);
|
|
|
|
//
|
|
// Send the response.
|
|
//
|
|
|
|
SrvFsdSendResponse( WorkContext );
|
|
|
|
Cleanup:
|
|
if (bNeedTrace) {
|
|
SrvWmiEndContext(WorkContext);
|
|
}
|
|
return;
|
|
|
|
} // RestartWriteMpx
|
|
|
|
BOOLEAN
|
|
CheckForWriteMpxComplete (
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
)
|
|
{
|
|
PSMB_HEADER header;
|
|
PRESP_WRITE_MPX_DATAGRAM response;
|
|
|
|
NTSTATUS status;
|
|
PRFCB rfcb = WorkContext->Rfcb;
|
|
PWRITE_MPX_CONTEXT writeMpx = &rfcb->WriteMpx;
|
|
PCONNECTION connection = WorkContext->Connection;
|
|
KIRQL oldIrql;
|
|
|
|
//
|
|
// If we have not received the sequenced command for this write mux,
|
|
// or this is not the last active command, then return FALSE.
|
|
// Otherwise, it's time to send the response, so build it and return
|
|
// TRUE.
|
|
//
|
|
|
|
ACQUIRE_SPIN_LOCK( &connection->SpinLock, &oldIrql );
|
|
|
|
if ( --writeMpx->ReferenceCount != 0 ) {
|
|
|
|
RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// WriteMpx refcount is 0.
|
|
//
|
|
|
|
if ( writeMpx->SequenceNumber == 0 ) {
|
|
|
|
//
|
|
// If the rfcb is closing, complete the cleanup.
|
|
//
|
|
|
|
if ( GET_BLOCK_STATE(rfcb) != BlockStateActive ) {
|
|
RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
|
|
RestartWriteMpxCompleteRfcbClose( WorkContext );
|
|
} else {
|
|
RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// We are done with this write mux. Save the accumulated mask, the
|
|
// sequence number, and the original MID, then clear the mask and
|
|
// sequence number to indicate that we no longer are in the middle
|
|
// of a write mux.
|
|
//
|
|
|
|
header = WorkContext->ResponseHeader;
|
|
response = (PRESP_WRITE_MPX_DATAGRAM)WorkContext->ResponseParameters;
|
|
|
|
SmbPutUlong( &response->Mask, writeMpx->Mask );
|
|
writeMpx->Mask = 0;
|
|
|
|
SmbPutAlignedUshort( &header->SequenceNumber, writeMpx->SequenceNumber );
|
|
writeMpx->SequenceNumber = 0;
|
|
|
|
SmbPutAlignedUshort( &header->Mid, writeMpx->Mid );
|
|
|
|
//
|
|
// Save the status.
|
|
//
|
|
|
|
status = rfcb->SavedError;
|
|
rfcb->SavedError = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Now we can release the lock.
|
|
//
|
|
|
|
if ( GET_BLOCK_STATE(rfcb) != BlockStateActive ) {
|
|
|
|
RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
|
|
RestartWriteMpxCompleteRfcbClose( WorkContext );
|
|
|
|
} else {
|
|
|
|
RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
|
|
}
|
|
|
|
//
|
|
// Build the response message.
|
|
//
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
SrvSetSmbError2( WorkContext, status, TRUE );
|
|
}
|
|
|
|
response->WordCount = 2;
|
|
SmbPutUshort( &response->ByteCount, 0 );
|
|
WorkContext->ResponseParameters = NEXT_LOCATION(
|
|
response,
|
|
RESP_WRITE_MPX_DATAGRAM,
|
|
0
|
|
);
|
|
|
|
return TRUE;
|
|
|
|
} // CheckForWriteMpxComplete
|
|
|
|
VOID SRVFASTCALL
|
|
RestartPrepareMpxMdlWrite (
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
)
|
|
{
|
|
PSMB_HEADER header;
|
|
PREQ_WRITE_MPX request;
|
|
|
|
PRFCB rfcb;
|
|
PWRITE_MPX_CONTEXT writeMpx;
|
|
PCONNECTION connection;
|
|
PLIST_ENTRY listEntry;
|
|
PWORK_CONTEXT workContext;
|
|
USHORT writeLength;
|
|
PCHAR writeAddress;
|
|
KIRQL oldIrql;
|
|
ULONG bytesCopied;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
SMB_STATUS SmbStatus = SmbStatusInProgress;
|
|
PMDL mdl;
|
|
BOOLEAN bNeedTrace = (WorkContext->bAlreadyTrace == FALSE);
|
|
|
|
if (bNeedTrace) {
|
|
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
|
|
WorkContext->PreviousSMB = EVENT_TYPE_SMB_WRITE_MPX;
|
|
SrvWmiStartContext(WorkContext);
|
|
}
|
|
else
|
|
WorkContext->bAlreadyTrace = FALSE;
|
|
header = WorkContext->RequestHeader;
|
|
request = (PREQ_WRITE_MPX)WorkContext->RequestParameters;
|
|
|
|
rfcb = WorkContext->Rfcb;
|
|
writeMpx = &rfcb->WriteMpx;
|
|
connection = WorkContext->Connection;
|
|
|
|
//
|
|
// If the MDL write preparation succeeded, copy the data from this
|
|
// packet into the cache. If it failed, toss this packet.
|
|
//
|
|
|
|
if( NT_SUCCESS(WorkContext->Irp->IoStatus.Status) ) {
|
|
|
|
mdl = WorkContext->Irp->MdlAddress;
|
|
#if DBG
|
|
IF_SMB_DEBUG(MPX2) {
|
|
KdPrint(( "RestartPrepareMpxMdlWrite: rfcb %p, input chain:\n", rfcb ));
|
|
DumpMdlChain( mdl );
|
|
}
|
|
#endif
|
|
writeMpx->MdlChain = mdl;
|
|
writeMpx->NumberOfRuns = 1;
|
|
writeMpx->RunList[0].Offset = 0;
|
|
writeLength = WorkContext->Parameters.WriteMpx.WriteLength;
|
|
writeMpx->RunList[0].Length = writeLength;
|
|
|
|
//
|
|
// If we have the transport context, setup writeAddress accordingly.
|
|
//
|
|
|
|
if ( WorkContext->Parameters.WriteMpx.TransportContext ) {
|
|
|
|
writeAddress = (PCHAR)WorkContext->Parameters.WriteMpx.Buffer +
|
|
SmbGetUshort( &request->DataOffset );
|
|
} else {
|
|
|
|
writeAddress = (PCHAR)WorkContext->ResponseHeader +
|
|
SmbGetUshort( &request->DataOffset );
|
|
}
|
|
|
|
status = TdiCopyBufferToMdl(
|
|
writeAddress,
|
|
0,
|
|
writeLength,
|
|
mdl,
|
|
0,
|
|
&bytesCopied
|
|
);
|
|
ASSERT( status == STATUS_SUCCESS );
|
|
ASSERT( bytesCopied == writeLength );
|
|
|
|
KeRaiseIrql( DISPATCH_LEVEL, &oldIrql );
|
|
ACQUIRE_DPC_SPIN_LOCK( &connection->SpinLock );
|
|
writeMpx->Glomming = TRUE;
|
|
|
|
ASSERT( writeMpx->Mask == 0 );
|
|
writeMpx->Mask = SmbGetUlong( &request->Mask );
|
|
|
|
--writeMpx->ReferenceCount;
|
|
ASSERT( writeMpx->SequenceNumber == 0 );
|
|
|
|
} else {
|
|
|
|
KeRaiseIrql( DISPATCH_LEVEL, &oldIrql );
|
|
ACQUIRE_DPC_SPIN_LOCK( &connection->SpinLock );
|
|
|
|
if ( rfcb->SavedError == STATUS_SUCCESS ) {
|
|
rfcb->SavedError = WorkContext->Irp->IoStatus.Status;
|
|
}
|
|
|
|
--writeMpx->ReferenceCount;
|
|
writeMpx->Glomming = FALSE;
|
|
}
|
|
|
|
//
|
|
// Return the TransportContext
|
|
//
|
|
if ( WorkContext->Parameters.WriteMpx.TransportContext ) {
|
|
TdiReturnChainedReceives( &WorkContext->Parameters.WriteMpx.TransportContext,
|
|
1
|
|
);
|
|
}
|
|
|
|
writeMpx->GlomPending = FALSE;
|
|
|
|
while ( !IsListEmpty( &writeMpx->GlomDelayList ) ) {
|
|
listEntry = RemoveHeadList( &writeMpx->GlomDelayList );
|
|
workContext = CONTAINING_RECORD( listEntry, WORK_CONTEXT, ListEntry );
|
|
workContext->FspRestartRoutine = AddPacketToGlom;
|
|
QUEUE_WORK_TO_FSP( workContext );
|
|
}
|
|
|
|
//
|
|
// If the rfcb is closing and the write mpx ref count == 0,
|
|
// then we must complete the close.
|
|
//
|
|
|
|
if ( (GET_BLOCK_STATE(rfcb) != BlockStateActive) &&
|
|
(writeMpx->ReferenceCount == 0) ) {
|
|
|
|
RELEASE_DPC_SPIN_LOCK( &connection->SpinLock );
|
|
WorkContext->FspRestartRoutine = RestartWriteMpxCompleteRfcbClose;
|
|
QUEUE_WORK_TO_FSP( WorkContext );
|
|
KeLowerIrql( oldIrql );
|
|
goto Cleanup;
|
|
}
|
|
|
|
RELEASE_DPC_SPIN_LOCK( &connection->SpinLock );
|
|
SrvFsdRestartSmbComplete( WorkContext );
|
|
KeLowerIrql( oldIrql );
|
|
|
|
Cleanup:
|
|
if (bNeedTrace) {
|
|
SrvWmiEndContext(WorkContext);
|
|
}
|
|
return;
|
|
|
|
} // RestartPrepareMpxMdlWrite
|
|
|
|
|
|
VOID SRVFASTCALL
|
|
AddPacketToGlom (
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
)
|
|
{
|
|
PSMB_HEADER header;
|
|
PREQ_WRITE_MPX request;
|
|
PRESP_WRITE_MPX_DATAGRAM response;
|
|
|
|
PRFCB rfcb;
|
|
PWRITE_MPX_CONTEXT writeMpx;
|
|
PCONNECTION connection;
|
|
ULONG fileOffset;
|
|
USHORT glomOffset;
|
|
CLONG bufferOffset;
|
|
PCHAR writeAddress;
|
|
USHORT writeLength;
|
|
ULONG bytesCopied;
|
|
KIRQL oldIrql;
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
USHORT sequenceNumber;
|
|
BOOLEAN rfcbClosing;
|
|
|
|
PWRITE_MPX_RUN run, nextRun;
|
|
ULONG runIndex, runCount;
|
|
|
|
USHORT runOffset;
|
|
USHORT runLength;
|
|
|
|
PMDL cacheMdl;
|
|
LARGE_INTEGER cacheOffset;
|
|
|
|
header = WorkContext->RequestHeader;
|
|
request = (PREQ_WRITE_MPX)WorkContext->RequestParameters;
|
|
|
|
rfcb = WorkContext->Rfcb;
|
|
connection = WorkContext->Connection;
|
|
writeMpx = &rfcb->WriteMpx;
|
|
cacheMdl = writeMpx->MdlChain;
|
|
|
|
if( writeMpx->Glomming == FALSE ) {
|
|
//
|
|
// We must have encountered an error in RestartPrepareMpxMdlWrite(), but
|
|
// we call through this routine to ensure we send a response back to the
|
|
// client.
|
|
//
|
|
KeRaiseIrql( DISPATCH_LEVEL, &oldIrql );
|
|
ACQUIRE_DPC_SPIN_LOCK( &connection->SpinLock );
|
|
goto check;
|
|
}
|
|
|
|
ASSERT( writeMpx->Glomming );
|
|
ASSERT( !writeMpx->GlomPending );
|
|
ASSERT( WorkContext->Parameters.WriteMpx.Mid == writeMpx->Mid );
|
|
|
|
//
|
|
// Get the file offset of this packet's data.
|
|
//
|
|
|
|
fileOffset = WorkContext->Parameters.WriteMpx.Offset;
|
|
|
|
//
|
|
// Determine the amount of data to write. This is the minimum of
|
|
// the amount requested by the client and the amount of data
|
|
// actually sent in the request buffer.
|
|
//
|
|
|
|
bufferOffset = SmbGetUshort( &request->DataOffset );
|
|
|
|
//
|
|
// If we have the transport context, setup writeAddress accordingly.
|
|
//
|
|
|
|
if ( WorkContext->Parameters.WriteMpx.TransportContext ) {
|
|
writeAddress = (PCHAR)WorkContext->Parameters.WriteMpx.Buffer +
|
|
bufferOffset;
|
|
} else {
|
|
writeAddress = (PCHAR)header + bufferOffset;
|
|
}
|
|
|
|
writeLength = WorkContext->Parameters.WriteMpx.WriteLength;
|
|
ASSERT( writeLength <= 0xffff );
|
|
|
|
//
|
|
// If the data doesn't fall within the bounds of the glommed write,
|
|
// discard the packet.
|
|
//
|
|
// We always know that we've copied at least the first part of the
|
|
// glom.
|
|
//
|
|
|
|
ASSERT( writeMpx->NumberOfRuns > 0 );
|
|
|
|
KeRaiseIrql( DISPATCH_LEVEL, &oldIrql );
|
|
if ( fileOffset <= writeMpx->StartOffset ) {
|
|
ACQUIRE_DPC_SPIN_LOCK( &connection->SpinLock );
|
|
goto discard;
|
|
}
|
|
|
|
fileOffset -= writeMpx->StartOffset;
|
|
if ( (fileOffset + writeLength) > writeMpx->Length ) {
|
|
ACQUIRE_DPC_SPIN_LOCK( &connection->SpinLock );
|
|
goto discard;
|
|
}
|
|
ASSERT( fileOffset <= 0xffff );
|
|
ASSERT( fileOffset + writeLength <= 0xffff );
|
|
|
|
glomOffset = (USHORT)fileOffset;
|
|
|
|
//
|
|
// Copy the packet data into the glom.
|
|
//
|
|
|
|
status = TdiCopyBufferToMdl(
|
|
writeAddress,
|
|
0,
|
|
writeLength,
|
|
cacheMdl,
|
|
glomOffset,
|
|
&bytesCopied
|
|
);
|
|
ASSERT( status == STATUS_SUCCESS );
|
|
ASSERT( bytesCopied == writeLength );
|
|
|
|
//
|
|
// Return the TransportContext
|
|
//
|
|
if ( WorkContext->Parameters.WriteMpx.TransportContext ) {
|
|
TdiReturnChainedReceives( &WorkContext->Parameters.WriteMpx.TransportContext,
|
|
1
|
|
);
|
|
}
|
|
|
|
ACQUIRE_DPC_SPIN_LOCK( &connection->SpinLock );
|
|
|
|
//
|
|
// Update the glom run information. Note that this packet may have
|
|
// been received multiple times, so it may already be marked in the
|
|
// run information.
|
|
//
|
|
|
|
if (0) IF_SMB_DEBUG(MPX2) {
|
|
KdPrint(( "rfcb %p, offset %lx, length %lx\n", rfcb, glomOffset, writeLength ));
|
|
}
|
|
|
|
runCount = writeMpx->NumberOfRuns;
|
|
|
|
for ( runIndex = 1, nextRun = &writeMpx->RunList[1];
|
|
runIndex < runCount;
|
|
runIndex++, nextRun++ ) {
|
|
if ( nextRun->Offset > glomOffset ) {
|
|
break;
|
|
}
|
|
}
|
|
run = nextRun - 1;
|
|
|
|
runOffset = run->Offset;
|
|
runLength = run->Length;
|
|
ASSERT( runOffset <= glomOffset );
|
|
|
|
if ( (runOffset + runLength) == glomOffset ) {
|
|
|
|
//
|
|
// This packet abuts the end of the previous run. Add the
|
|
// length of this packet to the run length and attempt to
|
|
// coalesce with the next run.
|
|
//
|
|
|
|
runLength += writeLength;
|
|
goto coalesce;
|
|
}
|
|
|
|
if ( (runOffset + runLength) > glomOffset ) {
|
|
|
|
//
|
|
// This packet overlaps the previous run. If it lies completely
|
|
// within the previous run, ignore it.
|
|
//
|
|
|
|
if ( (USHORT)(runOffset + runLength) >= (glomOffset + writeLength) ) {
|
|
goto discard;
|
|
}
|
|
|
|
//
|
|
// This packet overlaps and extends the previous run. Calculate
|
|
// the new run length and attempt to coalesce with the next run.
|
|
//
|
|
|
|
runLength = (glomOffset - runOffset + writeLength);
|
|
goto coalesce;
|
|
}
|
|
|
|
//
|
|
// This packet's data is disjoint from the previous run.
|
|
//
|
|
|
|
if ( runIndex < runCount ) {
|
|
|
|
//
|
|
// There is a next run. Does this packet overlap with that run?
|
|
//
|
|
|
|
runOffset = nextRun->Offset;
|
|
runLength = nextRun->Length;
|
|
|
|
if ( (glomOffset + writeLength) >= runOffset ) {
|
|
|
|
//
|
|
// This packet overlaps the next run. Calculate the new run
|
|
// length.
|
|
//
|
|
|
|
nextRun->Offset = glomOffset;
|
|
nextRun->Length = runOffset - glomOffset + runLength;
|
|
goto check;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Either this packet is disjoint from the next run, or there is no
|
|
// next run. Is there room in the run array for another run? If
|
|
// not, discard this packet. (Note that we discard it even though
|
|
// we have already copied the packet data. That's OK -- it will
|
|
// just be resent.)
|
|
//
|
|
|
|
if ( runCount == MAX_GLOM_RUN_COUNT ) {
|
|
goto discard;
|
|
}
|
|
|
|
//
|
|
// Add a new run. Since we know the new run is disjoint from the
|
|
// previous run, we know that the glom is not complete.
|
|
//
|
|
|
|
RtlMoveMemory( // NOT RtlCopyMemory -- buffers overlap
|
|
nextRun + 1,
|
|
nextRun,
|
|
(runCount - runIndex) * sizeof(WRITE_MPX_RUN)
|
|
);
|
|
writeMpx->NumberOfRuns++;
|
|
nextRun->Offset = glomOffset;
|
|
nextRun->Length = writeLength;
|
|
goto check;
|
|
|
|
coalesce:
|
|
|
|
if ( runIndex == runCount ) {
|
|
run->Length = runLength;
|
|
} else if ( (runOffset + runLength) >= nextRun->Offset ) {
|
|
run->Length = nextRun->Length + nextRun->Offset - runOffset;
|
|
writeMpx->NumberOfRuns--;
|
|
RtlMoveMemory( // NOT RtlCopyMemory -- buffers overlap
|
|
nextRun,
|
|
nextRun + 1,
|
|
(runCount - runIndex) * sizeof(WRITE_MPX_RUN)
|
|
);
|
|
} else {
|
|
run->Length += writeLength;
|
|
ASSERT( (runOffset + run->Length) < nextRun->Offset );
|
|
}
|
|
|
|
if ( (writeMpx->NumberOfRuns == 1) &&
|
|
(writeMpx->RunList[0].Length == writeMpx->Length) ) {
|
|
|
|
//
|
|
// The glom is complete.
|
|
//
|
|
|
|
writeMpx->GlomComplete = TRUE;
|
|
}
|
|
|
|
check:
|
|
|
|
if (0) IF_SMB_DEBUG(MPX2) {
|
|
if( writeMpx->Glomming ) {
|
|
ULONG i;
|
|
PWRITE_MPX_RUN runi;
|
|
for ( i = 0, runi = &writeMpx->RunList[0];
|
|
i < writeMpx->NumberOfRuns;
|
|
i++, runi++ ) {
|
|
KdPrint(( " run %d: offset %lx, length %lx\n", i, runi->Offset, runi->Length ));
|
|
}
|
|
}
|
|
}
|
|
|
|
writeMpx->Mask |= SmbGetUlong( &request->Mask );
|
|
|
|
//
|
|
// If this is an unsequenced request, we're done. We don't respond
|
|
// until we get a sequenced request.
|
|
//
|
|
|
|
sequenceNumber = SmbGetAlignedUshort( &header->SequenceNumber );
|
|
|
|
if ( sequenceNumber == 0 ) {
|
|
goto discard;
|
|
}
|
|
|
|
//
|
|
// This is the last request in this mux sent by the client. Save
|
|
// the sequence number.
|
|
//
|
|
|
|
writeMpx->SequenceNumber = sequenceNumber;
|
|
|
|
discard:
|
|
|
|
//
|
|
// If we have received the sequenced command for this write mux,
|
|
// and this is the last active command, then it's time to send
|
|
// the response. Otherwise, we are done with this SMB.
|
|
//
|
|
|
|
if ( --writeMpx->ReferenceCount != 0 ) {
|
|
|
|
RELEASE_DPC_SPIN_LOCK( &connection->SpinLock );
|
|
SrvFsdRestartSmbComplete( WorkContext );
|
|
KeLowerIrql( oldIrql );
|
|
return;
|
|
}
|
|
|
|
//
|
|
// WriteMpx refcount is 0.
|
|
//
|
|
|
|
rfcbClosing = (GET_BLOCK_STATE(rfcb) != BlockStateActive);
|
|
|
|
if ( writeMpx->SequenceNumber == 0 ) {
|
|
|
|
//
|
|
// If the rfcb is closing, complete the cleanup.
|
|
//
|
|
|
|
RELEASE_DPC_SPIN_LOCK( &connection->SpinLock );
|
|
KeLowerIrql( oldIrql );
|
|
|
|
if ( rfcbClosing ) {
|
|
RestartWriteMpxCompleteRfcbClose( WorkContext );
|
|
}
|
|
SrvRestartFsdComplete( WorkContext );
|
|
return;
|
|
}
|
|
|
|
//
|
|
// We are done with this write mux. Save the accumulated mask, the
|
|
// sequence number, and the original MID, then clear the mask and
|
|
// sequence number to indicate that we no longer are in the middle
|
|
// of a write mux.
|
|
//
|
|
|
|
response = (PRESP_WRITE_MPX_DATAGRAM)WorkContext->ResponseParameters;
|
|
|
|
SmbPutUlong( &response->Mask, writeMpx->Mask );
|
|
writeMpx->Mask = 0;
|
|
|
|
SmbPutAlignedUshort( &header->SequenceNumber, writeMpx->SequenceNumber );
|
|
writeMpx->SequenceNumber = 0;
|
|
|
|
SmbPutAlignedUshort( &header->Mid, writeMpx->Mid );
|
|
|
|
//
|
|
// If the glom is complete, we need to complete the MDL write. But
|
|
// we can't do that with the lock held, so we need to clear out all
|
|
// information related to the glom first.
|
|
//
|
|
|
|
if ( writeMpx->Glomming && writeMpx->GlomComplete ) {
|
|
|
|
PWORK_CONTEXT newContext;
|
|
|
|
//
|
|
// Save and clear information about the active glom.
|
|
//
|
|
|
|
writeMpx->Glomming = FALSE;
|
|
writeMpx->GlomComplete = FALSE;
|
|
|
|
cacheOffset.QuadPart = writeMpx->StartOffset;
|
|
writeLength = writeMpx->Length;
|
|
|
|
DEBUG writeMpx->MdlChain = NULL;
|
|
DEBUG writeMpx->StartOffset = 0;
|
|
DEBUG writeMpx->Length = 0;
|
|
|
|
//
|
|
// Save the status.
|
|
//
|
|
|
|
status = rfcb->SavedError;
|
|
rfcb->SavedError = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Now we can release the lock.
|
|
//
|
|
|
|
RELEASE_DPC_SPIN_LOCK( &connection->SpinLock );
|
|
KeLowerIrql( oldIrql );
|
|
|
|
ALLOCATE_WORK_CONTEXT( WorkContext->CurrentWorkQueue, &newContext );
|
|
|
|
#if DBG
|
|
IF_SMB_DEBUG(MPX2) {
|
|
KdPrint(( "AddPacketToGlom: rfcb %p, completed chain:\n", rfcb ));
|
|
DumpMdlChain( cacheMdl );
|
|
}
|
|
#endif
|
|
|
|
if( newContext == NULL ) {
|
|
|
|
//
|
|
// Tell the cache manager that we're done with this MDL write.
|
|
//
|
|
|
|
if( rfcb->Lfcb->MdlWriteComplete == NULL ||
|
|
rfcb->Lfcb->MdlWriteComplete(
|
|
rfcb->Lfcb->FileObject,
|
|
&cacheOffset,
|
|
cacheMdl,
|
|
rfcb->Lfcb->DeviceObject ) == FALSE ) {
|
|
|
|
status = SrvIssueMdlCompleteRequest( WorkContext, NULL,
|
|
cacheMdl,
|
|
IRP_MJ_WRITE,
|
|
&cacheOffset,
|
|
writeLength
|
|
);
|
|
|
|
if( !NT_SUCCESS( status ) ) {
|
|
SrvLogServiceFailure( SRV_SVC_MDL_COMPLETE, status );
|
|
}
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// Send the FsRtlMdlWriteComplete off on its way, and go ahead and send
|
|
// the response to the client now.
|
|
//
|
|
newContext->Rfcb = WorkContext->Rfcb;
|
|
SrvReferenceRfcb( newContext->Rfcb );
|
|
|
|
newContext->Parameters.WriteMpxMdlWriteComplete.CacheOffset = cacheOffset;
|
|
newContext->Parameters.WriteMpxMdlWriteComplete.WriteLength = writeLength;
|
|
newContext->Parameters.WriteMpxMdlWriteComplete.CacheMdl = cacheMdl;
|
|
newContext->FspRestartRoutine = WriteMpxMdlWriteComplete;
|
|
SrvQueueWorkToFsp( newContext );
|
|
}
|
|
|
|
} else {
|
|
|
|
if( writeMpx->Glomming == FALSE ) {
|
|
status = rfcb->SavedError;
|
|
rfcb->SavedError = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Now we can release the lock.
|
|
//
|
|
|
|
RELEASE_DPC_SPIN_LOCK( &connection->SpinLock );
|
|
KeLowerIrql( oldIrql );
|
|
|
|
}
|
|
|
|
//
|
|
// Complete the rfcb close.
|
|
//
|
|
|
|
if ( rfcbClosing ) {
|
|
|
|
RestartWriteMpxCompleteRfcbClose( WorkContext );
|
|
}
|
|
|
|
//
|
|
// Build the response message.
|
|
//
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
SrvSetSmbError2( WorkContext, status, TRUE );
|
|
}
|
|
|
|
response->WordCount = 2;
|
|
SmbPutUshort( &response->ByteCount, 0 );
|
|
WorkContext->ResponseParameters = NEXT_LOCATION(
|
|
response,
|
|
RESP_WRITE_MPX_DATAGRAM,
|
|
0
|
|
);
|
|
|
|
//
|
|
// Send the response.
|
|
//
|
|
|
|
SrvFsdSendResponse( WorkContext );
|
|
return;
|
|
|
|
} // AddPacketToGlom
|
|
|
|
BOOLEAN
|
|
AddPacketToGlomInIndication (
|
|
IN PWORK_CONTEXT WorkContext,
|
|
IN OUT PRFCB Rfcb,
|
|
IN PVOID Tsdu,
|
|
IN ULONG BytesAvailable,
|
|
IN ULONG ReceiveDatagramFlags,
|
|
IN PVOID SourceAddress,
|
|
IN PVOID Options
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Do Write glomming at indication.
|
|
|
|
*** connection spinlock assumed held. Released on exit ***
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
TRUE if the caller has to clean up the connection block.
|
|
|
|
--*/
|
|
|
|
{
|
|
PREQ_WRITE_MPX request;
|
|
PRESP_WRITE_MPX_DATAGRAM response;
|
|
PWRITE_MPX_CONTEXT writeMpx = &Rfcb->WriteMpx;
|
|
|
|
PCONNECTION connection = WorkContext->Connection;
|
|
ULONG fileOffset;
|
|
USHORT glomOffset;
|
|
CLONG bufferOffset;
|
|
PCHAR writeAddress;
|
|
USHORT writeLength;
|
|
ULONG bytesCopied;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
USHORT sequenceNumber;
|
|
|
|
PSMB_HEADER header = (PSMB_HEADER)Tsdu;
|
|
PWRITE_MPX_RUN run, nextRun;
|
|
ULONG runIndex, runCount;
|
|
|
|
USHORT runOffset;
|
|
USHORT runLength;
|
|
|
|
ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
|
|
|
|
//
|
|
// copied from SrvRestartReceive.
|
|
//
|
|
|
|
WorkContext->CurrentWorkQueue->stats.BytesReceived += BytesAvailable;
|
|
connection->BreakIIToNoneJustSent = FALSE;
|
|
SrvUpdateErrorCount( &SrvNetworkErrorRecord, FALSE );
|
|
|
|
//
|
|
// Set up locals.
|
|
//
|
|
|
|
request = (PREQ_WRITE_MPX)(header + 1);
|
|
|
|
ASSERT( writeMpx->Glomming );
|
|
ASSERT( !writeMpx->GlomPending );
|
|
ASSERT( header->Mid == writeMpx->Mid );
|
|
|
|
//
|
|
// Get the file offset of this packet's data.
|
|
//
|
|
|
|
fileOffset = SmbGetUlong( &request->Offset );
|
|
|
|
//
|
|
// Determine the amount of data to write. This is the minimum of
|
|
// the amount requested by the client and the amount of data
|
|
// actually sent in the request buffer.
|
|
//
|
|
|
|
bufferOffset = SmbGetUshort( &request->DataOffset );
|
|
|
|
writeAddress = (PCHAR)header + bufferOffset;
|
|
|
|
writeLength =
|
|
(USHORT)(MIN( (CLONG)SmbGetUshort( &request->DataLength ),
|
|
BytesAvailable - bufferOffset ));
|
|
ASSERT( writeLength <= 0xffff );
|
|
|
|
//
|
|
// If the data doesn't fall within the bounds of the glommed write,
|
|
// discard the packet.
|
|
//
|
|
// We always know that we've copied at least the first part of the
|
|
// glom.
|
|
//
|
|
|
|
ASSERT( writeMpx->NumberOfRuns > 0 );
|
|
|
|
if ( fileOffset <= writeMpx->StartOffset ) {
|
|
goto discard;
|
|
}
|
|
fileOffset -= writeMpx->StartOffset;
|
|
if ( (fileOffset + writeLength) > writeMpx->Length ) {
|
|
goto discard;
|
|
}
|
|
ASSERT( fileOffset <= 0xffff );
|
|
ASSERT( fileOffset + writeLength <= 0xffff );
|
|
glomOffset = (USHORT)fileOffset;
|
|
|
|
//
|
|
// Copy the packet data into the glom.
|
|
//
|
|
|
|
status = TdiCopyBufferToMdl(
|
|
writeAddress,
|
|
0,
|
|
writeLength,
|
|
writeMpx->MdlChain,
|
|
glomOffset,
|
|
&bytesCopied
|
|
);
|
|
ASSERT( status == STATUS_SUCCESS );
|
|
ASSERT( bytesCopied == writeLength );
|
|
|
|
//
|
|
// Update the glom run information. Note that this packet may have
|
|
// been received multiple times, so it may already be marked in the
|
|
// run information.
|
|
//
|
|
|
|
if (0) IF_SMB_DEBUG(MPX2) {
|
|
KdPrint(( "rfcb %p, offset %lx, length %lx\n", Rfcb, glomOffset, writeLength ));
|
|
}
|
|
|
|
runCount = writeMpx->NumberOfRuns;
|
|
|
|
for ( runIndex = 1, nextRun = &writeMpx->RunList[1];
|
|
runIndex < runCount;
|
|
runIndex++, nextRun++ ) {
|
|
if ( nextRun->Offset > glomOffset ) {
|
|
break;
|
|
}
|
|
}
|
|
run = nextRun - 1;
|
|
|
|
runOffset = run->Offset;
|
|
runLength = run->Length;
|
|
ASSERT( runOffset <= glomOffset );
|
|
|
|
if ( (runOffset + runLength) == glomOffset ) {
|
|
|
|
//
|
|
// This packet abuts the end of the previous run. Add the
|
|
// length of this packet to the run length and attempt to
|
|
// coalesce with the next run.
|
|
//
|
|
|
|
runLength += writeLength;
|
|
goto coalesce;
|
|
}
|
|
|
|
if ( (runOffset + runLength) > glomOffset ) {
|
|
|
|
//
|
|
// This packet overlaps the previous run. If it lies completely
|
|
// within the previous run, ignore it.
|
|
//
|
|
|
|
if ( (USHORT)(runOffset + runLength) >= (glomOffset + writeLength) ) {
|
|
goto discard;
|
|
}
|
|
|
|
//
|
|
// This packet overlaps and extends the previous run. Calculate
|
|
// the new run length and attempt to coalesce with the next run.
|
|
//
|
|
|
|
runLength = (glomOffset - runOffset + writeLength);
|
|
goto coalesce;
|
|
}
|
|
|
|
//
|
|
// This packet's data is disjoint from the previous run.
|
|
//
|
|
|
|
if ( runIndex < runCount ) {
|
|
|
|
//
|
|
// There is a next run. Does this packet overlap with that run?
|
|
//
|
|
|
|
runOffset = nextRun->Offset;
|
|
runLength = nextRun->Length;
|
|
|
|
if ( (glomOffset + writeLength) >= runOffset ) {
|
|
|
|
//
|
|
// This packet overlaps the next run. Calculate the new run
|
|
// length.
|
|
//
|
|
|
|
nextRun->Offset = glomOffset;
|
|
nextRun->Length = runOffset - glomOffset + runLength;
|
|
goto check;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Either this packet is disjoint from the next run, or there is no
|
|
// next run. Is there room in the run array for another run? If
|
|
// not, discard this packet. (Note that we discard it even though
|
|
// we have already copied the packet data. That's OK -- it will
|
|
// just be resent.)
|
|
//
|
|
|
|
if ( runCount == MAX_GLOM_RUN_COUNT ) {
|
|
goto discard;
|
|
}
|
|
|
|
//
|
|
// Add a new run. Since we know the new run is disjoint from the
|
|
// previous run, we know that the glom is not complete.
|
|
//
|
|
|
|
RtlMoveMemory( // NOT RtlCopyMemory -- buffers overlap
|
|
nextRun + 1,
|
|
nextRun,
|
|
(runCount - runIndex) * sizeof(WRITE_MPX_RUN)
|
|
);
|
|
writeMpx->NumberOfRuns++;
|
|
nextRun->Offset = glomOffset;
|
|
nextRun->Length = writeLength;
|
|
goto check;
|
|
|
|
coalesce:
|
|
|
|
if ( runIndex == runCount ) {
|
|
run->Length = runLength;
|
|
} else if ( (runOffset + runLength) >= nextRun->Offset ) {
|
|
run->Length = nextRun->Length + nextRun->Offset - runOffset;
|
|
writeMpx->NumberOfRuns--;
|
|
RtlMoveMemory( // NOT RtlCopyMemory -- buffers overlap
|
|
nextRun,
|
|
nextRun + 1,
|
|
(runCount - runIndex) * sizeof(WRITE_MPX_RUN)
|
|
);
|
|
} else {
|
|
run->Length += writeLength;
|
|
ASSERT( (runOffset + run->Length) < nextRun->Offset );
|
|
}
|
|
|
|
if ( (writeMpx->NumberOfRuns == 1) &&
|
|
(writeMpx->RunList[0].Length == writeMpx->Length) ) {
|
|
|
|
//
|
|
// The glom is complete.
|
|
//
|
|
|
|
writeMpx->GlomComplete = TRUE;
|
|
}
|
|
|
|
check:
|
|
|
|
if (0) IF_SMB_DEBUG(MPX2) {
|
|
ULONG i;
|
|
PWRITE_MPX_RUN runi;
|
|
for ( i = 0, runi = &writeMpx->RunList[0];
|
|
i < writeMpx->NumberOfRuns;
|
|
i++, runi++ ) {
|
|
KdPrint(( " run %d: offset %lx, length %lx\n", i, runi->Offset, runi->Length ));
|
|
}
|
|
}
|
|
|
|
writeMpx->Mask |= SmbGetUlong( &request->Mask );
|
|
|
|
//
|
|
// If this is an unsequenced request, we're done. We don't respond
|
|
// until we get a sequenced request.
|
|
//
|
|
|
|
sequenceNumber = SmbGetAlignedUshort( &header->SequenceNumber );
|
|
|
|
if ( sequenceNumber == 0 ) {
|
|
goto discard;
|
|
}
|
|
|
|
//
|
|
// This is the last request in this mux sent by the client. Save
|
|
// the sequence number.
|
|
//
|
|
|
|
writeMpx->SequenceNumber = sequenceNumber;
|
|
|
|
discard:
|
|
|
|
//
|
|
// If we have received the sequenced command for this write mux,
|
|
// and this is the last active command, then it's time to send
|
|
// the response. Otherwise, we are done with this SMB.
|
|
//
|
|
|
|
if ( (--writeMpx->ReferenceCount != 0) ||
|
|
(writeMpx->SequenceNumber == 0) ) {
|
|
RELEASE_DPC_SPIN_LOCK( &connection->SpinLock );
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Copy the header portion for the response.
|
|
//
|
|
|
|
TdiCopyLookaheadData(
|
|
WorkContext->RequestBuffer->Buffer,
|
|
Tsdu,
|
|
sizeof(SMB_HEADER),
|
|
ReceiveDatagramFlags
|
|
);
|
|
|
|
// WorkContext->RequestBuffer->DataLength = BytesAvailable;
|
|
|
|
//
|
|
// We are done with this write mux. Save the accumulated mask, the
|
|
// sequence number, and the original MID, then clear the mask and
|
|
// sequence number to indicate that we no longer are in the middle
|
|
// of a write mux.
|
|
//
|
|
|
|
response = (PRESP_WRITE_MPX_DATAGRAM)WorkContext->ResponseParameters;
|
|
|
|
SmbPutUlong( &response->Mask, writeMpx->Mask );
|
|
writeMpx->Mask = 0;
|
|
|
|
SmbPutAlignedUshort( &header->SequenceNumber, writeMpx->SequenceNumber );
|
|
writeMpx->SequenceNumber = 0;
|
|
|
|
SmbPutAlignedUshort( &header->Mid, writeMpx->Mid );
|
|
|
|
//
|
|
// If the glom is complete, we need to complete the MDL write. But
|
|
// we can't do that with the lock held, so we need to clear out all
|
|
// information related to the glom first.
|
|
//
|
|
|
|
if ( writeMpx->GlomComplete ) {
|
|
|
|
//
|
|
// The file is active and the TID is valid. Reference the
|
|
// RFCB.
|
|
//
|
|
|
|
Rfcb->BlockHeader.ReferenceCount++;
|
|
UPDATE_REFERENCE_HISTORY( Rfcb, FALSE );
|
|
|
|
//
|
|
// Now we can release the lock.
|
|
//
|
|
|
|
RELEASE_DPC_SPIN_LOCK( &connection->SpinLock );
|
|
|
|
WorkContext->Rfcb = Rfcb;
|
|
|
|
//
|
|
// Build the response message.
|
|
//
|
|
|
|
response->WordCount = 2;
|
|
SmbPutUshort( &response->ByteCount, 0 );
|
|
WorkContext->ResponseParameters = NEXT_LOCATION(
|
|
response,
|
|
RESP_WRITE_MPX_DATAGRAM,
|
|
0
|
|
);
|
|
|
|
//
|
|
// Send this off to the fsp for final processing. We need to do
|
|
// this since we cannot call the cache manager at dpc level.
|
|
//
|
|
|
|
WorkContext->FspRestartRoutine = RestartCompleteGlommingInIndication;
|
|
SrvQueueWorkToFsp( WorkContext );
|
|
return FALSE;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Now we can release the lock.
|
|
//
|
|
|
|
RELEASE_DPC_SPIN_LOCK( &connection->SpinLock );
|
|
}
|
|
|
|
//
|
|
// Build the response message.
|
|
//
|
|
|
|
ASSERT( status == STATUS_SUCCESS );
|
|
|
|
response->WordCount = 2;
|
|
SmbPutUshort( &response->ByteCount, 0 );
|
|
WorkContext->ResponseParameters = NEXT_LOCATION(
|
|
response,
|
|
RESP_WRITE_MPX_DATAGRAM,
|
|
0
|
|
);
|
|
|
|
//
|
|
// Send the response.
|
|
//
|
|
|
|
SrvFsdSendResponse( WorkContext );
|
|
return FALSE;
|
|
|
|
} // AddPacketToGlomInIndication
|
|
|
|
SMB_PROCESSOR_RETURN_TYPE
|
|
SrvSmbWriteMpxSecondary (
|
|
SMB_PROCESSOR_PARAMETERS
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Processes the Write Mpx Secondary SMB.
|
|
|
|
*** The server should never see this SMB, since it returns the "use
|
|
standard read" error to the main Write Mpx SMB, except over IPX,
|
|
which doesn't use Write Mpx Secondary.
|
|
|
|
Arguments:
|
|
|
|
WorkContext - Supplies a pointer to the work context block
|
|
representing the work item
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE( );
|
|
|
|
//
|
|
// Send a response that tells the client that this SMB is not
|
|
// valid.
|
|
//
|
|
|
|
INTERNAL_ERROR(
|
|
ERROR_LEVEL_UNEXPECTED,
|
|
"SrvSmbWriteMpxSecondary: unexpected SMB",
|
|
NULL,
|
|
NULL
|
|
);
|
|
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
|
|
return SmbStatusSendResponse;
|
|
|
|
} // SrvSmbWriteMpxSecondary
|
|
|
|
VOID SRVFASTCALL
|
|
RestartCompleteGlommingInIndication(
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
)
|
|
{
|
|
LARGE_INTEGER cacheOffset;
|
|
KIRQL oldIrql;
|
|
PMDL cacheMdl;
|
|
NTSTATUS status;
|
|
PRFCB rfcb = WorkContext->Rfcb;
|
|
PWRITE_MPX_CONTEXT writeMpx = &rfcb->WriteMpx;
|
|
PCONNECTION connection = WorkContext->Connection;
|
|
ULONG writeLength;
|
|
|
|
ACQUIRE_SPIN_LOCK( &connection->SpinLock, &oldIrql );
|
|
|
|
//
|
|
// Save the status.
|
|
//
|
|
|
|
status = rfcb->SavedError;
|
|
rfcb->SavedError = STATUS_SUCCESS;
|
|
|
|
//
|
|
// If the rfcb has closed, then the mdl write was completed.
|
|
//
|
|
|
|
if ( GET_BLOCK_STATE(rfcb) == BlockStateActive ) {
|
|
|
|
PWORK_CONTEXT newContext;
|
|
|
|
writeMpx->GlomComplete = FALSE;
|
|
writeMpx->Glomming = FALSE;
|
|
cacheOffset.QuadPart = writeMpx->StartOffset;
|
|
cacheMdl = writeMpx->MdlChain;
|
|
writeLength = writeMpx->Length;
|
|
|
|
DEBUG writeMpx->MdlChain = NULL;
|
|
DEBUG writeMpx->StartOffset = 0;
|
|
DEBUG writeMpx->Length = 0;
|
|
|
|
RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
|
|
|
|
|
|
KeRaiseIrql( DISPATCH_LEVEL, &oldIrql );
|
|
ALLOCATE_WORK_CONTEXT( WorkContext->CurrentWorkQueue, &newContext );
|
|
KeLowerIrql( oldIrql );
|
|
|
|
if( newContext == NULL ) {
|
|
|
|
//
|
|
// Tell the cache manager that we're done with this MDL write.
|
|
//
|
|
|
|
if( rfcb->Lfcb->MdlWriteComplete == NULL ||
|
|
rfcb->Lfcb->MdlWriteComplete(
|
|
rfcb->Lfcb->FileObject,
|
|
&cacheOffset,
|
|
cacheMdl,
|
|
rfcb->Lfcb->DeviceObject ) == FALSE ) {
|
|
|
|
status = SrvIssueMdlCompleteRequest( WorkContext, NULL,
|
|
cacheMdl,
|
|
IRP_MJ_WRITE,
|
|
&cacheOffset,
|
|
writeLength
|
|
);
|
|
|
|
if( !NT_SUCCESS( status ) ) {
|
|
SrvLogServiceFailure( SRV_SVC_MDL_COMPLETE, status );
|
|
}
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// Send the FsRtlMdlWriteComplete off on its way, and go ahead and send
|
|
// the response to the client now.
|
|
//
|
|
newContext->Rfcb = WorkContext->Rfcb;
|
|
WorkContext->Rfcb = NULL;
|
|
|
|
newContext->Parameters.WriteMpxMdlWriteComplete.CacheOffset = cacheOffset;
|
|
newContext->Parameters.WriteMpxMdlWriteComplete.WriteLength = writeLength;
|
|
newContext->Parameters.WriteMpxMdlWriteComplete.CacheMdl = cacheMdl;
|
|
newContext->FspRestartRoutine = WriteMpxMdlWriteComplete;
|
|
SrvQueueWorkToFsp( newContext );
|
|
}
|
|
|
|
} else {
|
|
|
|
ASSERT( !writeMpx->Glomming );
|
|
RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
|
|
}
|
|
|
|
//
|
|
// Send the response.
|
|
//
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
SrvSetSmbError2( WorkContext, status, TRUE );
|
|
}
|
|
|
|
SrvFsdSendResponse( WorkContext );
|
|
return;
|
|
|
|
} // RestartCompleteGlommingInIndication
|
|
|
|
VOID SRVFASTCALL
|
|
WriteMpxMdlWriteComplete (
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
if( WorkContext->Rfcb->Lfcb->MdlWriteComplete == NULL ||
|
|
|
|
WorkContext->Rfcb->Lfcb->MdlWriteComplete(
|
|
WorkContext->Rfcb->Lfcb->FileObject,
|
|
&WorkContext->Parameters.WriteMpxMdlWriteComplete.CacheOffset,
|
|
WorkContext->Parameters.WriteMpxMdlWriteComplete.CacheMdl,
|
|
WorkContext->Rfcb->Lfcb->DeviceObject ) == FALSE ) {
|
|
|
|
status = SrvIssueMdlCompleteRequest( WorkContext, NULL,
|
|
WorkContext->Parameters.WriteMpxMdlWriteComplete.CacheMdl,
|
|
IRP_MJ_WRITE,
|
|
&WorkContext->Parameters.WriteMpxMdlWriteComplete.CacheOffset,
|
|
WorkContext->Parameters.WriteMpxMdlWriteComplete.WriteLength );
|
|
|
|
if( !NT_SUCCESS( status ) ) {
|
|
SrvLogServiceFailure( SRV_SVC_MDL_COMPLETE, status );
|
|
}
|
|
}
|
|
|
|
SrvDereferenceRfcb( WorkContext->Rfcb );
|
|
WorkContext->Rfcb = NULL;
|
|
WorkContext->FspRestartRoutine = SrvRestartReceive;
|
|
ASSERT( WorkContext->BlockHeader.ReferenceCount == 1 );
|
|
#if DBG
|
|
WorkContext->BlockHeader.ReferenceCount = 0;
|
|
#endif
|
|
RETURN_FREE_WORKITEM( WorkContext );
|
|
}
|
|
|
|
|
|
VOID SRVFASTCALL
|
|
RestartWriteMpxCompleteRfcbClose (
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Completes the rfcb close after last active writempx is finished.
|
|
|
|
Arguments:
|
|
|
|
WorkContext - Supplies a pointer to the work context block
|
|
representing the work item
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCONNECTION connection = WorkContext->Connection;
|
|
PRFCB rfcb = WorkContext->Rfcb;
|
|
PWRITE_MPX_CONTEXT writeMpx = &rfcb->WriteMpx;
|
|
LARGE_INTEGER cacheOffset;
|
|
PMDL mdlChain;
|
|
KIRQL oldIrql;
|
|
ULONG writeLength;
|
|
NTSTATUS status;
|
|
|
|
//
|
|
// This rfcb is closing.
|
|
//
|
|
|
|
ACQUIRE_SPIN_LOCK( &connection->SpinLock, &oldIrql );
|
|
|
|
ASSERT ( GET_BLOCK_STATE(rfcb) != BlockStateActive );
|
|
|
|
writeMpx = &rfcb->WriteMpx;
|
|
|
|
if ( writeMpx->Glomming ) {
|
|
|
|
//
|
|
// We need to complete this write mdl
|
|
//
|
|
|
|
writeMpx->Glomming = FALSE;
|
|
writeMpx->GlomComplete = FALSE;
|
|
|
|
//
|
|
// Save the offset and MDL address.
|
|
//
|
|
|
|
cacheOffset.QuadPart = writeMpx->StartOffset;
|
|
mdlChain = writeMpx->MdlChain;
|
|
writeLength = writeMpx->Length;
|
|
|
|
DEBUG writeMpx->MdlChain = NULL;
|
|
DEBUG writeMpx->StartOffset = 0;
|
|
DEBUG writeMpx->Length = 0;
|
|
|
|
//
|
|
// Now we can release the lock.
|
|
//
|
|
|
|
RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
|
|
|
|
//
|
|
// Tell the cache manager that we're done with this MDL write.
|
|
//
|
|
|
|
if( rfcb->Lfcb->MdlWriteComplete == NULL ||
|
|
rfcb->Lfcb->MdlWriteComplete(
|
|
writeMpx->FileObject,
|
|
&cacheOffset,
|
|
mdlChain,
|
|
rfcb->Lfcb->DeviceObject ) == FALSE ) {
|
|
|
|
status = SrvIssueMdlCompleteRequest( WorkContext, NULL,
|
|
mdlChain,
|
|
IRP_MJ_WRITE,
|
|
&cacheOffset,
|
|
writeLength );
|
|
|
|
if( !NT_SUCCESS( status ) ) {
|
|
SrvLogServiceFailure( SRV_SVC_MDL_COMPLETE, status );
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Now we can release the lock.
|
|
//
|
|
|
|
RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
|
|
|
|
}
|
|
|
|
//
|
|
// Do the actual close
|
|
//
|
|
|
|
SrvCompleteRfcbClose( rfcb );
|
|
return;
|
|
|
|
} // RestartWriteMpxCompleteRfcbClose
|
|
|