356 lines
8.8 KiB
C
356 lines
8.8 KiB
C
/*++
|
||
|
||
Copyright (c) 1998 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
tranrecv.c
|
||
|
||
Abstract:
|
||
|
||
This module contains the SPUDTransmitFileAndRecv service.
|
||
|
||
Author:
|
||
|
||
John Ballard (jballard) 21-Oct-1996
|
||
|
||
Revision History:
|
||
|
||
Keith Moore (keithmo) 04-Feb-1998
|
||
Cleanup, added much needed comments.
|
||
|
||
--*/
|
||
|
||
|
||
#include "spudp.h"
|
||
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text( PAGE, SPUDTransmitFileAndRecv )
|
||
#endif
|
||
|
||
|
||
//
|
||
// Public functions.
|
||
//
|
||
|
||
|
||
NTSTATUS
|
||
SPUDTransmitFileAndRecv(
|
||
HANDLE hSocket,
|
||
struct _AFD_TRANSMIT_FILE_INFO *transmitInfo,
|
||
struct _AFD_RECV_INFO *recvInfo,
|
||
PSPUD_REQ_CONTEXT reqContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Batch TransmitFile & receive request.
|
||
|
||
Arguments:
|
||
|
||
hSocket - The target socket for the request.
|
||
|
||
transmitInfo - Information describing the TransmitFile.
|
||
|
||
recvInfo - Information describing the receive.
|
||
|
||
reqContext - The user-mode context for the request.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Completion status.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
NTSTATUS status;
|
||
PFILE_OBJECT fileObject;
|
||
IO_STATUS_BLOCK localIoStatus;
|
||
PIRP irp;
|
||
PIO_STACK_LOCATION irpSp;
|
||
PSPUD_AFD_REQ_CONTEXT SpudReqContext;
|
||
PVOID completionPort;
|
||
|
||
//
|
||
// Sanity check.
|
||
//
|
||
|
||
PAGED_CODE();
|
||
|
||
status = SPUD_ENTER_SERVICE( "SPUDTransmitFileAndRecv", TRUE );
|
||
|
||
if( !NT_SUCCESS(status) ) {
|
||
return status;
|
||
}
|
||
|
||
BumpCount( CtrTransmitfileAndRecv );
|
||
|
||
//
|
||
// SPUD doesn't support kernel-mode callers. In fact, we don't
|
||
// even build the "system stubs" necessary to invoke SPUD from
|
||
// kernel-mode.
|
||
//
|
||
|
||
ASSERT( ExGetPreviousMode() == UserMode );
|
||
|
||
try {
|
||
|
||
//
|
||
// Make sure we can write to reqContext
|
||
//
|
||
|
||
ProbeForWrite(
|
||
reqContext,
|
||
sizeof(SPUD_REQ_CONTEXT),
|
||
sizeof(ULONG)
|
||
);
|
||
|
||
//
|
||
// Make initial status invalid
|
||
//
|
||
|
||
reqContext->IoStatus1.Status = 0xffffffff;
|
||
reqContext->IoStatus1.Information = 0;
|
||
reqContext->IoStatus2.Status = 0xffffffff;
|
||
reqContext->IoStatus2.Information = 0;
|
||
reqContext->ReqType = TransmitFileAndRecv;
|
||
reqContext->KernelReqInfo = SPUD_INVALID_REQ_HANDLE;
|
||
|
||
//
|
||
// Make sure the buffer looks good
|
||
//
|
||
|
||
ProbeForRead(
|
||
recvInfo,
|
||
sizeof(*recvInfo),
|
||
sizeof(ULONG)
|
||
);
|
||
|
||
if( recvInfo->BufferCount < 1 ) {
|
||
ExRaiseStatus( STATUS_INVALID_PARAMETER );
|
||
}
|
||
|
||
ProbeForRead(
|
||
recvInfo->BufferArray,
|
||
sizeof(*recvInfo->BufferArray),
|
||
sizeof(ULONG)
|
||
);
|
||
|
||
ProbeForRead(
|
||
recvInfo->BufferArray->buf,
|
||
recvInfo->BufferArray->len,
|
||
sizeof(UCHAR)
|
||
);
|
||
|
||
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
||
status = GetExceptionCode();
|
||
SPUD_LEAVE_SERVICE( "SPUDTransmitFileAndRecv", status, FALSE );
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Reference the socket handle
|
||
//
|
||
|
||
status = ObReferenceObjectByHandle(
|
||
hSocket,
|
||
0L,
|
||
*IoFileObjectType,
|
||
UserMode,
|
||
(PVOID *)&fileObject,
|
||
NULL
|
||
);
|
||
|
||
if( !NT_SUCCESS(status) ) {
|
||
SPUD_LEAVE_SERVICE( "SPUDTransmitFileAndRecv", status, FALSE );
|
||
return status;
|
||
}
|
||
|
||
TRACE_OB_REFERENCE( fileObject );
|
||
|
||
//
|
||
// If we haven't already cached the Device Object and FastIoControl
|
||
// pointers, then do so now.
|
||
//
|
||
|
||
if( !SpudAfdDeviceObject ) {
|
||
|
||
status = SpudGetAfdDeviceObject( fileObject );
|
||
|
||
if( !NT_SUCCESS(status) ) {
|
||
TRACE_OB_DEREFERENCE( fileObject );
|
||
ObDereferenceObject( fileObject );
|
||
status = STATUS_INVALID_DEVICE_REQUEST;
|
||
SPUD_LEAVE_SERVICE( "SPUDTransmitFileAndRecv", status, FALSE );
|
||
return status;
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Reference the completion port.
|
||
//
|
||
|
||
completionPort = SpudReferenceCompletionPort();
|
||
|
||
if( completionPort == NULL ) {
|
||
TRACE_OB_DEREFERENCE( fileObject );
|
||
ObDereferenceObject( fileObject );
|
||
status = STATUS_INVALID_DEVICE_REQUEST;
|
||
SPUD_LEAVE_SERVICE( "SPUDTransmitFileAndRecv", status, FALSE );
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Let's check to see if fast io will work
|
||
//
|
||
|
||
if( SpudAfdFastIoDeviceControl(
|
||
fileObject,
|
||
TRUE,
|
||
(PVOID)transmitInfo,
|
||
sizeof(AFD_TRANSMIT_FILE_INFO),
|
||
NULL,
|
||
0,
|
||
IOCTL_AFD_TRANSMIT_FILE,
|
||
&localIoStatus,
|
||
SpudAfdDeviceObject
|
||
)) {
|
||
|
||
BumpCount( CtrTransRecvFastTrans );
|
||
|
||
//
|
||
// Lets remember the completion status for this operation
|
||
//
|
||
|
||
try {
|
||
reqContext->IoStatus1 = localIoStatus;
|
||
} except( EXCEPTION_EXECUTE_HANDLER) {
|
||
localIoStatus.Status = GetExceptionCode();
|
||
localIoStatus.Information = 0;
|
||
}
|
||
|
||
if( localIoStatus.Status == STATUS_SUCCESS ) {
|
||
localIoStatus.Status = SpudAfdRecvFastReq(
|
||
fileObject,
|
||
recvInfo,
|
||
reqContext
|
||
);
|
||
}
|
||
|
||
//
|
||
// If everything completed without pending then we can queue
|
||
// a completion packet to the port now.
|
||
//
|
||
// Note that we must not queue a completion packet if the
|
||
// request is failing in-line.
|
||
//
|
||
|
||
if( localIoStatus.Status != STATUS_PENDING ) {
|
||
|
||
if( NT_SUCCESS(localIoStatus.Status) ) {
|
||
localIoStatus.Status = IoSetIoCompletion(
|
||
SpudCompletionPort, // IoCompletion
|
||
reqContext, // KeyContext
|
||
NULL, // ApcContext
|
||
STATUS_SUCCESS, // IoStatus
|
||
0xFFFFFFFF, // IoStatusInformation
|
||
TRUE // Quota
|
||
);
|
||
}
|
||
|
||
TRACE_OB_DEREFERENCE( fileObject );
|
||
ObDereferenceObject( fileObject );
|
||
|
||
}
|
||
|
||
//
|
||
// At this point, we know the fast-path transmit-file has completed
|
||
// in-line and the receive either completed in-line or has pended.
|
||
// Since it is the receive code's responsibility to add any necessary
|
||
// references to the completion port (and since we know the transmit-
|
||
// file has not pended) we can remove the reference we added above.
|
||
//
|
||
|
||
SPUD_LEAVE_SERVICE( "SPUDTransmitFileAndRecv", localIoStatus.Status, TRUE );
|
||
return localIoStatus.Status;
|
||
}
|
||
|
||
BumpCount( CtrTransRecvSlowTrans );
|
||
|
||
//
|
||
// It looks like we will have to it the hard way.
|
||
// We will now build an IRP for AFD.
|
||
//
|
||
|
||
KeClearEvent( &fileObject->Event );
|
||
|
||
//
|
||
// Allocate and initialize the IRP.
|
||
//
|
||
|
||
irp = IoAllocateIrp( SpudAfdDeviceObject->StackSize, TRUE );
|
||
|
||
if( !irp ) {
|
||
TRACE_OB_DEREFERENCE( fileObject );
|
||
ObDereferenceObject( fileObject );
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
SPUD_LEAVE_SERVICE( "SPUDTransmitFileAndRecv", status, TRUE );
|
||
return status;
|
||
}
|
||
|
||
status = SpudAllocateRequestContext(
|
||
&SpudReqContext,
|
||
reqContext,
|
||
recvInfo,
|
||
irp,
|
||
NULL
|
||
);
|
||
|
||
if( !NT_SUCCESS(status) ) {
|
||
TRACE_OB_DEREFERENCE( fileObject );
|
||
ObDereferenceObject( fileObject );
|
||
IoFreeIrp( irp );
|
||
SPUD_LEAVE_SERVICE( "SPUDTransmitFileAndRecv", status, TRUE );
|
||
return status;
|
||
}
|
||
|
||
BumpCount( CtrTransRecvSlowRecv );
|
||
|
||
irp->RequestorMode = UserMode;
|
||
irp->Tail.Overlay.OriginalFileObject = fileObject;
|
||
irp->Tail.Overlay.Thread = PsGetCurrentThread();
|
||
IoQueueThreadIrp( irp );
|
||
|
||
irpSp = IoGetNextIrpStackLocation( irp );
|
||
|
||
irpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
|
||
irpSp->FileObject = fileObject;
|
||
irpSp->DeviceObject = SpudAfdDeviceObject;
|
||
|
||
// irpSp->Parameters.DeviceIoControl.OutputBufferLength = 0;
|
||
irpSp->Parameters.DeviceIoControl.InputBufferLength = sizeof(AFD_TRANSMIT_FILE_INFO);
|
||
irpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_AFD_TRANSMIT_FILE;
|
||
irpSp->Parameters.DeviceIoControl.Type3InputBuffer = transmitInfo;
|
||
|
||
IoSetCompletionRoutine(
|
||
irp,
|
||
SpudAfdContinueRecv,
|
||
SpudReqContext,
|
||
TRUE,
|
||
TRUE,
|
||
TRUE
|
||
);
|
||
|
||
IoCallDriver( SpudAfdDeviceObject, irp );
|
||
|
||
status = STATUS_PENDING;
|
||
SPUD_LEAVE_SERVICE( "SPUDTransmitFileAndRecv", status, FALSE );
|
||
return status;
|
||
|
||
} // SPUDTransmitFileAndRecv
|